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(FILE *trx
, size_t offset
, 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 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
);
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
);
596 printf("Extracted 0x%zx bytes into %s\n", length
, out_path
);
606 static int otrx_extract(int argc
, char **argv
) {
607 struct otrx_ctx otrx
= { };
612 fprintf(stderr
, "No TRX file passed\n");
619 otrx_extract_parse_options(argc
, argv
);
621 err
= otrx_open_parse(trx_path
, "r", &otrx
);
623 fprintf(stderr
, "Couldn't open & parse %s: %d\n", trx_path
, err
);
628 for (i
= 0; i
< TRX_MAX_PARTS
; i
++) {
629 struct otrx_part
*part
= &otrx
.parts
[i
];
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
])
636 otrx_extract_copy(otrx
.fp
, trx_offset
+ part
->offset
, part
->length
, partition
[part
->idx
]);
644 /**************************************************
646 **************************************************/
648 static void usage() {
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");
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");
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");
670 int main(int argc
, char **argv
) {
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
);