xref: /rk3399_rockchip-uboot/tools/rockchip/resource_tool.c (revision 5e817a0ea4271df00a147e77316f36f286ff9a56)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008-2015 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 
6 #include <errno.h>
7 #include <memory.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdbool.h>
12 #include <sys/stat.h>
13 #include <time.h>
14 #include <u-boot/sha1.h>
15 #include <u-boot/sha256.h>
16 
17 /* #define DEBUG */
18 
19 static bool g_debug =
20 #ifdef DEBUG
21         true;
22 #else
23         false;
24 #endif /* DEBUG */
25 
26 #define LOGE(fmt, args...)                                                     \
27   fprintf(stderr, "E/%s(%d): " fmt "\n", __func__, __LINE__, ##args)
28 #define LOGD(fmt, args...)                                                     \
29   do {                                                                         \
30     if (g_debug)                                                               \
31       fprintf(stderr, "D/%s(%d): " fmt "\n", __func__, __LINE__, ##args);      \
32   } while (0)
33 
34 /* sync with ./board/rockchip/rk30xx/rkloader.c #define FDT_PATH */
35 #define FDT_PATH "rk-kernel.dtb"
36 #define DTD_SUBFIX ".dtb"
37 
38 #define DEFAULT_IMAGE_PATH "resource.img"
39 #define DEFAULT_UNPACK_DIR "out"
40 #define BLOCK_SIZE 512
41 
42 #define RESOURCE_PTN_HDR_SIZE 1
43 #define INDEX_TBL_ENTR_SIZE 1
44 
45 #define RESOURCE_PTN_VERSION 0
46 #define INDEX_TBL_VERSION 0
47 
48 #define RESOURCE_PTN_HDR_MAGIC "RSCE"
49 typedef struct {
50 	char magic[4]; /* tag, "RSCE" */
51 	uint16_t resource_ptn_version;
52 	uint16_t index_tbl_version;
53 	uint8_t header_size;    /* blocks, size of ptn header. */
54 	uint8_t tbl_offset;     /* blocks, offset of index table. */
55 	uint8_t tbl_entry_size; /* blocks, size of index table's entry. */
56 	uint32_t tbl_entry_num; /* numbers of index table's entry. */
57 } resource_ptn_header;
58 
59 #define INDEX_TBL_ENTR_TAG "ENTR"
60 #define MAX_INDEX_ENTRY_PATH_LEN	220
61 #define MAX_HASH_LEN			32
62 
63 typedef struct {
64 	char tag[4]; /* tag, "ENTR" */
65 	char path[MAX_INDEX_ENTRY_PATH_LEN];
66 	char hash[MAX_HASH_LEN]; /* hash data */
67 	uint32_t hash_size;	 /* 20 or 32 */
68 	uint32_t content_offset; /* blocks, offset of resource content. */
69 	uint32_t content_size;   /* bytes, size of resource content. */
70 } index_tbl_entry;
71 
72 #define OPT_VERBOSE "--verbose"
73 #define OPT_HELP "--help"
74 #define OPT_VERSION "--version"
75 #define OPT_PRINT "--print"
76 #define OPT_PACK "--pack"
77 #define OPT_UNPACK "--unpack"
78 #define OPT_TEST_LOAD "--test_load"
79 #define OPT_TEST_CHARGE "--test_charge"
80 #define OPT_IMAGE "--image="
81 #define OPT_ROOT "--root="
82 
83 #define VERSION "2014-5-31 14:43:42"
84 
85 typedef struct {
86 	char path[MAX_INDEX_ENTRY_PATH_LEN];
87 	uint32_t content_offset; /* blocks, offset of resource content. */
88 	uint32_t content_size;   /* bytes, size of resource content. */
89 	void *load_addr;
90 } resource_content;
91 
92 typedef struct {
93 	int max_level;
94 	int num;
95 	int delay;
96 	char prefix[MAX_INDEX_ENTRY_PATH_LEN];
97 } anim_level_conf;
98 
99 #define DEF_CHARGE_DESC_PATH "charge_anim_desc.txt"
100 
101 #define OPT_CHARGE_ANIM_DELAY "delay="
102 #define OPT_CHARGE_ANIM_LOOP_CUR "only_current_level="
103 #define OPT_CHARGE_ANIM_LEVELS "levels="
104 #define OPT_CHARGE_ANIM_LEVEL_CONF "max_level="
105 #define OPT_CHARGE_ANIM_LEVEL_NUM "num="
106 #define OPT_CHARGE_ANIM_LEVEL_PFX "prefix="
107 
108 static char image_path[MAX_INDEX_ENTRY_PATH_LEN] = "\0";
109 
110 static int fix_blocks(size_t size)
111 {
112 	return (size + BLOCK_SIZE - 1) / BLOCK_SIZE;
113 }
114 
115 static const char *fix_path(const char *path)
116 {
117 	if (!memcmp(path, "./", 2)) {
118 		return path + 2;
119 	}
120 	return path;
121 }
122 
123 static uint16_t switch_short(uint16_t x)
124 {
125 	uint16_t val;
126 	uint8_t *p = (uint8_t *)(&x);
127 
128 	val = (*p++ & 0xff) << 0;
129 	val |= (*p & 0xff) << 8;
130 
131 	return val;
132 }
133 
134 static uint32_t switch_int(uint32_t x)
135 {
136 	uint32_t val;
137 	uint8_t *p = (uint8_t *)(&x);
138 
139 	val = (*p++ & 0xff) << 0;
140 	val |= (*p++ & 0xff) << 8;
141 	val |= (*p++ & 0xff) << 16;
142 	val |= (*p & 0xff) << 24;
143 
144 	return val;
145 }
146 
147 static void fix_header(resource_ptn_header *header)
148 {
149 	/* switch for be. */
150 	header->resource_ptn_version = switch_short(header->resource_ptn_version);
151 	header->index_tbl_version = switch_short(header->index_tbl_version);
152 	header->tbl_entry_num = switch_int(header->tbl_entry_num);
153 }
154 
155 static void fix_entry(index_tbl_entry *entry)
156 {
157 	/* switch for be. */
158 	entry->content_offset = switch_int(entry->content_offset);
159 	entry->content_size = switch_int(entry->content_size);
160 }
161 
162 static int inline get_ptn_offset(void)
163 {
164 	return 0;
165 }
166 
167 static bool StorageWriteLba(int offset_block, void *data, int blocks)
168 {
169 	bool ret = false;
170 	FILE *file = fopen(image_path, "rb+");
171 	if (!file)
172 		goto end;
173 	int offset = offset_block * BLOCK_SIZE;
174 	fseek(file, offset, SEEK_SET);
175 	if (offset != ftell(file)) {
176 		LOGE("Failed to seek %s to %d!", image_path, offset);
177 		goto end;
178 	}
179 	if (!fwrite(data, blocks * BLOCK_SIZE, 1, file)) {
180 		LOGE("Failed to write %s!", image_path);
181 		goto end;
182 	}
183 	ret = true;
184 end:
185 	if (file)
186 		fclose(file);
187 	return ret;
188 }
189 
190 static bool StorageReadLba(int offset_block, void *data, int blocks)
191 {
192 	bool ret = false;
193 	FILE *file = fopen(image_path, "rb");
194 	if (!file)
195 		goto end;
196 	int offset = offset_block * BLOCK_SIZE;
197 	fseek(file, offset, SEEK_SET);
198 	if (offset != ftell(file)) {
199 		goto end;
200 	}
201 	if (!fread(data, blocks * BLOCK_SIZE, 1, file)) {
202 		goto end;
203 	}
204 	ret = true;
205 end:
206 	if (file)
207 		fclose(file);
208 	return ret;
209 }
210 
211 static bool write_data(int offset_block, void *data, size_t len)
212 {
213 	bool ret = false;
214 	if (!data)
215 		goto end;
216 	int blocks = len / BLOCK_SIZE;
217 	if (blocks && !StorageWriteLba(offset_block, data, blocks)) {
218 		goto end;
219 	}
220 	int left = len % BLOCK_SIZE;
221 	if (left) {
222 		char buf[BLOCK_SIZE] = "\0";
223 		memcpy(buf, data + blocks * BLOCK_SIZE, left);
224 		if (!StorageWriteLba(offset_block + blocks, buf, 1))
225 			goto end;
226 	}
227 	ret = true;
228 end:
229 	return ret;
230 }
231 
232 /**********************load test************************/
233 static int load_file(const char *file_path, int offset_block, int blocks);
234 
235 static int test_load(int argc, char **argv)
236 {
237 	if (argc < 1) {
238 		LOGE("Nothing to load!");
239 		return -1;
240 	}
241 	const char *file_path;
242 	int offset_block = 0;
243 	int blocks = 0;
244 	if (argc > 0) {
245 		file_path = (const char *)fix_path(argv[0]);
246 		argc--, argv++;
247 	}
248 	if (argc > 0) {
249 		offset_block = atoi(argv[0]);
250 		argc--, argv++;
251 	}
252 	if (argc > 0) {
253 		blocks = atoi(argv[0]);
254 	}
255 	return load_file(file_path, offset_block, blocks);
256 }
257 
258 static void free_content(resource_content *content)
259 {
260 	if (content->load_addr) {
261 		free(content->load_addr);
262 		content->load_addr = 0;
263 	}
264 }
265 
266 static void tests_dump_file(const char *path, void *data, int len)
267 {
268 	FILE *file = fopen(path, "wb");
269 	if (!file)
270 		return;
271 	fwrite(data, len, 1, file);
272 	fclose(file);
273 }
274 
275 static bool load_content(resource_content *content)
276 {
277 	if (content->load_addr)
278 		return true;
279 	int blocks = fix_blocks(content->content_size);
280 	content->load_addr = malloc(blocks * BLOCK_SIZE);
281 	if (!content->load_addr)
282 		return false;
283 	if (!StorageReadLba(get_ptn_offset() + content->content_offset,
284 	                    content->load_addr, blocks)) {
285 		free_content(content);
286 		return false;
287 	}
288 
289 	tests_dump_file(content->path, content->load_addr, content->content_size);
290 	return true;
291 }
292 
293 static bool load_content_data(resource_content *content, int offset_block,
294                               void *data, int blocks)
295 {
296 	if (!StorageReadLba(get_ptn_offset() + content->content_offset + offset_block,
297 	                    data, blocks)) {
298 		return false;
299 	}
300 	tests_dump_file(content->path, data, blocks * BLOCK_SIZE);
301 	return true;
302 }
303 
304 static bool get_entry(const char *file_path, index_tbl_entry *entry)
305 {
306 	bool ret = false;
307 	char buf[BLOCK_SIZE];
308 	resource_ptn_header header;
309 	if (!StorageReadLba(get_ptn_offset(), buf, 1)) {
310 		LOGE("Failed to read header!");
311 		goto end;
312 	}
313 	memcpy(&header, buf, sizeof(header));
314 
315 	if (memcmp(header.magic, RESOURCE_PTN_HDR_MAGIC, sizeof(header.magic))) {
316 		LOGE("Not a resource image(%s)!", image_path);
317 		goto end;
318 	}
319 	/* test on pc, switch for be. */
320 	fix_header(&header);
321 
322 	/* TODO: support header_size & tbl_entry_size */
323 	if (header.resource_ptn_version != RESOURCE_PTN_VERSION ||
324 	    header.header_size != RESOURCE_PTN_HDR_SIZE ||
325 	    header.index_tbl_version != INDEX_TBL_VERSION ||
326 	    header.tbl_entry_size != INDEX_TBL_ENTR_SIZE) {
327 		LOGE("Not supported in this version!");
328 		goto end;
329 	}
330 
331 	int i;
332 	for (i = 0; i < header.tbl_entry_num; i++) {
333 		/* TODO: support tbl_entry_size */
334 		if (!StorageReadLba(
335 		            get_ptn_offset() + header.header_size + i * header.tbl_entry_size,
336 		            buf, 1)) {
337 			LOGE("Failed to read index entry:%d!", i);
338 			goto end;
339 		}
340 		memcpy(entry, buf, sizeof(*entry));
341 
342 		if (memcmp(entry->tag, INDEX_TBL_ENTR_TAG, sizeof(entry->tag))) {
343 			LOGE("Something wrong with index entry:%d!", i);
344 			goto end;
345 		}
346 
347 		if (!strncmp(entry->path, file_path, sizeof(entry->path)))
348 			break;
349 	}
350 	if (i == header.tbl_entry_num) {
351 		LOGE("Cannot find %s!", file_path);
352 		goto end;
353 	}
354 	/* test on pc, switch for be. */
355 	fix_entry(entry);
356 
357 	printf("Found entry:\n\tpath:%s\n\toffset:%d\tsize:%d\n", entry->path,
358 	       entry->content_offset, entry->content_size);
359 
360 	ret = true;
361 end:
362 	return ret;
363 }
364 
365 static bool get_content(resource_content *content)
366 {
367 	bool ret = false;
368 	index_tbl_entry entry;
369 	if (!get_entry(content->path, &entry))
370 		goto end;
371 	content->content_offset = entry.content_offset;
372 	content->content_size = entry.content_size;
373 	ret = true;
374 end:
375 	return ret;
376 }
377 
378 static int load_file(const char *file_path, int offset_block, int blocks)
379 {
380 	printf("Try to load:%s", file_path);
381 	if (blocks) {
382 		printf(", offset block:%d, blocks:%d\n", offset_block, blocks);
383 	} else {
384 		printf("\n");
385 	}
386 	bool ret = false;
387 	resource_content content;
388 	snprintf(content.path, sizeof(content.path), "%s", file_path);
389 	content.load_addr = 0;
390 	if (!get_content(&content)) {
391 		goto end;
392 	}
393 	if (!blocks) {
394 		if (!load_content(&content)) {
395 			goto end;
396 		}
397 	} else {
398 		void *data = malloc(blocks * BLOCK_SIZE);
399 		if (!data)
400 			goto end;
401 		if (!load_content_data(&content, offset_block, data, blocks)) {
402 			goto end;
403 		}
404 	}
405 	ret = true;
406 end:
407 	free_content(&content);
408 	return ret;
409 }
410 
411 /**********************load test end************************/
412 /**********************anim test************************/
413 
414 static bool parse_level_conf(const char *arg, anim_level_conf *level_conf)
415 {
416 	memset(level_conf, 0, sizeof(anim_level_conf));
417 	char *buf = NULL;
418 	buf = strstr(arg, OPT_CHARGE_ANIM_LEVEL_CONF);
419 	if (buf) {
420 		level_conf->max_level = atoi(buf + strlen(OPT_CHARGE_ANIM_LEVEL_CONF));
421 	} else {
422 		LOGE("Not found:%s", OPT_CHARGE_ANIM_LEVEL_CONF);
423 		return false;
424 	}
425 	buf = strstr(arg, OPT_CHARGE_ANIM_LEVEL_NUM);
426 	if (buf) {
427 		level_conf->num = atoi(buf + strlen(OPT_CHARGE_ANIM_LEVEL_NUM));
428 		if (level_conf->num <= 0) {
429 			return false;
430 		}
431 	} else {
432 		LOGE("Not found:%s", OPT_CHARGE_ANIM_LEVEL_NUM);
433 		return false;
434 	}
435 	buf = strstr(arg, OPT_CHARGE_ANIM_DELAY);
436 	if (buf) {
437 		level_conf->delay = atoi(buf + strlen(OPT_CHARGE_ANIM_DELAY));
438 	}
439 	buf = strstr(arg, OPT_CHARGE_ANIM_LEVEL_PFX);
440 	if (buf) {
441 		snprintf(level_conf->prefix, sizeof(level_conf->prefix), "%s",
442 		         buf + strlen(OPT_CHARGE_ANIM_LEVEL_PFX));
443 	} else {
444 		LOGE("Not found:%s", OPT_CHARGE_ANIM_LEVEL_PFX);
445 		return false;
446 	}
447 
448 	LOGD("Found conf:\nmax_level:%d, num:%d, delay:%d, prefix:%s",
449 	     level_conf->max_level, level_conf->num, level_conf->delay,
450 	     level_conf->prefix);
451 	return true;
452 }
453 
454 static int test_charge(int argc, char **argv)
455 {
456 	const char *desc;
457 	if (argc > 0) {
458 		desc = argv[0];
459 	} else {
460 		desc = DEF_CHARGE_DESC_PATH;
461 	}
462 
463 	resource_content content;
464 	snprintf(content.path, sizeof(content.path), "%s", desc);
465 	content.load_addr = 0;
466 	if (!get_content(&content)) {
467 		goto end;
468 	}
469 	if (!load_content(&content)) {
470 		goto end;
471 	}
472 
473 	char *buf = (char *)content.load_addr;
474 	char *end = buf + content.content_size - 1;
475 	*end = '\0';
476 	LOGD("desc:\n%s", buf);
477 
478 	int pos = 0;
479 	while (1) {
480 		char *line = (char *)memchr(buf + pos, '\n', strlen(buf + pos));
481 		if (!line)
482 			break;
483 		*line = '\0';
484 		LOGD("splite:%s", buf + pos);
485 		pos += (strlen(buf + pos) + 1);
486 	}
487 
488 	int delay = 900;
489 	int only_current_level = false;
490 	anim_level_conf *level_confs = NULL;
491 	int level_conf_pos = 0;
492 	int level_conf_num = 0;
493 
494 	while (true) {
495 		if (buf >= end)
496 			break;
497 		const char *arg = buf;
498 		buf += (strlen(buf) + 1);
499 
500 		LOGD("parse arg:%s", arg);
501 		if (!memcmp(arg, OPT_CHARGE_ANIM_LEVEL_CONF,
502 		            strlen(OPT_CHARGE_ANIM_LEVEL_CONF))) {
503 			if (!level_confs) {
504 				LOGE("Found level conf before levels!");
505 				goto end;
506 			}
507 			if (level_conf_pos >= level_conf_num) {
508 				LOGE("Too many level confs!(%d >= %d)", level_conf_pos, level_conf_num);
509 				goto end;
510 			}
511 			if (!parse_level_conf(arg, level_confs + level_conf_pos)) {
512 				LOGE("Failed to parse level conf:%s", arg);
513 				goto end;
514 			}
515 			level_conf_pos++;
516 		} else if (!memcmp(arg, OPT_CHARGE_ANIM_DELAY,
517 		                   strlen(OPT_CHARGE_ANIM_DELAY))) {
518 			delay = atoi(arg + strlen(OPT_CHARGE_ANIM_DELAY));
519 			LOGD("Found delay:%d", delay);
520 		} else if (!memcmp(arg, OPT_CHARGE_ANIM_LOOP_CUR,
521 		                   strlen(OPT_CHARGE_ANIM_LOOP_CUR))) {
522 			only_current_level =
523 			        !memcmp(arg + strlen(OPT_CHARGE_ANIM_LOOP_CUR), "true", 4);
524 			LOGD("Found only_current_level:%d", only_current_level);
525 		} else if (!memcmp(arg, OPT_CHARGE_ANIM_LEVELS,
526 		                   strlen(OPT_CHARGE_ANIM_LEVELS))) {
527 			if (level_conf_num) {
528 				goto end;
529 			}
530 			level_conf_num = atoi(arg + strlen(OPT_CHARGE_ANIM_LEVELS));
531 			if (!level_conf_num) {
532 				goto end;
533 			}
534 			level_confs =
535 			        (anim_level_conf *)malloc(level_conf_num * sizeof(anim_level_conf));
536 			LOGD("Found levels:%d", level_conf_num);
537 		} else {
538 			LOGE("Unknown arg:%s", arg);
539 			goto end;
540 		}
541 	}
542 
543 	if (level_conf_pos != level_conf_num || !level_conf_num) {
544 		LOGE("Something wrong with level confs!");
545 		goto end;
546 	}
547 
548 	int i = 0, j = 0;
549 	for (i = 0; i < level_conf_num; i++) {
550 		if (!level_confs[i].delay) {
551 			level_confs[i].delay = delay;
552 		}
553 		if (!level_confs[i].delay) {
554 			LOGE("Missing delay in level conf:%d", i);
555 			goto end;
556 		}
557 		for (j = 0; j < i; j++) {
558 			if (level_confs[j].max_level == level_confs[i].max_level) {
559 				LOGE("Dup level conf:%d", i);
560 				goto end;
561 			}
562 			if (level_confs[j].max_level > level_confs[i].max_level) {
563 				anim_level_conf conf = level_confs[i];
564 				memmove(level_confs + j + 1, level_confs + j,
565 				        (i - j) * sizeof(anim_level_conf));
566 				level_confs[j] = conf;
567 			}
568 		}
569 	}
570 
571 	printf("Parse anim desc(%s):\n", desc);
572 	printf("only_current_level=%d\n", only_current_level);
573 	printf("level conf:\n");
574 	for (i = 0; i < level_conf_num; i++) {
575 		printf("\tmax=%d, delay=%d, num=%d, prefix=%s\n", level_confs[i].max_level,
576 		       level_confs[i].delay, level_confs[i].num, level_confs[i].prefix);
577 	}
578 
579 end:
580 	free_content(&content);
581 	return 0;
582 }
583 
584 /**********************anim test end************************/
585 /**********************append file************************/
586 
587 static const char *PROG = NULL;
588 static resource_ptn_header header;
589 static bool just_print = false;
590 static char root_path[MAX_INDEX_ENTRY_PATH_LEN] = "\0";
591 
592 static void version(void)
593 {
594 	printf("%s (cjf@rock-chips.com)\t" VERSION "\n", PROG);
595 }
596 
597 static void usage(void)
598 {
599 	printf("Usage: %s [options] [FILES]\n", PROG);
600 	printf("Tools for Rockchip's resource image.\n");
601 	version();
602 	printf("Options:\n");
603 	printf("\t" OPT_PACK "\t\t\tPack image from given files.\n");
604 	printf("\t" OPT_UNPACK "\t\tUnpack given image to current dir.\n");
605 	printf("\t" OPT_IMAGE "path"
606 	       "\t\tSpecify input/output image path.\n");
607 	printf("\t" OPT_PRINT "\t\t\tJust print informations.\n");
608 	printf("\t" OPT_VERBOSE "\t\tDisplay more runtime informations.\n");
609 	printf("\t" OPT_HELP "\t\t\tDisplay this information.\n");
610 	printf("\t" OPT_VERSION "\t\tDisplay version information.\n");
611 	printf("\t" OPT_ROOT "path"
612 	       "\t\tSpecify resources' root dir.\n");
613 }
614 
615 static int pack_image(int file_num, const char **files);
616 static int unpack_image(const char *unpack_dir);
617 
618 enum ACTION {
619 	ACTION_PACK,
620 	ACTION_UNPACK,
621 	ACTION_TEST_LOAD,
622 	ACTION_TEST_CHARGE,
623 };
624 
625 int main(int argc, char **argv)
626 {
627 	PROG = fix_path(argv[0]);
628 
629 	enum ACTION action = ACTION_PACK;
630 
631 	argc--, argv++;
632 	while (argc > 0 && argv[0][0] == '-') {
633 		/* it's a opt arg. */
634 		const char *arg = argv[0];
635 		argc--, argv++;
636 		if (!strcmp(OPT_VERBOSE, arg)) {
637 			g_debug = true;
638 		} else if (!strcmp(OPT_HELP, arg)) {
639 			usage();
640 			return 0;
641 		} else if (!strcmp(OPT_VERSION, arg)) {
642 			version();
643 			return 0;
644 		} else if (!strcmp(OPT_PRINT, arg)) {
645 			just_print = true;
646 		} else if (!strcmp(OPT_PACK, arg)) {
647 			action = ACTION_PACK;
648 		} else if (!strcmp(OPT_UNPACK, arg)) {
649 			action = ACTION_UNPACK;
650 		} else if (!strcmp(OPT_TEST_LOAD, arg)) {
651 			action = ACTION_TEST_LOAD;
652 		} else if (!strcmp(OPT_TEST_CHARGE, arg)) {
653 			action = ACTION_TEST_CHARGE;
654 		} else if (!memcmp(OPT_IMAGE, arg, strlen(OPT_IMAGE))) {
655 			snprintf(image_path, sizeof(image_path), "%s", arg + strlen(OPT_IMAGE));
656 		} else if (!memcmp(OPT_ROOT, arg, strlen(OPT_ROOT))) {
657 			snprintf(root_path, sizeof(root_path), "%s", arg + strlen(OPT_ROOT));
658 		} else {
659 			LOGE("Unknown opt:%s", arg);
660 			usage();
661 			return -1;
662 		}
663 	}
664 
665 	if (!image_path[0]) {
666 		snprintf(image_path, sizeof(image_path), "%s", DEFAULT_IMAGE_PATH);
667 	}
668 
669 	switch (action) {
670 	case ACTION_PACK: {
671 		int file_num = argc;
672 		const char **files = (const char **)argv;
673 		if (!file_num) {
674 			LOGE("No file to pack!");
675 			return 0;
676 		}
677 		LOGD("try to pack %d files.", file_num);
678 		return pack_image(file_num, files);
679 	}
680 	case ACTION_UNPACK: {
681 		return unpack_image(argc > 0 ? argv[0] : DEFAULT_UNPACK_DIR);
682 	}
683 	case ACTION_TEST_LOAD: {
684 		return test_load(argc, argv);
685 	}
686 	case ACTION_TEST_CHARGE: {
687 		return test_charge(argc, argv);
688 	}
689 	}
690 	/* not reach here. */
691 	return -1;
692 }
693 
694 /************unpack code****************/
695 static bool mkdirs(char *path)
696 {
697 	char *tmp = path;
698 	char *pos = NULL;
699 	char buf[MAX_INDEX_ENTRY_PATH_LEN];
700 	bool ret = true;
701 	while ((pos = memchr(tmp, '/', strlen(tmp)))) {
702 		strcpy(buf, path);
703 		buf[pos - path] = '\0';
704 		tmp = pos + 1;
705 		LOGD("mkdir:%s", buf);
706 		if (!mkdir(buf, 0755)) {
707 			ret = false;
708 		}
709 	}
710 	if (!ret)
711 		LOGD("Failed to mkdir(%s)!", path);
712 	return ret;
713 }
714 
715 static bool dump_file(FILE *file, const char *unpack_dir,
716                       index_tbl_entry entry)
717 {
718 	LOGD("try to dump entry:%s", entry.path);
719 	bool ret = false;
720 	FILE *out_file = NULL;
721 	long int pos = 0;
722 	char path[MAX_INDEX_ENTRY_PATH_LEN * 2 + 1];
723 	if (just_print) {
724 		ret = true;
725 		goto done;
726 	}
727 
728 	pos = ftell(file);
729 	snprintf(path, sizeof(path), "%s/%s", unpack_dir, entry.path);
730 	mkdirs(path);
731 	out_file = fopen(path, "wb");
732 	if (!out_file) {
733 		LOGE("Failed to create:%s", path);
734 		goto end;
735 	}
736 	long int offset = entry.content_offset * BLOCK_SIZE;
737 	fseek(file, offset, SEEK_SET);
738 	if (offset != ftell(file)) {
739 		LOGE("Failed to read content:%s", entry.path);
740 		goto end;
741 	}
742 	char buf[BLOCK_SIZE];
743 	int n;
744 	int len = entry.content_size;
745 	while (len > 0) {
746 		n = len > BLOCK_SIZE ? BLOCK_SIZE : len;
747 		if (!fread(buf, n, 1, file)) {
748 			LOGE("Failed to read content:%s", entry.path);
749 			goto end;
750 		}
751 		if (!fwrite(buf, n, 1, out_file)) {
752 			LOGE("Failed to write:%s", entry.path);
753 			goto end;
754 		}
755 		len -= n;
756 	}
757 done:
758 	ret = true;
759 end:
760 	if (out_file)
761 		fclose(out_file);
762 	if (pos)
763 		fseek(file, pos, SEEK_SET);
764 	return ret;
765 }
766 
767 static int unpack_image(const char *dir)
768 {
769 	FILE *image_file = NULL;
770 	bool ret = false;
771 	char unpack_dir[MAX_INDEX_ENTRY_PATH_LEN];
772 	if (just_print)
773 		dir = ".";
774 	snprintf(unpack_dir, sizeof(unpack_dir), "%s", dir);
775 	if (!strlen(unpack_dir)) {
776 		goto end;
777 	} else if (unpack_dir[strlen(unpack_dir) - 1] == '/') {
778 		unpack_dir[strlen(unpack_dir) - 1] = '\0';
779 	}
780 
781 	mkdir(unpack_dir, 0755);
782 	image_file = fopen(image_path, "rb");
783 	char buf[BLOCK_SIZE];
784 	if (!image_file) {
785 		LOGE("Failed to open:%s", image_path);
786 		goto end;
787 	}
788 	if (!fread(buf, BLOCK_SIZE, 1, image_file)) {
789 		LOGE("Failed to read header!");
790 		goto end;
791 	}
792 	memcpy(&header, buf, sizeof(header));
793 
794 	if (memcmp(header.magic, RESOURCE_PTN_HDR_MAGIC, sizeof(header.magic))) {
795 		LOGE("Not a resource image(%s)!", image_path);
796 		goto end;
797 	}
798 	/* switch for be. */
799 	fix_header(&header);
800 
801 	printf("Dump header:\n");
802 	printf("partition version:%d.%d\n", header.resource_ptn_version,
803 	       header.index_tbl_version);
804 	printf("header size:%d\n", header.header_size);
805 	printf("index tbl:\n\toffset:%d\tentry size:%d\tentry num:%d\n",
806 	       header.tbl_offset, header.tbl_entry_size, header.tbl_entry_num);
807 
808 	/* TODO: support header_size & tbl_entry_size */
809 	if (header.resource_ptn_version != RESOURCE_PTN_VERSION ||
810 	    header.header_size != RESOURCE_PTN_HDR_SIZE ||
811 	    header.index_tbl_version != INDEX_TBL_VERSION ||
812 	    header.tbl_entry_size != INDEX_TBL_ENTR_SIZE) {
813 		LOGE("Not supported in this version!");
814 		goto end;
815 	}
816 
817 	printf("Dump Index table:\n");
818 	index_tbl_entry entry;
819 	int i;
820 	for (i = 0; i < header.tbl_entry_num; i++) {
821 		/* TODO: support tbl_entry_size */
822 		if (!fread(buf, BLOCK_SIZE, 1, image_file)) {
823 			LOGE("Failed to read index entry:%d!", i);
824 			goto end;
825 		}
826 		memcpy(&entry, buf, sizeof(entry));
827 
828 		if (memcmp(entry.tag, INDEX_TBL_ENTR_TAG, sizeof(entry.tag))) {
829 			LOGE("Something wrong with index entry:%d!", i);
830 			goto end;
831 		}
832 		/* switch for be. */
833 		fix_entry(&entry);
834 
835 		printf("entry(%d):\n\tpath:%s\n\toffset:%d\tsize:%d\n", i, entry.path,
836 		       entry.content_offset, entry.content_size);
837 		if (!dump_file(image_file, unpack_dir, entry)) {
838 			goto end;
839 		}
840 	}
841 	printf("Unack %s to %s successed!\n", image_path, unpack_dir);
842 	ret = true;
843 end:
844 	if (image_file)
845 		fclose(image_file);
846 	return ret ? 0 : -1;
847 }
848 
849 /************unpack code end****************/
850 /************pack code****************/
851 
852 static inline size_t get_file_size(const char *path)
853 {
854 	LOGD("try to get size(%s)...", path);
855 	struct stat st;
856 	if (stat(path, &st) < 0) {
857 		LOGE("Failed to get size:%s", path);
858 		return -1;
859 	}
860 	LOGD("path:%s, size:%ld", path, st.st_size);
861 	return st.st_size;
862 }
863 
864 static int write_file(int offset_block, const char *src_path,
865 		      char hash[], int hash_size)
866 {
867 	LOGD("try to write file(%s) to offset:%d...", src_path, offset_block);
868 	char *buf = NULL;
869 	int ret = -1;
870 	size_t file_size;
871 	FILE *src_file = fopen(src_path, "rb");
872 	if (!src_file) {
873 		LOGE("Failed to open:%s", src_path);
874 		goto end;
875 	}
876 
877 	file_size = get_file_size(src_path);
878 	if (file_size < 0) {
879 		goto end;
880 	}
881 
882 	buf = calloc(file_size, 1);
883 	if (!buf)
884 		goto end;
885 
886 	if (!fread(buf, file_size, 1, src_file))
887 		goto end;
888 
889 	if (!write_data(offset_block, buf, file_size))
890 		goto end;
891 
892 	if (hash_size == 20)
893 		sha1_csum((const unsigned char *)buf, file_size,
894 			  (unsigned char *)hash);
895 	else if (hash_size == 32)
896 		sha256_csum((const unsigned char *)buf, file_size,
897 			    (unsigned char *)hash);
898 	else
899 		goto end;
900 
901 	ret = file_size;
902 end:
903 	if (src_file)
904 		fclose(src_file);
905 	if (buf)
906 		free(buf);
907 
908 	return ret;
909 }
910 
911 static bool write_header(const int file_num)
912 {
913 	LOGD("try to write header...");
914 	memcpy(header.magic, RESOURCE_PTN_HDR_MAGIC, sizeof(header.magic));
915 	header.resource_ptn_version = RESOURCE_PTN_VERSION;
916 	header.index_tbl_version = INDEX_TBL_VERSION;
917 	header.header_size = RESOURCE_PTN_HDR_SIZE;
918 	header.tbl_offset = header.header_size;
919 	header.tbl_entry_size = INDEX_TBL_ENTR_SIZE;
920 	header.tbl_entry_num = file_num;
921 
922 	/* switch for le. */
923 	resource_ptn_header hdr = header;
924 	fix_header(&hdr);
925 	return write_data(0, &hdr, sizeof(hdr));
926 }
927 
928 static bool write_index_tbl(const int file_num, const char **files)
929 {
930 	LOGD("try to write index table...");
931 	bool ret = false;
932 	bool foundFdt = false;
933 	int offset =
934 	        header.header_size + header.tbl_entry_size * header.tbl_entry_num;
935 	index_tbl_entry entry;
936 	char hash[20];	/* sha1 */
937 	int i;
938 
939 	memcpy(entry.tag, INDEX_TBL_ENTR_TAG, sizeof(entry.tag));
940 	for (i = 0; i < file_num; i++) {
941 		size_t file_size = get_file_size(files[i]);
942 		if (file_size < 0)
943 			goto end;
944 		entry.content_size = file_size;
945 		entry.content_offset = offset;
946 
947 		if (write_file(offset, files[i], hash, sizeof(hash)) < 0)
948 			goto end;
949 
950 		memcpy(entry.hash, hash, sizeof(hash));
951 		entry.hash_size = sizeof(hash);
952 
953 		LOGD("try to write index entry(%s)...", files[i]);
954 
955 		/* switch for le. */
956 		fix_entry(&entry);
957 		memset(entry.path, 0, sizeof(entry.path));
958 		const char *path = files[i];
959 		if (root_path[0]) {
960 			if (!strncmp(path, root_path, strlen(root_path))) {
961 				path += strlen(root_path);
962 				if (path[0] == '/')
963 					path++;
964 			}
965 		}
966 		path = fix_path(path);
967 		if (!strcmp(files[i] + strlen(files[i]) - strlen(DTD_SUBFIX), DTD_SUBFIX)) {
968 			if (!foundFdt) {
969 				/* use default path. */
970 				LOGD("mod fdt path:%s -> %s...", files[i], FDT_PATH);
971 				path = FDT_PATH;
972 				foundFdt = true;
973 			}
974 		}
975 		snprintf(entry.path, sizeof(entry.path), "%s", path);
976 		offset += fix_blocks(file_size);
977 		if (!write_data(header.header_size + i * header.tbl_entry_size, &entry,
978 		                sizeof(entry)))
979 			goto end;
980 	}
981 	ret = true;
982 end:
983 	return ret;
984 }
985 
986 static int pack_image(int file_num, const char **files)
987 {
988 	bool ret = false;
989 	FILE *image_file = fopen(image_path, "wb");
990 	if (!image_file) {
991 		LOGE("Failed to create:%s", image_path);
992 		goto end;
993 	}
994 	fclose(image_file);
995 
996 	/* prepare files */
997 	int i = 0;
998 	int pos = 0;
999 	const char *tmp;
1000 	for (i = 0; i < file_num; i++) {
1001 		if (!strcmp(files[i] + strlen(files[i]) - strlen(DTD_SUBFIX), DTD_SUBFIX)) {
1002 			/* dtb files for kernel. */
1003 			tmp = files[pos];
1004 			files[pos] = files[i];
1005 			files[i] = tmp;
1006 			pos++;
1007 		} else if (!strcmp(fix_path(image_path), fix_path(files[i]))) {
1008 			/* not to pack image itself! */
1009 			tmp = files[file_num - 1];
1010 			files[file_num - 1] = files[i];
1011 			files[i] = tmp;
1012 			file_num--;
1013 		}
1014 	}
1015 
1016 	if (!write_header(file_num)) {
1017 		LOGE("Failed to write header!");
1018 		goto end;
1019 	}
1020 	if (!write_index_tbl(file_num, files)) {
1021 		LOGE("Failed to write index table!");
1022 		goto end;
1023 	}
1024 	printf("Pack to %s successed!\n", image_path);
1025 	ret = true;
1026 end:
1027 	return ret ? 0 : -1;
1028 }
1029 
1030 /************pack code end****************/
1031