blob: 2dd63e8c17fa13ad2616c7421edb04061001b379 [file] [log] [blame]
Bill Richardson794d4d42011-02-10 19:13:10 -08001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <fcntl.h>
Bill Richardson61362d62011-02-14 10:28:03 -08007#include <limits.h>
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +08008#include <lzma.h>
Bill Richardson794d4d42011-02-10 19:13:10 -08009#include <stdio.h>
10#include <string.h>
11#include <sys/mman.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <unistd.h>
15
16#include "bmpblk_util.h"
Bill Richardson61362d62011-02-14 10:28:03 -080017#include "eficompress.h"
Bill Richardson794d4d42011-02-10 19:13:10 -080018
19
20// Returns pointer to buffer containing entire file, sets length.
21static void *read_entire_file(const char *filename, size_t *length) {
22 int fd;
23 struct stat sbuf;
24 void *ptr;
25
26 *length = 0; // just in case
27
28 if (0 != stat(filename, &sbuf)) {
29 fprintf(stderr, "Unable to stat %s: %s\n", filename, strerror(errno));
30 return 0;
31 }
32
33 if (!sbuf.st_size) {
34 fprintf(stderr, "File %s is empty\n", filename);
35 return 0;
36 }
37
38 fd = open(filename, O_RDONLY);
39 if (fd < 0) {
40 fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
41 return 0;
42 }
43
44 ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
45 if (MAP_FAILED == ptr) {
46 fprintf(stderr, "Unable to mmap %s: %s\n", filename, strerror(errno));
47 close(fd);
48 return 0;
49 }
50
51 *length = sbuf.st_size;
52
53 close(fd);
54
55 return ptr;
56}
57
58
59// Reclaims buffer from read_entire_file().
60static void discard_file(void *ptr, size_t length) {
61 munmap(ptr, length);
62}
63
Bill Richardson61362d62011-02-14 10:28:03 -080064//////////////////////////////////////////////////////////////////////////////
Bill Richardson794d4d42011-02-10 19:13:10 -080065
Bill Richardson61362d62011-02-14 10:28:03 -080066static int require_dir(const char *dirname) {
67 struct stat sbuf;
68
69 if (0 == stat(dirname, &sbuf)) {
70 // Something's there. Is it a directory?
71 if (S_ISDIR(sbuf.st_mode)) {
72 return 0;
73 }
74 fprintf(stderr, "%s already exists and is not a directory\n", dirname);
75 return 1;
76 }
77
78 // dirname doesn't exist. Try to create it.
79 if (ENOENT == errno) {
80 if (0 != mkdir(dirname, 0777)) {
81 fprintf(stderr, "Unable to create directory %s: %s\n",
82 dirname, strerror(errno));
83 return 1;
84 }
85 return 0;
86 }
87
88 fprintf(stderr, "Unable to stat %s: %s\n", dirname, strerror(errno));
89 return 1;
90}
91
92
93
94static void *do_efi_decompress(ImageInfo *img) {
95 void *ibuf;
96 void *sbuf;
97 void *obuf;
98 uint32_t isize;
99 uint32_t ssize;
100 uint32_t osize;
101 EFI_STATUS r;
102
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800103 ibuf = (void*)(img + 1);
Bill Richardson61362d62011-02-14 10:28:03 -0800104 isize = img->compressed_size;
105
106 r = EfiGetInfo(ibuf, isize, &osize, &ssize);
107 if (EFI_SUCCESS != r) {
108 fprintf(stderr, "EfiGetInfo() failed with code %d\n",
109 r);
110 return 0;
111 }
112
113 sbuf = malloc(ssize);
114 if (!sbuf) {
115 fprintf(stderr, "Can't allocate %d bytes: %s\n",
116 ssize,
117 strerror(errno));
118 return 0;
119 }
120
121 obuf = malloc(osize);
122 if (!obuf) {
123 fprintf(stderr, "Can't allocate %d bytes: %s\n",
124 osize,
125 strerror(errno));
126 free(sbuf);
127 return 0;
128 }
129
130 r = EfiDecompress(ibuf, isize, obuf, osize, sbuf, ssize);
131 if (r != EFI_SUCCESS) {
132 fprintf(stderr, "EfiDecompress failed with code %d\n", r);
133 free(obuf);
134 free(sbuf);
135 return 0;
136 }
137
138 free(sbuf);
139 return obuf;
140}
141
142
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800143
144static void *do_lzma_decompress(ImageInfo *img) {
145 void *ibuf;
146 void *obuf;
147 uint32_t isize;
148 uint32_t osize;
149 lzma_stream stream = LZMA_STREAM_INIT;
150 lzma_ret result;
151
152 ibuf = (void*)(img + 1);
153 isize = img->compressed_size;
154 osize = img->original_size;
155 obuf = malloc(osize);
156 if (!obuf) {
157 fprintf(stderr, "Can't allocate %d bytes: %s\n",
158 osize,
159 strerror(errno));
160 return 0;
161 }
162
163 result = lzma_auto_decoder(&stream, -1, 0);
164 if (result != LZMA_OK) {
165 fprintf(stderr, "Unable to initialize auto decoder (error: %d)!\n",
166 result);
167 free(obuf);
168 return 0;
169 }
170
171 stream.next_in = ibuf;
172 stream.avail_in = isize;
173 stream.next_out = obuf;
174 stream.avail_out = osize;
175 result = lzma_code(&stream, LZMA_FINISH);
176 if (result != LZMA_STREAM_END) {
177 fprintf(stderr, "Unalbe to decode data (error: %d)!\n", result);
178 free(obuf);
179 return 0;
180 }
181 lzma_end(&stream);
182 return obuf;
183}
184
185
186
Bill Richardson61362d62011-02-14 10:28:03 -0800187// Show what's inside. If todir is NULL, just print. Otherwise unpack.
188int dump_bmpblock(const char *infile, int show_as_yaml,
189 const char *todir, int overwrite) {
190 void *ptr, *data_ptr;
Bill Richardson794d4d42011-02-10 19:13:10 -0800191 size_t length = 0;
192 BmpBlockHeader *hdr;
Bill Richardson61362d62011-02-14 10:28:03 -0800193 ImageInfo *img;
194 ScreenLayout *scr;
195 int loc_num;
196 int screen_num;
197 int i;
198 int offset;
199 int free_data;
200 char image_name[80];
201 char full_path_name[PATH_MAX];
202 int yfd, bfd;
203 FILE *yfp = stdout;
204 FILE *bfp = stdout;
Bill Richardson794d4d42011-02-10 19:13:10 -0800205
Bill Richardson61362d62011-02-14 10:28:03 -0800206 ptr = (void *)read_entire_file(infile, &length);
Bill Richardson794d4d42011-02-10 19:13:10 -0800207 if (!ptr)
208 return 1;
209
210 if (length < sizeof(BmpBlockHeader)) {
211 fprintf(stderr, "File %s is too small to be a BMPBLOCK\n", infile);
212 discard_file(ptr, length);
213 return 1;
214 }
215
216 if (0 != memcmp(ptr, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
217 fprintf(stderr, "File %s is not a BMPBLOCK\n", infile);
218 discard_file(ptr, length);
219 return 1;
220 }
221
Bill Richardson61362d62011-02-14 10:28:03 -0800222 if (todir) {
223 // Unpacking everything. Create the output directory if needed.
224 if (0 != require_dir(todir)) {
225 discard_file(ptr, length);
226 return 1;
227 }
228
229 // Open yaml output.
230 show_as_yaml = 1;
231
232 sprintf(full_path_name, "%s/%s", todir, "config.yaml");
233 yfd = open(full_path_name,
234 O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
235 0666);
236 if (yfd < 0) {
237 fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
238 strerror(errno));
239 discard_file(ptr, length);
240 return 1;
241 }
242
243 yfp = fdopen(yfd, "wb");
244 if (!yfp) {
245 fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
246 strerror(errno));
247 close(yfd);
248 discard_file(ptr, length);
249 return 1;
250 }
251 }
252
Bill Richardson794d4d42011-02-10 19:13:10 -0800253 hdr = (BmpBlockHeader *)ptr;
Bill Richardson61362d62011-02-14 10:28:03 -0800254
255 if (!show_as_yaml) {
256 printf("%s:\n", infile);
257 printf(" version %d.%d\n", hdr->major_version, hdr->minor_version);
258 printf(" %d screens\n", hdr->number_of_screenlayouts);
259 printf(" %d localizations\n", hdr->number_of_localizations);
260 printf(" %d discrete images\n", hdr->number_of_imageinfos);
261 discard_file(ptr, length);
262 return 0;
263 }
264
265 // Write out yaml
266 fprintf(yfp, "bmpblock: %d.%d\n", hdr->major_version, hdr->minor_version);
Bill Richardson61362d62011-02-14 10:28:03 -0800267 offset = sizeof(BmpBlockHeader) +
268 (sizeof(ScreenLayout) *
269 hdr->number_of_localizations *
270 hdr->number_of_screenlayouts);
Bill Richardsona7209ee2011-02-17 14:30:14 -0800271 // FIXME(chromium-os:12134): The bmbblock structure allows each image to be
272 // compressed differently, but we haven't provided a way for the yaml file to
273 // specify that. Additionally, we allow the yaml file to specify a default
274 // compression scheme for all images, but only if that line appears in the
275 // yaml file before any images. Accordingly, we'll just check the first image
276 // to see if it has any compression, and if it does, we'll write that out as
277 // the default. When this bug is fixed, we should just write each image's
278 // compression setting separately.
279 img = (ImageInfo *)(ptr + offset);
280 if (img->compression)
281 fprintf(yfp, "compression: %d\n", img->compression);
282 fprintf(yfp, "images:\n");
Bill Richardson61362d62011-02-14 10:28:03 -0800283 for(i=0; i<hdr->number_of_imageinfos; i++) {
284 img = (ImageInfo *)(ptr + offset);
Bill Richardson54e95822011-05-05 15:12:10 -0700285 if (img->compressed_size) {
286 sprintf(image_name, "img_%08x.bmp", offset);
287 if (img->tag == TAG_HWID) {
288 fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d\n",
289 RENDER_HWID, image_name,
290 img->width, img->height,
291 img->compressed_size, img->original_size, img->tag);
292 } else if (img->tag == TAG_HWID_RTOL) {
293 fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d\n",
294 RENDER_HWID_RTOL, image_name,
295 img->width, img->height,
296 img->compressed_size, img->original_size, img->tag);
297 } else {
298 fprintf(yfp, " img_%08x: %s # %dx%d %d/%d tag=%d\n",
299 offset, image_name,
300 img->width, img->height,
301 img->compressed_size, img->original_size, img->tag);
Bill Richardson61362d62011-02-14 10:28:03 -0800302 }
Bill Richardson54e95822011-05-05 15:12:10 -0700303 if (todir) {
304 sprintf(full_path_name, "%s/%s", todir, image_name);
305 bfd = open(full_path_name,
306 O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
307 0666);
308 if (bfd < 0) {
309 fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
310 strerror(errno));
311 fclose(yfp);
312 discard_file(ptr, length);
313 return 1;
314 }
315 bfp = fdopen(bfd, "wb");
316 if (!bfp) {
317 fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
318 strerror(errno));
319 close(bfd);
320 fclose(yfp);
321 discard_file(ptr, length);
322 return 1;
323 }
324 switch(img->compression) {
325 case COMPRESS_NONE:
326 data_ptr = ptr + offset + sizeof(ImageInfo);
327 free_data = 0;
328 break;
329 case COMPRESS_EFIv1:
330 data_ptr = do_efi_decompress(img);
331 if (!data_ptr) {
332 fclose(bfp);
333 fclose(yfp);
334 discard_file(ptr, length);
335 return 1;
336 }
337 free_data = 1;
338 break;
339 case COMPRESS_LZMA1:
340 data_ptr = do_lzma_decompress(img);
341 if (!data_ptr) {
342 fclose(bfp);
343 fclose(yfp);
344 discard_file(ptr, length);
345 return 1;
346 }
347 free_data = 1;
348 break;
349 default:
350 fprintf(stderr, "Unsupported compression method encountered.\n");
Bill Richardson61362d62011-02-14 10:28:03 -0800351 fclose(bfp);
352 fclose(yfp);
353 discard_file(ptr, length);
354 return 1;
355 }
Bill Richardson54e95822011-05-05 15:12:10 -0700356 if (1 != fwrite(data_ptr, img->original_size, 1, bfp)) {
357 fprintf(stderr, "Unable to write %s: %s\n", full_path_name,
358 strerror(errno));
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800359 fclose(bfp);
360 fclose(yfp);
361 discard_file(ptr, length);
362 return 1;
363 }
Bill Richardson61362d62011-02-14 10:28:03 -0800364 fclose(bfp);
Bill Richardson54e95822011-05-05 15:12:10 -0700365 if (free_data)
366 free(data_ptr);
Bill Richardson61362d62011-02-14 10:28:03 -0800367 }
Bill Richardson61362d62011-02-14 10:28:03 -0800368 }
369 offset += sizeof(ImageInfo);
370 offset += img->compressed_size;
371 // 4-byte aligned
372 if ((offset & 3) > 0)
373 offset = (offset & ~3) + 4;
374 }
375 fprintf(yfp, "screens:\n");
376 for(loc_num = 0;
377 loc_num < hdr->number_of_localizations;
378 loc_num++) {
379 for(screen_num = 0;
380 screen_num < hdr->number_of_screenlayouts;
381 screen_num++) {
382 fprintf(yfp, " scr_%d_%d:\n", loc_num, screen_num);
383 i = loc_num * hdr->number_of_screenlayouts + screen_num;
384 offset = sizeof(BmpBlockHeader) + i * sizeof(ScreenLayout);
385 scr = (ScreenLayout *)(ptr + offset);
386 for(i=0; i<MAX_IMAGE_IN_LAYOUT; i++) {
387 if (scr->images[i].image_info_offset) {
Bill Richardson54e95822011-05-05 15:12:10 -0700388 ImageInfo *iptr =
389 (ImageInfo *)(ptr + scr->images[i].image_info_offset);
390 if (iptr->tag == TAG_HWID) {
391 fprintf(yfp, " - [%d, %d, %s]\n",
392 scr->images[i].x, scr->images[i].y,
393 RENDER_HWID);
394 } else if (iptr->tag == TAG_HWID_RTOL) {
395 fprintf(yfp, " - [%d, %d, %s]\n",
396 scr->images[i].x, scr->images[i].y,
397 RENDER_HWID_RTOL);
398 } else {
399 fprintf(yfp, " - [%d, %d, img_%08x]\n",
400 scr->images[i].x, scr->images[i].y,
401 scr->images[i].image_info_offset);
402 }
Bill Richardson61362d62011-02-14 10:28:03 -0800403 }
404 }
405 }
406 }
407 fprintf(yfp, "localizations:\n");
408 for(loc_num = 0;
409 loc_num < hdr->number_of_localizations;
410 loc_num++) {
411 fprintf(yfp, " - [");
412 for(screen_num = 0;
413 screen_num < hdr->number_of_screenlayouts;
414 screen_num++) {
415 fprintf(yfp, " scr_%d_%d", loc_num, screen_num);
416 if (screen_num != hdr->number_of_screenlayouts - 1)
417 fprintf(yfp, ",");
418 }
419 fprintf(yfp, " ]\n");
420 }
421
422 if (todir)
423 fclose(yfp);
Bill Richardson794d4d42011-02-10 19:13:10 -0800424
425 discard_file(ptr, length);
426
427 return 0;
428}
429