blob: 5c94d8e9beca78829660745f8547b592d85e0ed8 [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);
285 sprintf(image_name, "img_%08x.bmp", offset);
286 fprintf(yfp, " img_%08x: %s # %dx%d %d/%d\n", offset, image_name,
287 img->width, img->height,
288 img->compressed_size, img->original_size);
289 if (todir) {
290 sprintf(full_path_name, "%s/%s", todir, image_name);
291 bfd = open(full_path_name,
292 O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
293 0666);
294 if (bfd < 0) {
295 fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
296 strerror(errno));
297 fclose(yfp);
298 discard_file(ptr, length);
299 return 1;
300 }
301 bfp = fdopen(bfd, "wb");
302 if (!bfp) {
303 fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
304 strerror(errno));
305 close(bfd);
306 fclose(yfp);
307 discard_file(ptr, length);
308 return 1;
309 }
310 switch(img->compression) {
311 case COMPRESS_NONE:
312 data_ptr = ptr + offset + sizeof(ImageInfo);
313 free_data = 0;
314 break;
315 case COMPRESS_EFIv1:
316 data_ptr = do_efi_decompress(img);
317 if (!data_ptr) {
318 fclose(bfp);
319 fclose(yfp);
320 discard_file(ptr, length);
321 return 1;
322 }
323 free_data = 1;
324 break;
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800325 case COMPRESS_LZMA1:
326 data_ptr = do_lzma_decompress(img);
327 if (!data_ptr) {
328 fclose(bfp);
329 fclose(yfp);
330 discard_file(ptr, length);
331 return 1;
332 }
333 free_data = 1;
334 break;
Bill Richardson61362d62011-02-14 10:28:03 -0800335 default:
336 fprintf(stderr, "Unsupported compression method encountered.\n");
337 fclose(bfp);
338 fclose(yfp);
339 discard_file(ptr, length);
340 return 1;
341 }
342 if (1 != fwrite(data_ptr, img->original_size, 1, bfp)) {
343 fprintf(stderr, "Unable to write %s: %s\n", full_path_name,
344 strerror(errno));
345 fclose(bfp);
346 fclose(yfp);
347 discard_file(ptr, length);
348 return 1;
349 }
350 fclose(bfp);
351 if (free_data)
352 free(data_ptr);
353 }
354 offset += sizeof(ImageInfo);
355 offset += img->compressed_size;
356 // 4-byte aligned
357 if ((offset & 3) > 0)
358 offset = (offset & ~3) + 4;
359 }
360 fprintf(yfp, "screens:\n");
361 for(loc_num = 0;
362 loc_num < hdr->number_of_localizations;
363 loc_num++) {
364 for(screen_num = 0;
365 screen_num < hdr->number_of_screenlayouts;
366 screen_num++) {
367 fprintf(yfp, " scr_%d_%d:\n", loc_num, screen_num);
368 i = loc_num * hdr->number_of_screenlayouts + screen_num;
369 offset = sizeof(BmpBlockHeader) + i * sizeof(ScreenLayout);
370 scr = (ScreenLayout *)(ptr + offset);
371 for(i=0; i<MAX_IMAGE_IN_LAYOUT; i++) {
372 if (scr->images[i].image_info_offset) {
373 fprintf(yfp, " - [%d, %d, img_%08x]\n",
374 scr->images[i].x, scr->images[i].y,
375 scr->images[i].image_info_offset);
376 }
377 }
378 }
379 }
380 fprintf(yfp, "localizations:\n");
381 for(loc_num = 0;
382 loc_num < hdr->number_of_localizations;
383 loc_num++) {
384 fprintf(yfp, " - [");
385 for(screen_num = 0;
386 screen_num < hdr->number_of_screenlayouts;
387 screen_num++) {
388 fprintf(yfp, " scr_%d_%d", loc_num, screen_num);
389 if (screen_num != hdr->number_of_screenlayouts - 1)
390 fprintf(yfp, ",");
391 }
392 fprintf(yfp, " ]\n");
393 }
394
395 if (todir)
396 fclose(yfp);
Bill Richardson794d4d42011-02-10 19:13:10 -0800397
398 discard_file(ptr, length);
399
400 return 0;
401}
402