otrx: change command line API to start with a mode
[librecmc/librecmc.git] / package / utils / otrx / src / otrx.c
1 /*
2  * otrx
3  *
4  * Copyright (C) 2015 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 <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x)  bswap_32(x)
22 #define le32_to_cpu(x)  bswap_32(x)
23 #elif __BYTE_ORDER == __LITTLE_ENDIAN
24 #define cpu_to_le32(x)  (x)
25 #define le32_to_cpu(x)  (x)
26 #else
27 #error "Unsupported endianness"
28 #endif
29
30 #define TRX_MAGIC                       0x30524448
31 #define TRX_FLAGS_OFFSET                12
32 #define TRX_MAX_PARTS                   3
33
34 struct trx_header {
35         uint32_t magic;
36         uint32_t length;
37         uint32_t crc32;
38         uint16_t flags;
39         uint16_t version;
40         uint32_t offset[3];
41 };
42
43 char *trx_path;
44 size_t trx_offset = 0;
45 char *partition[TRX_MAX_PARTS] = {};
46
47 /**************************************************
48  * CRC32
49  **************************************************/
50
51 uint32_t otrx_crc32(uint8_t *buf, size_t len) {
52         static const uint32_t t[] = {
53                 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
54                 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
55                 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
56                 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
57                 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
58                 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
59                 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
60                 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
61                 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
62                 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
63                 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
64                 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
65                 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
66                 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
67                 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
68                 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
69                 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
70                 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
71                 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
72                 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
73                 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
74                 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
75                 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
76                 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
77                 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
78                 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
79                 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
80                 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
81                 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
82                 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
83                 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
84                 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
85                 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
86                 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
87                 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
88                 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
89                 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
90                 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
91                 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
92                 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
93                 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
94                 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
95                 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
96                 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
97                 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
98                 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
99                 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
100                 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
101                 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
102                 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
103                 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
104                 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
105                 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
106                 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
107                 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
108                 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
109                 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
110                 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
111                 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
112                 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
113                 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
114                 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
115                 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
116                 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
117         };
118         uint32_t crc = 0xffffffff;
119
120         while (len) {
121                 crc = t[(crc ^ *buf) & 0xff] ^ (crc >> 8);
122                 buf++;
123                 len--;
124         }
125
126         return crc;
127 }
128
129 /**************************************************
130  * Check
131  **************************************************/
132
133 static void otrx_check_parse_options(int argc, char **argv) {
134         int c;
135
136         while ((c = getopt(argc, argv, "o:")) != -1) {
137                 switch (c) {
138                 case 'o':
139                         trx_offset = atoi(optarg);
140                         break;
141                 }
142         }
143 }
144
145 static int otrx_check(int argc, char **argv) {
146         FILE *trx;
147         struct trx_header hdr;
148         size_t bytes, length;
149         uint8_t *buf;
150         uint32_t crc32;
151         int err = 0;
152
153         if (argc < 3) {
154                 fprintf(stderr, "No TRX file passed\n");
155                 err = -EINVAL;
156                 goto out;
157         }
158         trx_path = argv[2];
159
160         optind = 3;
161         otrx_check_parse_options(argc, argv);
162
163         trx = fopen(trx_path, "r");
164         if (!trx) {
165                 fprintf(stderr, "Couldn't open %s\n", trx_path);
166                 err = -EACCES;
167                 goto out;
168         }
169
170         fseek(trx, trx_offset, SEEK_SET);
171         bytes = fread(&hdr, 1, sizeof(hdr), trx);
172         if (bytes != sizeof(hdr)) {
173                 fprintf(stderr, "Couldn't read %s header\n", trx_path);
174                 err =  -EIO;
175                 goto err_close;
176         }
177
178         if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
179                 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
180                 err =  -EINVAL;
181                 goto err_close;
182         }
183
184         length = le32_to_cpu(hdr.length);
185         if (length < sizeof(hdr)) {
186                 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
187                 err = -EINVAL;
188                 goto err_close;
189         }
190
191         buf = malloc(length);
192         if (!buf) {
193                 fprintf(stderr, "Couldn't alloc %zd B buffer\n", length);
194                 err =  -ENOMEM;
195                 goto err_close;
196         }
197
198         fseek(trx, trx_offset, SEEK_SET);
199         bytes = fread(buf, 1, length, trx);
200         if (bytes != length) {
201                 fprintf(stderr, "Couldn't read %zd B of data from %s\n", length, trx_path);
202                 err =  -ENOMEM;
203                 goto err_free_buf;
204         }
205
206         crc32 = otrx_crc32(buf + TRX_FLAGS_OFFSET, length - TRX_FLAGS_OFFSET);
207         if (crc32 != le32_to_cpu(hdr.crc32)) {
208                 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32));
209                 err =  -EINVAL;
210                 goto err_free_buf;
211         }
212
213         printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version));
214
215 err_free_buf:
216         free(buf);
217 err_close:
218         fclose(trx);
219 out:
220         return err;
221 }
222
223 /**************************************************
224  * Extract
225  **************************************************/
226
227 static void otrx_extract_parse_options(int argc, char **argv) {
228         int c;
229
230         while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
231                 switch (c) {
232                 case 'o':
233                         trx_offset = atoi(optarg);
234                         break;
235                 case '1':
236                         partition[0] = optarg;
237                         break;
238                 case '2':
239                         partition[1] = optarg;
240                         break;
241                 case '3':
242                         partition[2] = optarg;
243                         break;
244                 }
245         }
246 }
247
248 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
249         FILE *out;
250         size_t bytes;
251         uint8_t *buf;
252         int err = 0;
253
254         out = fopen(out_path, "w");
255         if (!out) {
256                 fprintf(stderr, "Couldn't open %s\n", out_path);
257                 err = -EACCES;
258                 goto out;
259         }
260
261         buf = malloc(length);
262         if (!buf) {
263                 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
264                 err =  -ENOMEM;
265                 goto err_close;
266         }
267
268         fseek(trx, offset, SEEK_SET);
269         bytes = fread(buf, 1, length, trx);
270         if (bytes != length) {
271                 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
272                 err =  -ENOMEM;
273                 goto err_free_buf;
274         };
275
276         bytes = fwrite(buf, 1, length, out);
277         if (bytes != length) {
278                 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
279                 err =  -ENOMEM;
280                 goto err_free_buf;
281         }
282
283         printf("Extracted 0x%zx bytes into %s\n", length, out_path);
284
285 err_free_buf:
286         free(buf);
287 err_close:
288         fclose(out);
289 out:
290         return err;
291 }
292
293 static int otrx_extract(int argc, char **argv) {
294         FILE *trx;
295         struct trx_header hdr;
296         size_t bytes;
297         int i;
298         int err = 0;
299
300         if (argc < 3) {
301                 fprintf(stderr, "No TRX file passed\n");
302                 err = -EINVAL;
303                 goto out;
304         }
305         trx_path = argv[2];
306
307         optind = 3;
308         otrx_extract_parse_options(argc, argv);
309
310         trx = fopen(trx_path, "r");
311         if (!trx) {
312                 fprintf(stderr, "Couldn't open %s\n", trx_path);
313                 err = -EACCES;
314                 goto out;
315         }
316
317         fseek(trx, trx_offset, SEEK_SET);
318         bytes = fread(&hdr, 1, sizeof(hdr), trx);
319         if (bytes != sizeof(hdr)) {
320                 fprintf(stderr, "Couldn't read %s header\n", trx_path);
321                 err =  -EIO;
322                 goto err_close;
323         }
324
325         if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
326                 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
327                 err =  -EINVAL;
328                 goto err_close;
329         }
330
331         for (i = 0; i < TRX_MAX_PARTS; i++) {
332                 size_t length;
333
334                 if (!partition[i])
335                         continue;
336                 if (!hdr.offset[i]) {
337                         printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
338                         continue;
339                 }
340
341                 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
342                         length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
343                 else
344                         length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
345
346                 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
347         }
348
349 err_close:
350         fclose(trx);
351 out:
352         return err;
353 }
354
355 /**************************************************
356  * Start
357  **************************************************/
358
359 static void usage() {
360         printf("Usage:\n");
361         printf("\n");
362         printf("Checking TRX file:\n");
363         printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
364         printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
365         printf("\n");
366         printf("Extracting from TRX file:\n");
367         printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
368         printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
369         printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
370         printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
371         printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
372 }
373
374 int main(int argc, char **argv) {
375         if (argc > 1) {
376                 if (!strcmp(argv[1], "check"))
377                         return otrx_check(argc, argv);
378                 else if (!strcmp(argv[1], "extract"))
379                         return otrx_extract(argc, argv);
380         }
381
382         usage();
383         return 0;
384 }