1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * Copyright (C) 2015-2017 Rafał Miłecki <zajec5@gmail.com>
18 #if !defined(__BYTE_ORDER)
19 #error "Unknown byte order"
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)
29 #error "Unsupported endianness"
32 #define TRX_MAGIC 0x30524448
33 #define TRX_FLAGS_OFFSET 12
34 #define TRX_MAX_PARTS 3
53 struct trx_header hdr
;
54 struct otrx_part parts
[TRX_MAX_PARTS
]; /* Sorted partitions */
58 size_t trx_offset
= 0;
59 char *partition
[TRX_MAX_PARTS
] = {};
61 static inline size_t otrx_min(size_t x
, size_t y
) {
65 /**************************************************
67 **************************************************/
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,
136 uint32_t otrx_crc32(uint32_t crc
, uint8_t *buf
, size_t len
) {
138 crc
= crc32_tbl
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
146 /**************************************************
148 **************************************************/
150 static FILE *otrx_open(const char *pathname
, const char *mode
) {
151 if (strcmp(pathname
, "-"))
152 return fopen(pathname
, mode
);
154 if (isatty(fileno(stdin
))) {
155 fprintf(stderr
, "Reading from TTY stdin is unsupported\n");
162 static int otrx_skip(FILE *fp
, size_t length
)
164 if (fseek(fp
, length
, SEEK_CUR
)) {
169 bytes
= fread(buf
, 1, otrx_min(sizeof(buf
), length
), fp
);
179 static void otrx_close(FILE *fp
) {
184 static int otrx_part_compar(const void *a
, const void *b
)
186 const struct otrx_part
*partA
= a
;
187 const struct otrx_part
*partB
= b
;
194 return partA
->offset
- partB
->offset
;
197 static int otrx_open_parse(const char *pathname
, const char *mode
,
198 struct otrx_ctx
*otrx
)
205 otrx
->fp
= otrx_open(pathname
, mode
);
207 fprintf(stderr
, "Couldn't open %s\n", pathname
);
212 if (trx_offset
&& otrx_skip(otrx
->fp
, trx_offset
)) {
213 fprintf(stderr
, "Couldn't skip first %zd B\n", trx_offset
);
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
);
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
));
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
);
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
]);
242 qsort(otrx
->parts
, TRX_MAX_PARTS
, sizeof(otrx
->parts
[0]), otrx_part_compar
);
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
;
250 otrx
->parts
[i
].length
= otrx
->parts
[i
+ 1].offset
- otrx
->parts
[i
].offset
;
257 otrx_close(otrx
->fp
);
262 /**************************************************
264 **************************************************/
266 static void otrx_check_parse_options(int argc
, char **argv
) {
269 while ((c
= getopt(argc
, argv
, "o:")) != -1) {
272 trx_offset
= atoi(optarg
);
278 static int otrx_check(int argc
, char **argv
) {
279 struct otrx_ctx otrx
= { };
280 size_t bytes
, length
;
286 fprintf(stderr
, "No TRX file passed\n");
293 otrx_check_parse_options(argc
, argv
);
295 err
= otrx_open_parse(trx_path
, "r", &otrx
);
297 fprintf(stderr
, "Couldn't open & parse %s: %d\n", trx_path
, err
);
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
);
311 fprintf(stderr
, "Couldn't read last %zd B of data from %s\n", length
, trx_path
);
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
));
322 printf("Found a valid TRX version %d\n", le32_to_cpu(otrx
.hdr
.version
));
330 /**************************************************
332 **************************************************/
334 static ssize_t
otrx_create_append_file(FILE *trx
, const char *in_path
) {
340 in
= fopen(in_path
, "r");
342 fprintf(stderr
, "Couldn't open %s\n", in_path
);
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
);
360 static ssize_t
otrx_create_append_zeros(FILE *trx
, size_t length
) {
363 buf
= malloc(length
);
366 memset(buf
, 0, length
);
368 if (fwrite(buf
, 1, length
, trx
) != length
) {
369 fprintf(stderr
, "Couldn't write %zu B to %s\n", length
, trx_path
);
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
);
388 static int otrx_create_write_hdr(FILE *trx
, struct trx_header
*hdr
) {
389 size_t bytes
, length
;
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
);
402 length
= le32_to_cpu(hdr
->length
);
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
);
411 hdr
->crc32
= cpu_to_le32(crc32
);
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
);
423 static int otrx_create(int argc
, char **argv
) {
425 struct trx_header hdr
= {};
428 size_t curr_offset
= sizeof(hdr
);
434 hdr
.magic
= cpu_to_le32(TRX_MAGIC
);
437 fprintf(stderr
, "No TRX file passed\n");
443 trx
= fopen(trx_path
, "w+");
445 fprintf(stderr
, "Couldn't open %s\n", trx_path
);
449 fseek(trx
, curr_offset
, SEEK_SET
);
452 while ((c
= getopt(argc
, argv
, "f:A:a:b:M:")) != -1) {
455 if (curr_idx
>= TRX_MAX_PARTS
) {
457 fprintf(stderr
, "Reached TRX partitions limit, no place for %s\n", optarg
);
461 sbytes
= otrx_create_append_file(trx
, optarg
);
463 fprintf(stderr
, "Failed to append file %s\n", optarg
);
465 hdr
.offset
[curr_idx
++] = curr_offset
;
466 curr_offset
+= sbytes
;
469 sbytes
= otrx_create_align(trx
, curr_offset
, 4);
471 fprintf(stderr
, "Failed to append zeros\n");
473 curr_offset
+= sbytes
;
477 sbytes
= otrx_create_append_file(trx
, optarg
);
479 fprintf(stderr
, "Failed to append file %s\n", optarg
);
481 curr_offset
+= sbytes
;
484 sbytes
= otrx_create_align(trx
, curr_offset
, 4);
486 fprintf(stderr
, "Failed to append zeros\n");
488 curr_offset
+= sbytes
;
491 sbytes
= otrx_create_align(trx
, curr_offset
, strtol(optarg
, NULL
, 0));
493 fprintf(stderr
, "Failed to append zeros\n");
495 curr_offset
+= sbytes
;
498 sbytes
= strtol(optarg
, NULL
, 0) - curr_offset
;
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));
502 sbytes
= otrx_create_append_zeros(trx
, sbytes
);
504 fprintf(stderr
, "Failed to append zeros\n");
506 curr_offset
+= sbytes
;
511 magic
= strtoul(optarg
, &e
, 0);
512 if (errno
|| (e
== optarg
) || *e
)
513 fprintf(stderr
, "illegal magic string %s\n", optarg
);
515 hdr
.magic
= cpu_to_le32(magic
);
522 sbytes
= otrx_create_align(trx
, curr_offset
, 0x1000);
524 fprintf(stderr
, "Failed to append zeros\n");
526 curr_offset
+= sbytes
;
528 hdr
.length
= curr_offset
;
529 otrx_create_write_hdr(trx
, &hdr
);
536 /**************************************************
538 **************************************************/
540 static void otrx_extract_parse_options(int argc
, char **argv
) {
543 while ((c
= getopt(argc
, argv
, "c:e:o:1:2:3:")) != -1) {
546 trx_offset
= atoi(optarg
);
549 partition
[0] = optarg
;
552 partition
[1] = optarg
;
555 partition
[2] = optarg
;
561 static int otrx_extract_copy(struct otrx_ctx
*otrx
, size_t length
, char *out_path
) {
567 out
= fopen(out_path
, "w");
569 fprintf(stderr
, "Couldn't open %s\n", out_path
);
574 buf
= malloc(length
);
576 fprintf(stderr
, "Couldn't alloc %zu B buffer\n", length
);
581 bytes
= fread(buf
, 1, length
, otrx
->fp
);
582 if (bytes
!= length
) {
583 fprintf(stderr
, "Couldn't read %zu B of data from %s\n", length
, trx_path
);
588 bytes
= fwrite(buf
, 1, length
, out
);
589 if (bytes
!= length
) {
590 fprintf(stderr
, "Couldn't write %zu B to %s\n", length
, out_path
);
595 printf("Extracted 0x%zx bytes into %s\n", length
, out_path
);
605 static int otrx_extract(int argc
, char **argv
) {
606 struct otrx_ctx otrx
= { };
611 fprintf(stderr
, "No TRX file passed\n");
618 otrx_extract_parse_options(argc
, argv
);
620 err
= otrx_open_parse(trx_path
, "r", &otrx
);
622 fprintf(stderr
, "Couldn't open & parse %s: %d\n", trx_path
, err
);
627 for (i
= 0; i
< TRX_MAX_PARTS
; i
++) {
628 struct otrx_part
*part
= &otrx
.parts
[i
];
630 if (!part
->offset
&& partition
[part
->idx
])
631 printf("TRX doesn't contain partition %d, can't extract %s\n", part
->idx
+ 1, partition
[part
->idx
]);
632 if (!part
->offset
|| !partition
[part
->idx
])
633 otrx_skip(otrx
.fp
, part
->length
);
635 otrx_extract_copy(&otrx
, part
->length
, partition
[part
->idx
]);
643 /**************************************************
645 **************************************************/
647 static void usage() {
650 printf("Checking TRX file:\n");
651 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
652 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
654 printf("Creating new TRX file:\n");
655 printf("\totrx create <file> [options] [partitions]\n");
656 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
657 printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n");
658 printf("\t-a alignment\t\t\t[partition] align current partition\n");
659 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
661 printf("Extracting from TRX file:\n");
662 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
663 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
664 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
665 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
666 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
669 int main(int argc
, char **argv
) {
671 if (!strcmp(argv
[1], "check"))
672 return otrx_check(argc
, argv
);
673 else if (!strcmp(argv
[1], "create"))
674 return otrx_create(argc
, argv
);
675 else if (!strcmp(argv
[1], "extract"))
676 return otrx_extract(argc
, argv
);