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