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