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