otrx: support unsorted partitions offsets
[project/firmware-utils.git] / src / otrx.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * otrx
4 *
5 * Copyright (C) 2015-2017 Rafał Miłecki <zajec5@gmail.com>
6 */
7
8 #include <byteswap.h>
9 #include <endian.h>
10 #include <errno.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17
18 #if !defined(__BYTE_ORDER)
19 #error "Unknown byte order"
20 #endif
21
22 #if __BYTE_ORDER == __BIG_ENDIAN
23 #define cpu_to_le32(x) bswap_32(x)
24 #define le32_to_cpu(x) bswap_32(x)
25 #elif __BYTE_ORDER == __LITTLE_ENDIAN
26 #define cpu_to_le32(x) (x)
27 #define le32_to_cpu(x) (x)
28 #else
29 #error "Unsupported endianness"
30 #endif
31
32 #define TRX_MAGIC 0x30524448
33 #define TRX_FLAGS_OFFSET 12
34 #define TRX_MAX_PARTS 3
35
36 struct trx_header {
37 uint32_t magic;
38 uint32_t length;
39 uint32_t crc32;
40 uint16_t flags;
41 uint16_t version;
42 uint32_t offset[3];
43 };
44
45 struct otrx_part {
46 int idx;
47 size_t offset;
48 size_t length;
49 };
50
51 struct otrx_ctx {
52 FILE *fp;
53 struct trx_header hdr;
54 struct otrx_part parts[TRX_MAX_PARTS]; /* Sorted partitions */
55 };
56
57 char *trx_path;
58 size_t trx_offset = 0;
59 char *partition[TRX_MAX_PARTS] = {};
60
61 static inline size_t otrx_min(size_t x, size_t y) {
62 return x < y ? x : y;
63 }
64
65 /**************************************************
66 * CRC32
67 **************************************************/
68
69 static const uint32_t crc32_tbl[] = {
70 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
71 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
72 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
73 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
74 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
75 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
76 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
77 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
78 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
79 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
80 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
81 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
82 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
83 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
84 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
85 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
86 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
87 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
88 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
89 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
90 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
91 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
92 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
93 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
94 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
95 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
96 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
97 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
98 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
99 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
100 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
101 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
102 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
103 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
104 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
105 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
106 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
107 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
108 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
109 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
110 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
111 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
112 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
113 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
114 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
115 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
116 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
117 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
118 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
119 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
120 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
121 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
122 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
123 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
124 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
125 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
126 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
127 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
128 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
129 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
130 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
131 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
132 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
133 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
134 };
135
136 uint32_t otrx_crc32(uint32_t crc, uint8_t *buf, size_t len) {
137 while (len) {
138 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
139 buf++;
140 len--;
141 }
142
143 return crc;
144 }
145
146 /**************************************************
147 * Helpers
148 **************************************************/
149
150 static FILE *otrx_open(const char *pathname, const char *mode) {
151 if (strcmp(pathname, "-"))
152 return fopen(pathname, mode);
153
154 if (isatty(fileno(stdin))) {
155 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
156 return NULL;
157 }
158
159 return stdin;
160 }
161
162 static int otrx_skip(FILE *fp, size_t length)
163 {
164 if (fseek(fp, length, SEEK_CUR)) {
165 uint8_t buf[1024];
166 size_t bytes;
167
168 do {
169 bytes = fread(buf, 1, otrx_min(sizeof(buf), length), fp);
170 if (bytes <= 0)
171 return -EIO;
172 length -= bytes;
173 } while (length);
174 }
175
176 return 0;
177 }
178
179 static void otrx_close(FILE *fp) {
180 if (fp != stdin)
181 fclose(fp);
182 }
183
184 static int otrx_part_compar(const void *a, const void *b)
185 {
186 const struct otrx_part *partA = a;
187 const struct otrx_part *partB = b;
188
189 if (!partA->offset)
190 return 1;
191 if (!partB->offset)
192 return -1;
193
194 return partA->offset - partB->offset;
195 }
196
197 static int otrx_open_parse(const char *pathname, const char *mode,
198 struct otrx_ctx *otrx)
199 {
200 size_t length;
201 size_t bytes;
202 int err;
203 int i;
204
205 otrx->fp = otrx_open(pathname, mode);
206 if (!otrx->fp) {
207 fprintf(stderr, "Couldn't open %s\n", pathname);
208 err = -EACCES;
209 goto err_out;
210 }
211
212 if (trx_offset && otrx_skip(otrx->fp, trx_offset)) {
213 fprintf(stderr, "Couldn't skip first %zd B\n", trx_offset);
214 err = -EIO;
215 goto err_close;
216 }
217
218 bytes = fread(&otrx->hdr, 1, sizeof(otrx->hdr), otrx->fp);
219 if (bytes != sizeof(otrx->hdr)) {
220 fprintf(stderr, "Couldn't read %s header\n", pathname);
221 err = -EIO;
222 goto err_close;
223 }
224
225 if (le32_to_cpu(otrx->hdr.magic) != TRX_MAGIC) {
226 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(otrx->hdr.magic));
227 err = -EINVAL;
228 goto err_close;
229 }
230
231 length = le32_to_cpu(otrx->hdr.length);
232 if (length < sizeof(otrx->hdr)) {
233 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
234 err = -EINVAL;
235 goto err_close;
236 }
237
238 for (i = 0; i < TRX_MAX_PARTS; i++) {
239 otrx->parts[i].idx = i;
240 otrx->parts[i].offset = le32_to_cpu(otrx->hdr.offset[i]);
241 }
242 qsort(otrx->parts, TRX_MAX_PARTS, sizeof(otrx->parts[0]), otrx_part_compar);
243
244 /* Calculate length of every partition */
245 for (i = 0; i < TRX_MAX_PARTS; i++) {
246 if (otrx->parts[i].offset) {
247 if (i + 1 >= TRX_MAX_PARTS || !otrx->parts[i + 1].offset)
248 otrx->parts[i].length = le32_to_cpu(otrx->hdr.length) - otrx->parts[i].offset;
249 else
250 otrx->parts[i].length = otrx->parts[i + 1].offset - otrx->parts[i].offset;
251 }
252 }
253
254 return 0;
255
256 err_close:
257 otrx_close(otrx->fp);
258 err_out:
259 return err;
260 }
261
262 /**************************************************
263 * Check
264 **************************************************/
265
266 static void otrx_check_parse_options(int argc, char **argv) {
267 int c;
268
269 while ((c = getopt(argc, argv, "o:")) != -1) {
270 switch (c) {
271 case 'o':
272 trx_offset = atoi(optarg);
273 break;
274 }
275 }
276 }
277
278 static int otrx_check(int argc, char **argv) {
279 struct otrx_ctx otrx = { };
280 size_t bytes, length;
281 uint8_t buf[1024];
282 uint32_t crc32;
283 int err = 0;
284
285 if (argc < 3) {
286 fprintf(stderr, "No TRX file passed\n");
287 err = -EINVAL;
288 goto out;
289 }
290 trx_path = argv[2];
291
292 optind = 3;
293 otrx_check_parse_options(argc, argv);
294
295 err = otrx_open_parse(trx_path, "r", &otrx);
296 if (err) {
297 fprintf(stderr, "Couldn't open & parse %s: %d\n", trx_path, err);
298 err = -EACCES;
299 goto out;
300 }
301
302 crc32 = 0xffffffff;
303 crc32 = otrx_crc32(crc32, (uint8_t *)&otrx.hdr + TRX_FLAGS_OFFSET, sizeof(otrx.hdr) - TRX_FLAGS_OFFSET);
304 length = le32_to_cpu(otrx.hdr.length) - sizeof(otrx.hdr);
305 while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), otrx.fp)) > 0) {
306 crc32 = otrx_crc32(crc32, buf, bytes);
307 length -= bytes;
308 }
309
310 if (length) {
311 fprintf(stderr, "Couldn't read last %zd B of data from %s\n", length, trx_path);
312 err = -EIO;
313 goto err_close;
314 }
315
316 if (crc32 != le32_to_cpu(otrx.hdr.crc32)) {
317 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(otrx.hdr.crc32));
318 err = -EINVAL;
319 goto err_close;
320 }
321
322 printf("Found a valid TRX version %d\n", le32_to_cpu(otrx.hdr.version));
323
324 err_close:
325 otrx_close(otrx.fp);
326 out:
327 return err;
328 }
329
330 /**************************************************
331 * Create
332 **************************************************/
333
334 static ssize_t otrx_create_append_file(FILE *trx, const char *in_path) {
335 FILE *in;
336 size_t bytes;
337 ssize_t length = 0;
338 uint8_t buf[1024];
339
340 in = fopen(in_path, "r");
341 if (!in) {
342 fprintf(stderr, "Couldn't open %s\n", in_path);
343 return -EACCES;
344 }
345
346 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
347 if (fwrite(buf, 1, bytes, trx) != bytes) {
348 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path);
349 length = -EIO;
350 break;
351 }
352 length += bytes;
353 }
354
355 fclose(in);
356
357 return length;
358 }
359
360 static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) {
361 uint8_t *buf;
362
363 buf = malloc(length);
364 if (!buf)
365 return -ENOMEM;
366 memset(buf, 0, length);
367
368 if (fwrite(buf, 1, length, trx) != length) {
369 fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path);
370 free(buf);
371 return -EIO;
372 }
373
374 free(buf);
375
376 return length;
377 }
378
379 static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) {
380 if (curr_offset & (alignment - 1)) {
381 size_t length = alignment - (curr_offset % alignment);
382 return otrx_create_append_zeros(trx, length);
383 }
384
385 return 0;
386 }
387
388 static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) {
389 size_t bytes, length;
390 uint8_t buf[1024];
391 uint32_t crc32;
392
393 hdr->version = 1;
394
395 fseek(trx, 0, SEEK_SET);
396 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
397 if (bytes != sizeof(struct trx_header)) {
398 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
399 return -EIO;
400 }
401
402 length = le32_to_cpu(hdr->length);
403
404 crc32 = 0xffffffff;
405 fseek(trx, TRX_FLAGS_OFFSET, SEEK_SET);
406 length -= TRX_FLAGS_OFFSET;
407 while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) {
408 crc32 = otrx_crc32(crc32, buf, bytes);
409 length -= bytes;
410 }
411 hdr->crc32 = cpu_to_le32(crc32);
412
413 fseek(trx, 0, SEEK_SET);
414 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
415 if (bytes != sizeof(struct trx_header)) {
416 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
417 return -EIO;
418 }
419
420 return 0;
421 }
422
423 static int otrx_create(int argc, char **argv) {
424 FILE *trx;
425 struct trx_header hdr = {};
426 ssize_t sbytes;
427 size_t curr_idx = 0;
428 size_t curr_offset = sizeof(hdr);
429 char *e;
430 uint32_t magic;
431 int c;
432 int err = 0;
433
434 hdr.magic = cpu_to_le32(TRX_MAGIC);
435
436 if (argc < 3) {
437 fprintf(stderr, "No TRX file passed\n");
438 err = -EINVAL;
439 goto out;
440 }
441 trx_path = argv[2];
442
443 trx = fopen(trx_path, "w+");
444 if (!trx) {
445 fprintf(stderr, "Couldn't open %s\n", trx_path);
446 err = -EACCES;
447 goto out;
448 }
449 fseek(trx, curr_offset, SEEK_SET);
450
451 optind = 3;
452 while ((c = getopt(argc, argv, "f:A:a:b:M:")) != -1) {
453 switch (c) {
454 case 'f':
455 if (curr_idx >= TRX_MAX_PARTS) {
456 err = -ENOSPC;
457 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
458 goto err_close;
459 }
460
461 sbytes = otrx_create_append_file(trx, optarg);
462 if (sbytes < 0) {
463 fprintf(stderr, "Failed to append file %s\n", optarg);
464 } else {
465 hdr.offset[curr_idx++] = curr_offset;
466 curr_offset += sbytes;
467 }
468
469 sbytes = otrx_create_align(trx, curr_offset, 4);
470 if (sbytes < 0)
471 fprintf(stderr, "Failed to append zeros\n");
472 else
473 curr_offset += sbytes;
474
475 break;
476 case 'A':
477 sbytes = otrx_create_append_file(trx, optarg);
478 if (sbytes < 0) {
479 fprintf(stderr, "Failed to append file %s\n", optarg);
480 } else {
481 curr_offset += sbytes;
482 }
483
484 sbytes = otrx_create_align(trx, curr_offset, 4);
485 if (sbytes < 0)
486 fprintf(stderr, "Failed to append zeros\n");
487 else
488 curr_offset += sbytes;
489 break;
490 case 'a':
491 sbytes = otrx_create_align(trx, curr_offset, strtol(optarg, NULL, 0));
492 if (sbytes < 0)
493 fprintf(stderr, "Failed to append zeros\n");
494 else
495 curr_offset += sbytes;
496 break;
497 case 'b':
498 sbytes = strtol(optarg, NULL, 0) - curr_offset;
499 if (sbytes < 0) {
500 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
501 } else {
502 sbytes = otrx_create_append_zeros(trx, sbytes);
503 if (sbytes < 0)
504 fprintf(stderr, "Failed to append zeros\n");
505 else
506 curr_offset += sbytes;
507 }
508 break;
509 case 'M':
510 errno = 0;
511 magic = strtoul(optarg, &e, 0);
512 if (errno || (e == optarg) || *e)
513 fprintf(stderr, "illegal magic string %s\n", optarg);
514 else
515 hdr.magic = cpu_to_le32(magic);
516 break;
517 }
518 if (err)
519 break;
520 }
521
522 sbytes = otrx_create_align(trx, curr_offset, 0x1000);
523 if (sbytes < 0)
524 fprintf(stderr, "Failed to append zeros\n");
525 else
526 curr_offset += sbytes;
527
528 hdr.length = curr_offset;
529 otrx_create_write_hdr(trx, &hdr);
530 err_close:
531 fclose(trx);
532 out:
533 return err;
534 }
535
536 /**************************************************
537 * Extract
538 **************************************************/
539
540 static void otrx_extract_parse_options(int argc, char **argv) {
541 int c;
542
543 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
544 switch (c) {
545 case 'o':
546 trx_offset = atoi(optarg);
547 break;
548 case '1':
549 partition[0] = optarg;
550 break;
551 case '2':
552 partition[1] = optarg;
553 break;
554 case '3':
555 partition[2] = optarg;
556 break;
557 }
558 }
559 }
560
561 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
562 FILE *out;
563 size_t bytes;
564 uint8_t *buf;
565 int err = 0;
566
567 out = fopen(out_path, "w");
568 if (!out) {
569 fprintf(stderr, "Couldn't open %s\n", out_path);
570 err = -EACCES;
571 goto out;
572 }
573
574 buf = malloc(length);
575 if (!buf) {
576 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
577 err = -ENOMEM;
578 goto err_close;
579 }
580
581 fseek(trx, offset, SEEK_SET);
582 bytes = fread(buf, 1, length, trx);
583 if (bytes != length) {
584 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
585 err = -ENOMEM;
586 goto err_free_buf;
587 };
588
589 bytes = fwrite(buf, 1, length, out);
590 if (bytes != length) {
591 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
592 err = -ENOMEM;
593 goto err_free_buf;
594 }
595
596 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
597
598 err_free_buf:
599 free(buf);
600 err_close:
601 fclose(out);
602 out:
603 return err;
604 }
605
606 static int otrx_extract(int argc, char **argv) {
607 struct otrx_ctx otrx = { };
608 int i;
609 int err = 0;
610
611 if (argc < 3) {
612 fprintf(stderr, "No TRX file passed\n");
613 err = -EINVAL;
614 goto err_out;
615 }
616 trx_path = argv[2];
617
618 optind = 3;
619 otrx_extract_parse_options(argc, argv);
620
621 err = otrx_open_parse(trx_path, "r", &otrx);
622 if (err) {
623 fprintf(stderr, "Couldn't open & parse %s: %d\n", trx_path, err);
624 err = -EACCES;
625 goto err_out;
626 }
627
628 for (i = 0; i < TRX_MAX_PARTS; i++) {
629 struct otrx_part *part = &otrx.parts[i];
630
631 if (!part->offset && partition[part->idx])
632 printf("TRX doesn't contain partition %d, can't extract %s\n", part->idx + 1, partition[part->idx]);
633 if (!part->offset || !partition[part->idx])
634 continue;
635 else
636 otrx_extract_copy(otrx.fp, trx_offset + part->offset, part->length, partition[part->idx]);
637 }
638
639 otrx_close(otrx.fp);
640 err_out:
641 return err;
642 }
643
644 /**************************************************
645 * Start
646 **************************************************/
647
648 static void usage() {
649 printf("Usage:\n");
650 printf("\n");
651 printf("Checking TRX file:\n");
652 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
653 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
654 printf("\n");
655 printf("Creating new TRX file:\n");
656 printf("\totrx create <file> [options] [partitions]\n");
657 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
658 printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n");
659 printf("\t-a alignment\t\t\t[partition] align current partition\n");
660 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
661 printf("\n");
662 printf("Extracting from TRX file:\n");
663 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
664 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
665 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
666 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
667 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
668 }
669
670 int main(int argc, char **argv) {
671 if (argc > 1) {
672 if (!strcmp(argv[1], "check"))
673 return otrx_check(argc, argv);
674 else if (!strcmp(argv[1], "create"))
675 return otrx_create(argc, argv);
676 else if (!strcmp(argv[1], "extract"))
677 return otrx_extract(argc, argv);
678 }
679
680 usage();
681 return 0;
682 }