1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 #define TITLE "cdpatch - CD-XA image insert/extract utility"
4 #define COPYR "Copyright (C) 2001,2011 Neill Corlett"
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include "common.h"
22 #include "banner.h"
23 
24 ////////////////////////////////////////////////////////////////////////////////
25 
26 static const uint32_t max_path_depth = 256;
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 //
30 // Program options
31 //
32 struct cdpatch_options {
33     int8_t insert;
34     int8_t extract;
35     const char* binname;
36     const char* basedir;
37     int8_t big;
38     int8_t little;
39     int8_t boot;
40     int8_t enforce_fscheck;
41     int8_t overwrite;
42     int8_t verbose;
43     int8_t recurse;
44     const char** files;
45     int files_count;
46 };
47 
48 static const char* fsoverride = " (use -f to override)";
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 
exists(const char * filename)52 static int exists(const char* filename) {
53     FILE* f = fopen(filename, "rb");
54     if(f) { fclose(f); return 1; }
55     return 0;
56 }
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 
60 static const char* oom = "Error: Out of memory\n";
61 
62 ////////////////////////////////////////////////////////////////////////////////
63 
get32lsb(const uint8_t * p)64 static uint32_t get32lsb(const uint8_t* p) {
65     return
66         (((uint32_t)(p[0])) <<  0) |
67         (((uint32_t)(p[1])) <<  8) |
68         (((uint32_t)(p[2])) << 16) |
69         (((uint32_t)(p[3])) << 24);
70 }
71 
get32msb(const uint8_t * p)72 static uint32_t get32msb(const uint8_t* p) {
73     return
74         (((uint32_t)(p[0])) << 24) |
75         (((uint32_t)(p[1])) << 16) |
76         (((uint32_t)(p[2])) <<  8) |
77         (((uint32_t)(p[3])) <<  0);
78 }
79 
set32lsb(uint8_t * p,uint32_t value)80 static void set32lsb(uint8_t* p, uint32_t value) {
81     p[0] = (uint8_t)(value >>  0);
82     p[1] = (uint8_t)(value >>  8);
83     p[2] = (uint8_t)(value >> 16);
84     p[3] = (uint8_t)(value >> 24);
85 }
86 
set32msb(uint8_t * p,uint32_t value)87 static void set32msb(uint8_t* p, uint32_t value) {
88     p[0] = (uint8_t)(value >> 24);
89     p[1] = (uint8_t)(value >> 16);
90     p[2] = (uint8_t)(value >>  8);
91     p[3] = (uint8_t)(value >>  0);
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 //
96 // Returns nonzero if any bytes in the array are nonzero
97 //
anynonzero(const uint8_t * data,size_t len)98 static int anynonzero(const uint8_t* data, size_t len) {
99     for(; len; len--) {
100         if(*data++) { return 1; }
101     }
102     return 0;
103 }
104 
105 ////////////////////////////////////////////////////////////////////////////////
106 //
107 // Convert ISO9660 file size to sector count, rounding up
108 //
sectorcount(uint32_t size)109 static uint32_t sectorcount(uint32_t size) {
110     return (size >> 11) + ((size & 0x7FF) != 0);
111 }
112 
113 ////////////////////////////////////////////////////////////////////////////////
114 //
115 // LUTs for computing ECC/EDC
116 //
117 static uint8_t  ecc_f_lut[256];
118 static uint8_t  ecc_b_lut[256];
119 static uint32_t edc_lut  [256];
120 
eccedc_init(void)121 static void eccedc_init(void) {
122     uint32_t i, j, edc;
123     for(i = 0; i < 256; i++) {
124         j = (i << 1) ^ (i & 0x80 ? 0x11D : 0);
125         ecc_f_lut[i    ] = (uint8_t)j;
126         ecc_b_lut[i ^ j] = (uint8_t)i;
127         edc = i;
128         for(j = 0; j < 8; j++) {
129             edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0);
130         }
131         edc_lut[i] = edc;
132     }
133 }
134 
135 ////////////////////////////////////////////////////////////////////////////////
136 //
137 // Compute EDC for a block
138 //
edc_computeblock(const uint8_t * src,size_t size,uint8_t * dest)139 static void edc_computeblock(const uint8_t* src, size_t size, uint8_t* dest) {
140     uint32_t edc = 0;
141     while(size--) {
142         edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF];
143     }
144     set32lsb(dest, edc);
145 }
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 //
149 // Compute ECC for a block (can do either P or Q)
150 //
ecc_computeblock(uint8_t * src,uint32_t major_count,uint32_t minor_count,uint32_t major_mult,uint32_t minor_inc,uint8_t * dest)151 static void ecc_computeblock(
152     uint8_t* src,
153     uint32_t major_count,
154     uint32_t minor_count,
155     uint32_t major_mult,
156     uint32_t minor_inc,
157     uint8_t* dest
158 ) {
159     uint32_t size = major_count * minor_count;
160     uint32_t major, minor;
161     for(major = 0; major < major_count; major++) {
162         uint32_t index = (major >> 1) * major_mult + (major & 1);
163         uint8_t ecc_a = 0;
164         uint8_t ecc_b = 0;
165         for(minor = 0; minor < minor_count; minor++) {
166             uint8_t temp = src[index];
167             index += minor_inc;
168             if(index >= size) index -= size;
169             ecc_a ^= temp;
170             ecc_b ^= temp;
171             ecc_a = ecc_f_lut[ecc_a];
172         }
173         ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b];
174         dest[major              ] = ecc_a;
175         dest[major + major_count] = ecc_a ^ ecc_b;
176     }
177 }
178 
179 //
180 // Generate ECC P and Q codes for a block
181 //
ecc_generate(uint8_t * sector,int zeroaddress)182 static void ecc_generate(uint8_t* sector, int zeroaddress) {
183     uint8_t saved_address[4];
184     //
185     // Save the address and zero it out, if necessary
186     //
187     if(zeroaddress) {
188         memmove(saved_address, sector + 12, 4);
189         memset(sector + 12, 0, 4);
190     }
191     //
192     // Compute ECC P code
193     //
194     ecc_computeblock(sector + 0xC, 86, 24,  2, 86, sector + 0x81C);
195     //
196     // Compute ECC Q code
197     //
198     ecc_computeblock(sector + 0xC, 52, 43, 86, 88, sector + 0x8C8);
199     //
200     // Restore the address, if necessary
201     //
202     if(zeroaddress) {
203         memmove(sector + 12, saved_address, 4);
204     }
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 //
209 // CD sync header
210 //
211 static const uint8_t sync_header[12] = {
212     0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00
213 };
214 
215 ////////////////////////////////////////////////////////////////////////////////
216 //
217 // Generate ECC/EDC information for a sector (must be 2352 = 0x930 bytes)
218 //
eccedc_generate(uint8_t * sector)219 static void eccedc_generate(uint8_t* sector) {
220     //
221     // Generate sync
222     //
223     memmove(sector, sync_header, sizeof(sync_header));
224     switch(sector[0x0F]) {
225     case 0x00:
226         //
227         // Mode 0: no data; generate zeroes
228         //
229         memset(sector + 0x10, 0, 0x920);
230         break;
231 
232     case 0x01:
233         //
234         // Mode 1:
235         //
236         // Compute EDC
237         //
238         edc_computeblock(sector + 0x00, 0x810, sector + 0x810);
239         //
240         // Zero out reserved area
241         //
242         memset(sector + 0x814, 0, 8);
243         //
244         // Generate ECC P/Q codes
245         //
246         ecc_generate(sector, 0);
247         break;
248 
249     case 0x02:
250         //
251         // Mode 2:
252         //
253         // Make sure XA flags match
254         //
255         memmove(sector + 0x14, sector + 0x10, 4);
256 
257         if(!(sector[0x12] & 0x20)) {
258             //
259             // Form 1: Compute EDC
260             //
261             edc_computeblock(sector + 0x10, 0x808, sector + 0x818);
262             //
263             // Generate ECC P/Q codes
264             //
265             ecc_generate(sector, 1);
266 
267         } else {
268             //
269             // Form 2: Compute EDC
270             //
271             edc_computeblock(sector + 0x10, 0x91C, sector + 0x92C);
272         }
273         break;
274     }
275 }
276 
277 ////////////////////////////////////////////////////////////////////////////////
278 //
279 // Verify EDC for a sector (must be 2352 = 0x930 bytes)
280 // Returns 0 on success
281 //
edc_verify(const uint8_t * sector)282 static int edc_verify(const uint8_t* sector) {
283     uint8_t myedc[4];
284     //
285     // Verify sync
286     //
287     if(memcmp(sector, sync_header, sizeof(sync_header))) { return 1; }
288 
289     switch(sector[0x0F]) {
290     case 0x00:
291         //
292         // Mode 0: no data; everything had better be zero
293         //
294         return anynonzero(sector + 0x10, 0x920);
295 
296     case 0x01:
297         //
298         // Mode 1
299         //
300         edc_computeblock(sector + 0x00, 0x810, myedc);
301         return memcmp(myedc, sector + 0x810, 4);
302 
303     case 0x02:
304         //
305         // Mode 2: Verify that the XA type is correctly copied twice
306         //
307         if(memcmp(sector + 0x10, sector + 0x14, 4)) { return 1; }
308 
309         if(!(sector[0x12] & 0x20)) {
310             //
311             // Form 1
312             //
313             edc_computeblock(sector + 0x10, 0x808, myedc);
314             return memcmp(myedc, sector + 0x818, 4);
315 
316         } else {
317             //
318             // Form 2
319             //
320             edc_computeblock(sector + 0x10, 0x91C, myedc);
321             return memcmp(myedc, sector + 0x92C, 4);
322         }
323     }
324     //
325     // Invalid mode
326     //
327     return 1;
328 }
329 
330 ////////////////////////////////////////////////////////////////////////////////
331 
332 struct cacheentry {
333     uint8_t* data;
334     uint32_t sector;
335     uint8_t valid;
336 };
337 
338 enum { CACHE_ENTRIES = 4 };
339 
340 enum {
341     BINTYPE_UNKNOWN = 0,
342     BINTYPE_2048    = 1,
343     BINTYPE_2352    = 2
344 };
345 
346 struct binfile {
347     FILE* f;
348     const char* name;
349     int type;
350     uint32_t sectors;
351     struct cacheentry cache[CACHE_ENTRIES];
352 };
353 
bin_quit(struct binfile * bin)354 static void bin_quit(struct binfile* bin) {
355     size_t i;
356     if(bin->f) { fclose(bin->f); }
357     for(i = 0; i < CACHE_ENTRIES; i++) {
358         if(bin->cache[i].data) { free(bin->cache[i].data); }
359     }
360 }
361 
bin_init(struct binfile * bin)362 static int bin_init(struct binfile* bin) {
363     size_t i;
364     memset(bin, 0, sizeof(struct binfile));
365     for(i = 0; i < CACHE_ENTRIES; i++) {
366         bin->cache[i].data = malloc(2352);
367         if(!bin->cache[i].data) {
368             printf("%s", oom);
369             bin_quit(bin);
370             return 1;
371         }
372     }
373     return 0;
374 }
375 
cache_mtf(struct binfile * bin,size_t entry)376 static void cache_mtf(struct binfile* bin, size_t entry) {
377     if(entry) {
378         struct cacheentry tmp = bin->cache[entry];
379         memmove(bin->cache + 1, bin->cache, sizeof(struct cacheentry) * entry);
380         bin->cache[0] = tmp;
381     }
382 }
383 
cache_find(struct binfile * bin,uint32_t sector)384 static uint8_t* cache_find(struct binfile* bin, uint32_t sector) {
385     size_t i;
386     for(i = 0; i < CACHE_ENTRIES; i++) {
387         if((bin->cache[i].sector == sector) && bin->cache[i].valid) {
388             cache_mtf(bin, i);
389             return bin->cache[0].data;
390         }
391     }
392     return NULL;
393 }
394 
cache_allocbegin(struct binfile * bin,uint32_t sector)395 static uint8_t* cache_allocbegin(struct binfile* bin, uint32_t sector) {
396     bin->cache[CACHE_ENTRIES - 1].valid = 0;
397     bin->cache[CACHE_ENTRIES - 1].sector = sector;
398     return bin->cache[CACHE_ENTRIES - 1].data;
399 }
400 
cache_allocend(struct binfile * bin)401 static void cache_allocend(struct binfile* bin) {
402     cache_mtf(bin, CACHE_ENTRIES - 1);
403     bin->cache[0].valid = 1;
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 //
408 // Detect whether image is ISO or BIN
409 // Returns nonzero on error
410 //
bintype_detect(struct binfile * bin)411 static int bintype_detect(struct binfile* bin) {
412     uint8_t* sector = NULL;
413     off_t size;
414 
415     bin->type = BINTYPE_UNKNOWN;
416 
417     if(fseeko(bin->f, 0, SEEK_END) != 0) { goto error_bin; }
418     size = ftello(bin->f);
419     if(size == -1) { goto error_bin; }
420     if(fseeko(bin->f, 0, SEEK_SET) != 0) { goto error_bin; }
421 
422     if(size <= 0 || ((size % 2352) != 0 && (size % 2048) != 0)) {
423         //
424         // Size is zero or not cleanly divisible
425         //
426         printf("Error: %s: Unable to determine BIN or ISO format based on size\n",
427             bin->name
428         );
429         goto error;
430 
431     } else if((size % 2352) != 0) {
432         //
433         // If indivisible by 2352, assume ISO
434         //
435         bin->type = BINTYPE_2048;
436 
437     } else if((size % 2048) != 0) {
438         //
439         // If indivisible by 2048, assume BIN
440         //
441         bin->type = BINTYPE_2352;
442 
443     } else {
444         //
445         // If divisible by both, read the first 2352 bytes and see if it's a
446         // valid raw sector.  If so, assume BIN.
447         //
448         sector = malloc(2352);
449         if(!sector) { goto error_mem; }
450 
451         if(fread(sector, 1, 2352, bin->f) != 2352) { goto error_bin; }
452         bin->type = edc_verify(sector) ? BINTYPE_2048 : BINTYPE_2352;
453     }
454 
455     //
456     // Figure out the number of sectors
457     //
458     {   off_t sectorsize = (bin->type == BINTYPE_2048) ? 2048 : 2352;
459         bin->sectors =
460             (sizeof(off_t) > 4) ? (
461                 ((((off_t)(size / sectorsize)) >> 31) > 1) ?
462                     ((uint32_t)(0xFFFFFFFFLU)) :
463                     ((uint32_t)(size / sectorsize))
464             ) : (((uint32_t)size) / ((uint32_t)sectorsize));
465     }
466 
467     goto done;
468 
469 error_mem:
470     printf("%s", oom);
471     goto error;
472 error_bin:
473     printfileerror(bin->f, bin->name);
474     goto error;
475 error:
476     bin->type = BINTYPE_UNKNOWN;
477     goto done;
478 done:
479     if(sector) { free(sector); }
480     return (bin->type == BINTYPE_UNKNOWN);
481 }
482 
483 ////////////////////////////////////////////////////////////////////////////////
484 //
485 // Ensure that the sector number is within the seekable range for the bin file;
486 // returns nonzero on error
487 //
check_bin_sector_range(const struct binfile * bin,uint32_t sector)488 int check_bin_sector_range(const struct binfile* bin, uint32_t sector) {
489     if(sector >= bin->sectors) {
490         printf("Error: Sector %lu is out of range\n", (unsigned long)sector);
491         return 1;
492     }
493     return 0;
494 }
495 
496 ////////////////////////////////////////////////////////////////////////////////
497 //
498 // Returns NULL on failure
499 // The returned buffer is valid until the next call
500 //
read_raw_sector(struct binfile * bin,uint32_t sector)501 static uint8_t* read_raw_sector(struct binfile* bin, uint32_t sector) {
502     uint8_t* data = NULL;
503 
504     if(check_bin_sector_range(bin, sector)) { goto error; }
505 
506     if(bin->type != BINTYPE_2352) {
507         printf("Error: Tried to read raw sector from ISO\n");
508         goto error;
509     }
510 
511     data = cache_find(bin, sector);
512     if(!data) {
513         data = cache_allocbegin(bin, sector);
514         if(fseeko(bin->f, 2352 * ((off_t)sector), SEEK_SET) != 0) {
515             goto error_f;
516         }
517         if(fread(data, 1, 2352, bin->f) != 2352) { goto error_f; }
518         cache_allocend(bin);
519     }
520     return data;
521 
522 error_f:
523     printf("At sector %lu: ", (unsigned long)sector);
524     printfileerror(bin->f, bin->name);
525     goto error;
526 error:
527     return NULL;
528 }
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 //
532 // Returns NULL on failure
533 // The returned buffer is valid until the next call
534 //
read_cooked_sector(struct binfile * bin,uint32_t sector,const struct cdpatch_options * opt)535 static uint8_t* read_cooked_sector(
536     struct binfile* bin,
537     uint32_t sector,
538     const struct cdpatch_options* opt
539 ) {
540     uint8_t* data = NULL;
541 
542     if(check_bin_sector_range(bin, sector)) { goto error; }
543 
544     data = cache_find(bin, sector);
545     if(!data) {
546         data = cache_allocbegin(bin, sector);
547         if(bin->type == BINTYPE_2048) {
548             if(fseeko(bin->f, 2048 * ((off_t)sector), SEEK_SET) != 0) {
549                 goto error_f;
550             }
551             if(fread(data, 1, 2048, bin->f) != 2048) { goto error_f; }
552         } else {
553             if(fseeko(bin->f, 2352 * ((off_t)sector), SEEK_SET) != 0) {
554                 goto error_f;
555             }
556             if(fread(data, 1, 2352, bin->f) != 2352) { goto error_f; }
557             //
558             // Verify the EDC
559             //
560             if(edc_verify(data)) {
561                 if(opt->enforce_fscheck || opt->verbose) {
562                     printf("%s: CD sector %lu is corrupt%s\n",
563                         opt->enforce_fscheck ? "Error" : "Warning",
564                         (unsigned long)sector,
565                         opt->enforce_fscheck ? fsoverride : ""
566                     );
567                 }
568                 if(opt->enforce_fscheck) { goto error; }
569             }
570         }
571         cache_allocend(bin);
572     }
573 
574     if(bin->type == BINTYPE_2352) {
575         //
576         // Figure out where the data actually resides in the sector
577         //
578         switch(data[0xF]) {
579         case 1:
580             data += 0x10;
581             break;
582         case 2:
583             if(data[0x12] & 0x20) {
584                 if(opt->enforce_fscheck || opt->verbose) {
585                     printf("%s: Attempted to read Form 2 sector %lu%s\n",
586                         opt->enforce_fscheck ? "Error" : "Warning",
587                         (unsigned long)sector,
588                         opt->enforce_fscheck ? fsoverride : ""
589                     );
590                 }
591                 if(opt->enforce_fscheck) { goto error; }
592             }
593             data += 0x18;
594             break;
595         default:
596             if(opt->enforce_fscheck || opt->verbose) {
597                 printf("%s: Invalid mode 0x%02X at sector %lu%s\n",
598                     opt->enforce_fscheck ? "Error" : "Warning",
599                     (int)data[0xF], (unsigned long)sector,
600                     opt->enforce_fscheck ? fsoverride : ""
601                 );
602                 if(opt->enforce_fscheck) { goto error; }
603             }
604             data += 0x10; // assume mode 1
605             break;
606         }
607     }
608 
609     return data;
610 
611 error_f:
612     printf("At sector %lu: ", (unsigned long)sector);
613     printfileerror(bin->f, bin->name);
614     goto error;
615 error:
616     return NULL;
617 }
618 
619 ////////////////////////////////////////////////////////////////////////////////
620 //
621 // Allocate space for a cooked sector, with the understanding that we will
622 // overwrite all of it
623 //
624 // Returns NULL on failure
625 // The returned buffer is valid until the next call
626 //
alloc_cooked_sector(struct binfile * bin,uint32_t sector,const struct cdpatch_options * opt)627 static uint8_t* alloc_cooked_sector(
628     struct binfile* bin,
629     uint32_t sector,
630     const struct cdpatch_options* opt
631 ) {
632     if(bin->type == BINTYPE_2048) {
633         uint8_t* data;
634         if(check_bin_sector_range(bin, sector)) { return NULL; }
635         //
636         // Just allocate space and not initialize it
637         //
638         data = cache_find(bin, sector);
639         if(!data) {
640             data = cache_allocbegin(bin, sector);
641             cache_allocend(bin);
642         }
643         return data;
644     } else {
645         //
646         // We need to read the sector anyway, so just read it
647         //
648         return read_cooked_sector(bin, sector, opt);
649     }
650 }
651 
652 ////////////////////////////////////////////////////////////////////////////////
653 //
654 // Returns 0 on success
655 //
writeback_raw_sector(struct binfile * bin,uint32_t sector)656 static int writeback_raw_sector(struct binfile* bin, uint32_t sector) {
657     int returncode = 0;
658     const uint8_t* data;
659 
660     if(check_bin_sector_range(bin, sector)) { goto error; }
661 
662     if(bin->type != BINTYPE_2352) {
663         printf("Error: Tried to write raw sector to ISO\n");
664         goto error;
665     }
666 
667     data = cache_find(bin, sector);
668     if(!data) {
669         printf("Error: Sector not in cache\n");
670         goto error;
671     }
672 
673     //
674     // Seek and write
675     //
676     if(fseeko(bin->f, 2352 * ((off_t)sector), SEEK_SET) != 0) { goto error_f; }
677     if(fwrite(data, 1, 2352, bin->f) != 2352) { goto error_f; }
678     fflush(bin->f);
679 
680     goto done;
681 
682 error_f:
683     printf("At sector %lu: ", (unsigned long)sector);
684     printfileerror(bin->f, bin->name);
685     goto error;
686 error:
687     returncode = 1;
688     goto done;
689 done:
690     return returncode;
691 }
692 
693 ////////////////////////////////////////////////////////////////////////////////
694 //
695 // Returns 0 on success
696 //
writeback_cooked_sector(struct binfile * bin,uint32_t sector,int redoflags,int last)697 static int writeback_cooked_sector(
698     struct binfile* bin,
699     uint32_t sector,
700     int redoflags,
701     int last
702 ) {
703     int returncode = 0;
704     uint8_t* data;
705 
706     if(check_bin_sector_range(bin, sector)) { goto error; }
707 
708     data = cache_find(bin, sector);
709     if(!data) {
710         printf("Error: Sector not in cache\n");
711         goto error;
712     }
713 
714     if(bin->type == BINTYPE_2048) {
715         //
716         // If this is an ISO file, just seek and write directly
717         //
718         if(fseeko(bin->f, 2048 * ((off_t)sector), SEEK_SET) != 0) { goto error_f; }
719         if(fwrite(data, 1, 2048, bin->f) != 2048) { goto error_f; }
720 
721     } else {
722         //
723         // If mode 2, and we care about the XA flags, set those up
724         //
725         if(redoflags && (data[0xF] == 2)) {
726             data[0x10] = data[0x14] = 0;
727             data[0x11] = data[0x15] = 0;
728             data[0x12] = data[0x16] = 0x08 | (last ? 0x81 : 0x00);
729             data[0x13] = data[0x17] = 0;
730         }
731         //
732         // Regenerate ECC/EDC
733         //
734         eccedc_generate(data);
735         //
736         // Seek and write
737         //
738         if(fseeko(bin->f, 2352 * ((off_t)sector), SEEK_SET) != 0) { goto error_f; }
739         if(fwrite(data, 1, 2352, bin->f) != 2352) { goto error_f; }
740 
741     }
742 
743     fflush(bin->f);
744     goto done;
745 
746 error_f:
747     printf("At sector %lu: ", (unsigned long)sector);
748     printfileerror(bin->f, bin->name);
749     goto error;
750 error:
751     returncode = 1;
752     goto done;
753 done:
754     return returncode;
755 }
756 
757 ////////////////////////////////////////////////////////////////////////////////
758 //
759 // Read or write arbitrary cooked data; returns nonzero on error
760 //
rw_cooked_data(struct binfile * bin,uint32_t sector,uint32_t offset,uint8_t * data,size_t size,const struct cdpatch_options * opt,int write)761 static int rw_cooked_data(
762     struct binfile* bin,
763     uint32_t sector,
764     uint32_t offset,
765     uint8_t* data,
766     size_t size,
767     const struct cdpatch_options* opt,
768     int write
769 ) {
770     int returncode = 0;
771     //
772     // Normalize sector/offset, and verify the range
773     //
774     if((offset >> 11) > (0xFFFFFFFFLU - sector)) { goto error_range; }
775     sector += offset >> 11;
776     offset &= 0x7FF;
777     if(check_bin_sector_range(bin, sector)) { goto error; }
778 
779     if(bin->type == BINTYPE_2048) {
780         //
781         // Read or write the image file directly
782         //
783         if(fseeko(
784             bin->f, 2048 * ((off_t)sector) + ((off_t)offset), SEEK_SET
785         ) != 0) { goto error_f; }
786         if(write) {
787             if(fwrite(data, 1, size, bin->f) != size) { goto error_f; }
788         } else {
789             if(fread (data, 1, size, bin->f) != size) { goto error_f; }
790         }
791     } else {
792         //
793         // Read or write sector-by-sector
794         //
795         for(; size; sector++) {
796             size_t insector = 0x800 - offset;
797             size_t z = size < insector ? size : insector;
798             uint8_t* d = read_cooked_sector(bin, sector, opt);
799             if(!d) { goto error; }
800             if(write) {
801                 memmove(d + offset, data, z);
802                 if(writeback_cooked_sector(bin, sector, 0, 0)) { goto error; }
803             } else {
804                 memmove(data, d + offset, z);
805             }
806             data += z;
807             size -= z;
808             offset = 0;
809             if(size && (sector == 0xFFFFFFFFLU)) { goto error_range; }
810         }
811     }
812     goto done;
813 
814 error_f:
815     printf("At sector %lu: ", (unsigned long)sector);
816     printfileerror(bin->f, bin->name);
817     goto error;
818 error_range:
819     printf("Error: %s out of range: sector=%lu offset=%lu\n",
820         write ? "Write" : "Read", (unsigned long)sector, (unsigned long)offset
821     );
822     goto error;
823 error:
824     returncode = 1;
825 done:
826     return returncode;
827 }
828 
read_cooked_data(struct binfile * bin,uint32_t sector,uint32_t offset,uint8_t * data,size_t size,const struct cdpatch_options * opt)829 static int read_cooked_data(
830     struct binfile* bin,
831     uint32_t sector,
832     uint32_t offset,
833     uint8_t* data,
834     size_t size,
835     const struct cdpatch_options* opt
836 ) {
837     return rw_cooked_data(bin, sector, offset, data, size, opt, 0);
838 }
839 
write_cooked_data(struct binfile * bin,uint32_t sector,uint32_t offset,const uint8_t * data,size_t size,const struct cdpatch_options * opt)840 static int write_cooked_data(
841     struct binfile* bin,
842     uint32_t sector,
843     uint32_t offset,
844     const uint8_t* data,
845     size_t size,
846     const struct cdpatch_options* opt
847 ) {
848     return rw_cooked_data(bin, sector, offset, (uint8_t*)data, size, opt, 1);
849 }
850 
851 ////////////////////////////////////////////////////////////////////////////////
852 //
853 // Use RIFF if:
854 // - The sector is mode 0, or invalid
855 // - The sector is mode 2 and any of the flag bits besides 'data', 'last sector
856 //   of data record', or 'last sector of file' are set.
857 //
should_use_riff_format(const uint8_t * sector)858 static int should_use_riff_format(const uint8_t* sector) {
859     if(sector[0xF] == 1) { return 0; }
860     if(sector[0xF] == 2) {
861         return
862             ( sector[0x10]         != 0) ||
863             ( sector[0x11]         != 0) ||
864             ((sector[0x12] & 0x76) != 0) ||
865             ( sector[0x13]         != 0);
866     }
867     return 1;
868 }
869 
870 ////////////////////////////////////////////////////////////////////////////////
871 //
872 // Returns nonzero on error
873 //
extract_file(struct binfile * bin,uint32_t sector,uint32_t filesize,time_t modtime,const char * filename,const struct cdpatch_options * opt)874 static int extract_file(
875     struct binfile* bin,
876     uint32_t sector,
877     uint32_t filesize,
878     time_t modtime,
879     const char* filename,
880     const struct cdpatch_options* opt
881 ) {
882     int returncode = 0;
883     FILE* f = NULL;
884     uint8_t* data = NULL;
885 
886     if(!opt->overwrite && exists(filename)) {
887         printf("Error: %s already exists (use -o to override)\n", filename);
888         goto error;
889     }
890     f = fopen(filename, "wb");
891     if(!f) { goto error_f; }
892 
893     //
894     // Check for RIFF format
895     //
896     if(bin->type == BINTYPE_2352) {
897         data = read_raw_sector(bin, sector);
898         if(!data) { goto error; }
899     }
900     if(data && should_use_riff_format(data)) {
901         //
902         // Extract in RIFF format
903         //
904         uint32_t sectors = sectorcount(filesize);
905         if(sectors > 1826091LU) {
906             sectors = 1826091LU; // Exceeds uint32_t - just cap it
907         }
908         if(opt->verbose) {
909             printf(
910                 "Extract %7lu %10lu %s (CDXA)\n",
911                 (unsigned long)sector,
912                 (unsigned long)(2352 * sectors + 0x2C),
913                 filename
914             );
915         }
916         //
917         // Write header
918         //
919         {   uint8_t hdr[0x2C];
920             memset(hdr, 0, sizeof(hdr));
921             memmove (hdr + 0x00, "RIFF", 4);
922             set32lsb(hdr + 0x04, 2352 * sectors + 0x2C);
923             memmove (hdr + 0x08, "CDXA", 4);
924             set32lsb(hdr + 0x0C, 2352 * sectors);
925             if(fwrite(hdr, 1, 0x2C, f) != 0x2C) { goto error_f; }
926         }
927         //
928         // Write contents
929         //
930         for(; sectors; sector++, sectors--) {
931             data = read_raw_sector(bin, sector);
932             if(!data) { goto error; }
933             if(edc_verify(data)) {
934                 if(opt->enforce_fscheck || opt->verbose) {
935                     printf("%s: %s: CD sector %lu is corrupt%s\n",
936                         opt->enforce_fscheck ? "Error" : "Warning",
937                         filename, (unsigned long)sector,
938                         opt->enforce_fscheck ? fsoverride : ""
939                     );
940                 }
941                 if(opt->enforce_fscheck) { goto error; }
942             }
943             if(fwrite(data, 1, 2352, f) != 2352) { goto error_f; }
944         }
945     } else {
946         if(opt->verbose) {
947             printf(
948                 "Extract %7lu %10lu %s\n",
949                 (unsigned long)sector,
950                 (unsigned long)filesize,
951                 filename
952             );
953         }
954         //
955         // Extract normally
956         //
957         for(; filesize; sector++) {
958             size_t remain = filesize < 2048 ? filesize : 2048;
959             data = read_cooked_sector(bin, sector, opt);
960             if(!data) { goto error; }
961             if(fwrite(data, 1, remain, f) != remain) { goto error_f; }
962             filesize -= remain;
963         }
964     }
965     if(f) { fclose(f); f = NULL; }
966 
967     //
968     // Set modification time, if it was valid
969     //
970     if(modtime != ((time_t)(-1))) {
971         struct utimbuf b;
972         b.actime  = modtime;
973         b.modtime = modtime;
974         if(utime((char*)filename, &b) != 0) {
975             // Silently fail - preserving modtime shouldn't be considered critical
976         }
977     }
978 
979     goto done;
980 
981 error_f:
982     printfileerror(f, filename);
983     goto error;
984 error:
985     returncode = 1;
986     goto done;
987 done:
988     if(f) { fclose(f); }
989     return returncode;
990 }
991 
992 ////////////////////////////////////////////////////////////////////////////////
993 //
994 // Returns nonzero on error
995 // Outputs the new size in *filesize
996 //
insert_file(struct binfile * bin,uint32_t sector,uint32_t * filesize,const char * filename,const struct cdpatch_options * opt)997 static int insert_file(
998     struct binfile* bin,
999     uint32_t sector,
1000     uint32_t* filesize,
1001     const char* filename,
1002     const struct cdpatch_options* opt
1003 ) {
1004     int returncode = 0;
1005     uint8_t* data = NULL;
1006     FILE* f = NULL;
1007     uint32_t newfilesize;
1008     uint32_t sectors = sectorcount(*filesize);
1009 
1010     f = fopen(filename, "rb");
1011     if(!f) { goto error_f; }
1012 
1013     //
1014     // Get the replacement file size
1015     //
1016     {   off_t size;
1017         if(fseeko(f, 0, SEEK_END) != 0) { goto error_f; }
1018         size = ftello(f);
1019         if(size == -1) { goto error_f; }
1020         if(fseeko(f, 0, SEEK_SET) != 0) { goto error_f; }
1021         // Verify size is not out of uint32_t range
1022         if(sizeof(size) > 4 && (size >> 31) > 1) {
1023             printf("Error: %s: Too large for ISO9660 (> 4GiB)\n", filename);
1024             goto error;
1025         }
1026         newfilesize = (uint32_t)size;
1027     }
1028 
1029     //
1030     // Check for RIFF format
1031     //
1032     if(bin->type == BINTYPE_2352) {
1033         data = read_raw_sector(bin, sector);
1034         if(!data) { goto error; }
1035     }
1036     if(data && should_use_riff_format(data)) {
1037         //
1038         // Insert in RIFF format
1039         //
1040         if(sectors > 1826091LU) {
1041             sectors = 1826091LU; // Exceeds uint32_t - just cap it
1042         }
1043         if(opt->verbose) {
1044             printf(
1045                 "Insert %7lu %10lu %s (CDXA)\n",
1046                 (unsigned long)sector,
1047                 (unsigned long)newfilesize,
1048                 filename
1049             );
1050         }
1051         if(
1052             (newfilesize < 0x2C) ||
1053             ((newfilesize - 0x2C) % 2352) != 0
1054         ) {
1055             printf("Error: %s: CDXA file has invalid size\n", filename);
1056             goto error;
1057         }
1058         //
1059         // Make sure we're not expanding the file, at all
1060         //
1061         if(newfilesize > (2352 * sectors + 0x2C)) {
1062             printf("Error: %s: Cannot expand CDXA data beyond %lu bytes\n",
1063                 filename, (unsigned long)(2352 * sectors)
1064             );
1065             goto error;
1066         }
1067         //
1068         // Read and verify header
1069         //
1070         {   uint8_t hdr[0x2C];
1071             if(fread(hdr, 1, 0x2C, f) != 0x2C) { goto error_f; }
1072             if(
1073                 memcmp(hdr + 0x00, "RIFF", 4) ||
1074                 memcmp(hdr + 0x08, "CDXA", 4) ||
1075                 anynonzero(hdr + 0x10, 0x1C)
1076             ) {
1077                 printf("Error: %s: RIFF header is invalid\n", filename);
1078                 goto error;
1079             }
1080             if(
1081                 get32lsb(hdr + 0x04) != (newfilesize       ) ||
1082                 get32lsb(hdr + 0x0C) != (newfilesize - 0x2C)
1083             ) {
1084                 printf("Error: %s: RIFF header mismatches actual file size\n",
1085                     filename
1086                 );
1087                 goto error;
1088             }
1089         }
1090         //
1091         // Read contents
1092         //
1093         newfilesize -= 0x2C;
1094         *filesize = (newfilesize / 2352) * 2048;
1095         for(; newfilesize >= 2352; sector++) {
1096             data = read_raw_sector(bin, sector);
1097             if(!data) { goto error; }
1098 
1099             // Ignore sync and address
1100             if(fread(data + 0x00F, 1, 0x00F, f) != 0x00F) { goto error_f; }
1101             // Read mode and everything else
1102             if(fread(data + 0x00F, 1, 0x921, f) != 0x921) { goto error_f; }
1103             // Regenerate sync and ECC/EDC
1104             memmove(data, sync_header, sizeof(sync_header));
1105             eccedc_generate(data);
1106 
1107             if(writeback_raw_sector(bin, sector)) { goto error; }
1108             newfilesize -= 2352;
1109         }
1110 
1111     } else {
1112         //
1113         // Make sure we're not expanding the file too much
1114         //
1115         uint32_t bytelimit = (sectors >= 0x200000LU) ?
1116             ((uint32_t)(0xFFFFFFFFLU)) :
1117             ((uint32_t)(sectors << 11));
1118         if(newfilesize > bytelimit) {
1119             printf("Error: %s: Cannot expand file beyond %lu bytes\n",
1120                 filename, (unsigned long)bytelimit
1121             );
1122             goto error;
1123         }
1124         if(opt->verbose) {
1125             printf(
1126                 "Insert %7lu %10lu %s\n",
1127                 (unsigned long)sector,
1128                 (unsigned long)newfilesize,
1129                 filename
1130             );
1131         }
1132         //
1133         // Insert normally
1134         //
1135         *filesize = newfilesize;
1136         for(; newfilesize; sector++) {
1137             size_t remain = newfilesize < 2048 ? newfilesize : 2048;
1138 
1139             data = alloc_cooked_sector(bin, sector, opt);
1140             if(!data) { goto error; }
1141 
1142             if(fread(data, 1, remain, f) != remain) { goto error_f; }
1143             if(remain < 2048) { memset(data + remain, 0, 2048 - remain); }
1144 
1145             if(writeback_cooked_sector(bin, sector, 1, newfilesize <= 2048)) {
1146                 goto error;
1147             }
1148             newfilesize -= remain;
1149         }
1150     }
1151 
1152     goto done;
1153 
1154 error_f:
1155     printfileerror(f, filename);
1156     goto error;
1157 error:
1158     returncode = 1;
1159     goto done;
1160 done:
1161     if(f) { fclose(f); }
1162     return returncode;
1163 }
1164 
1165 ////////////////////////////////////////////////////////////////////////////////
1166 //
1167 // ISO9660 data structure offsets
1168 //
1169 enum {
1170     PD_type                    =    0,
1171     PD_id                      =    1,
1172     PD_version                 =    6,
1173     PD_unused1                 =    7,
1174     PD_system_id               =    8,
1175     PD_volume_id               =   40,
1176     PD_unused2                 =   72,
1177     PD_volume_space_size       =   80,
1178     PD_unused3                 =   88,
1179     PD_volume_set_size         =  120,
1180     PD_volume_sequence_number  =  124,
1181     PD_logical_block_size      =  128,
1182     PD_path_table_size         =  132,
1183     PD_type_l_path_table       =  140,
1184     PD_opt_type_l_path_table   =  144,
1185     PD_type_m_path_table       =  148,
1186     PD_opt_type_m_path_table   =  152,
1187     PD_root_dir_record         =  156,
1188     PD_volume_set_id           =  190,
1189     PD_publisher_id            =  318,
1190     PD_preparer_id             =  446,
1191     PD_application_id          =  574,
1192     PD_copyright_file_id       =  702,
1193     PD_abstract_file_id        =  739,
1194     PD_bibliographic_file_id   =  776,
1195     PD_creation_date           =  813,
1196     PD_modification_date       =  830,
1197     PD_expiration_date         =  847,
1198     PD_effective_date          =  864,
1199     PD_file_structure_version  =  881,
1200     PD_unused4                 =  882,
1201     PD_application_data        =  883,
1202     PD_unused5                 = 1395
1203 };
1204 
1205 enum {
1206     DR_length                  =    0,
1207     DR_ext_attr_length         =    1,
1208     DR_extent                  =    2,
1209     DR_size                    =   10,
1210     DR_date                    =   18,
1211     DR_flags                   =   25,
1212     DR_file_unit_size          =   26,
1213     DR_interleave              =   27,
1214     DR_volume_sequence_number  =   28,
1215     DR_name_len                =   32,
1216     DR_name                    =   33
1217 };
1218 
1219 ////////////////////////////////////////////////////////////////////////////////
1220 
printsafestring(const uint8_t * src,size_t size)1221 static void printsafestring(const uint8_t* src, size_t size) {
1222     size_t i;
1223     for(i = 0; i < size; i++) {
1224         int c = src[i];
1225         if(!c) { break; }
1226         if(isprint(c)) {
1227             fputc(c, stdout);
1228         }
1229     }
1230 }
1231 
1232 ////////////////////////////////////////////////////////////////////////////////
1233 //
1234 // Node: Info about a ISO9660 directory or file
1235 //
1236 struct node {
1237     //
1238     // Location of the ISO DR representing this node
1239     //
1240     uint32_t dr_sector;
1241     uint32_t dr_ofs;
1242     //
1243     // Info copied from the ISO DR
1244     //
1245     uint8_t name[256];
1246     int8_t isdir;
1247     uint32_t sector;
1248     uint32_t size;
1249     time_t modtime;
1250     //
1251     // Internal data
1252     //
1253     uint32_t depth;
1254     struct node* up;
1255     //
1256     // Current state, if this is a directory and we're reading it
1257     //
1258     uint32_t dirread;
1259 };
1260 
rewindnode(struct node * n)1261 static void rewindnode(struct node* n) { n->dirread = 0; }
1262 
freesubnodes(struct node * n,struct node * root)1263 static void freesubnodes(struct node* n, struct node* root) {
1264     while(n && n != root) {
1265         struct node* up = n->up;
1266         free(n);
1267         n = up;
1268     }
1269 }
1270 
printnodename(struct node * n)1271 static void printnodename(struct node* n) {
1272     struct node* prev = NULL;
1273     //
1274     // Special case root directory
1275     //
1276     if(n && !n->up) {
1277         printf("/");
1278     } else {
1279         int num = 0;
1280         //
1281         // Momentarily reverse node order
1282         //
1283         while(n) {
1284             struct node* up = n->up;
1285             n->up = prev;
1286             prev = n;
1287             n = up;
1288         }
1289         n = prev;
1290         prev = NULL;
1291         //
1292         // Reverse node order again while printing names
1293         //
1294         while(n) {
1295             struct node* up = n->up;
1296             n->up = prev;
1297             if(strcmp((const char*)(n->name), ".")) {
1298                 if(num++) { printf("/"); }
1299                 printsafestring(n->name, strlen((const char*)(n->name)));
1300             }
1301             prev = n;
1302             n = up;
1303         }
1304     }
1305 }
1306 
nodenamelen(const struct node * n)1307 static size_t nodenamelen(const struct node* n) {
1308     size_t len = 0;
1309     for(; n && n->up; n = n->up) {
1310         len += strlen((const char*)(n->name));
1311         if(n->up->up) {
1312             len++; // directory separator
1313         }
1314     }
1315     return len;
1316 }
1317 
copynodename(char * end,const struct node * n)1318 static void copynodename(char* end, const struct node* n) {
1319     for(; n && n->up; n = n->up) {
1320         size_t len = strlen((const char*)(n->name));
1321         memmove(end - len, n->name, len);
1322         end -= len;
1323         if(n->up->up) {
1324             *(--end) = '/';
1325         }
1326     }
1327 }
1328 
1329 ////////////////////////////////////////////////////////////////////////////////
1330 //
1331 // Read an ISO9660 directory record
1332 //
1333 // Returns the number of bytes in the record, 0 on end, or -1 on error
1334 //
read_iso_dr(struct binfile * bin,struct node * n,uint32_t sector,uint32_t ofs,uint32_t size,const struct cdpatch_options * opt)1335 static int read_iso_dr(
1336     struct binfile* bin,
1337     struct node* n,
1338     uint32_t sector,
1339     uint32_t ofs,
1340     uint32_t size,
1341     const struct cdpatch_options* opt
1342 ) {
1343     uint8_t dr[256];
1344     uint32_t little;
1345     uint32_t big;
1346     size_t i, namelen;
1347 
1348     n->dr_sector = sector;
1349     n->dr_ofs    = ofs;
1350     n->name[0]   = 0;
1351     n->isdir     = 0;
1352     n->sector    = 0;
1353     n->size      = 0;
1354     n->modtime   = (time_t)(-1);
1355     rewindnode(n);
1356 
1357     if(ofs >= size) { return 0; }
1358     //
1359     // Read DR length
1360     //
1361     if(read_cooked_data(bin, sector, ofs, dr, 1, opt)) { return -1; }
1362     if(dr[DR_length] == 0) { return 0; }
1363     if(dr[DR_length] > (size - ofs)) {
1364         dr[DR_length] = (uint8_t)(size - ofs);
1365         goto malformed;
1366     }
1367     if(dr[DR_length] < DR_name) { goto malformed; }
1368     //
1369     // Read DR
1370     //
1371     if(read_cooked_data(bin, sector, ofs+1, dr+1, dr[DR_length]-1, opt)) {
1372         return -1;
1373     }
1374     if(dr[DR_name_len] > (dr[DR_length] - DR_name)) { goto malformed; }
1375     if(dr[DR_name_len] == 0) { goto malformed; }
1376     namelen = dr[DR_name_len];
1377 
1378     //
1379     // Get name (but don't normalize yet)
1380     //
1381     memmove(n->name, dr + DR_name, namelen);
1382     n->name[namelen] = 0;
1383     //
1384     // Get directory flag
1385     //
1386     n->isdir = (dr[DR_flags] & 0x02) != 0;
1387 
1388     //
1389     // Normalize name, checking it for errors in the process
1390     //
1391     if(namelen == 1 && n->name[0] == 0) {
1392         n->name[0] = '.';
1393     } else if(namelen == 1 && n->name[0] == 1) {
1394         n->name[0] = '.';
1395         n->name[1] = '.';
1396         n->name[2] = 0;
1397         namelen = 2;
1398     } else {
1399         for(i = 0; i < namelen; i++) {
1400             uint8_t c = n->name[i];
1401             if(
1402                 (c >= 'a' && c <= 'z') ||
1403                 (c >= '0' && c <= '9') ||
1404                 (c == '_')
1405             ) {
1406                 // acceptable character
1407             } else if(c >= 'A' && c <= 'Z') {
1408                 // uppercase alpha - convert to lowercase
1409                 c += ('a' - 'A');
1410             } else if(c == '.') {
1411                 // if the next character is the end, or version number, then
1412                 // eat the dot
1413                 if(n->name[i + 1] == 0 || n->name[i + 1] == ';') {
1414                     memmove(n->name + i, n->name + i + 1, namelen - i);
1415                     namelen--;
1416                     i--;
1417                     continue;
1418                 }
1419             } else if(c == ';') {
1420                 // semicolon - strip the rest of the name if ";1"
1421                 if(i == (namelen - 2) && n->name[i + 1] == '1') {
1422                     c = 0;
1423                     namelen = i;
1424                 }
1425             } else {
1426                 // not ok - just replace with an underscore
1427                 c = '_';
1428             }
1429             n->name[i] = c;
1430         }
1431     }
1432     //
1433     // Get sector
1434     //
1435     little = get32lsb(dr + DR_extent + 0);
1436     big    = get32msb(dr + DR_extent + 4);
1437     if(little == big || opt->little) {
1438         n->sector = little;
1439     } else if(opt->big) {
1440         n->sector = big;
1441     } else {
1442         goto error_mismatch;
1443     }
1444     //
1445     // Get size
1446     //
1447     little = get32lsb(dr + DR_size + 0);
1448     big    = get32msb(dr + DR_size + 4);
1449     if(little == big || opt->little) {
1450         n->size = little;
1451     } else if(opt->big) {
1452         n->size = big;
1453     } else {
1454         goto error_mismatch;
1455     }
1456     //
1457     // Get modified time
1458     //
1459     {   int32_t year   = ((uint8_t)(dr[DR_date + 0]));
1460         int32_t month  = ((uint8_t)(dr[DR_date + 1]));
1461         int32_t day    = ((uint8_t)(dr[DR_date + 2]));
1462         int32_t hour   = ((uint8_t)(dr[DR_date + 3]));
1463         int32_t minute = ((uint8_t)(dr[DR_date + 4]));
1464         int32_t second = ((uint8_t)(dr[DR_date + 5]));
1465         int32_t gmtofs = (( int8_t)(dr[DR_date + 6]));
1466         year -= 70; // year = Years since 1970
1467         if(year < 0) {
1468             // Before 1970: Invalid
1469             n->modtime = (time_t)(-1);
1470         } else {
1471             int32_t i;
1472             // Calculate modtime = days since 1970
1473             n->modtime = year;
1474             n->modtime *= 365;
1475             // Adjust for leap years
1476             if(year > 2) {
1477                 n->modtime += (year + 1) / 4;
1478             }
1479             if(((((year+2) % 4)) == 0) && (month > 2)) {
1480                 n->modtime += 1;
1481             }
1482             // Add days from previous months
1483             for(i = 1; i < month; i++) {
1484                 static const int32_t md[] = {
1485                     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
1486                 };
1487                 n->modtime += md[i - 1];
1488             }
1489             // Add days from this month
1490             n->modtime += day - 1;
1491             // Multiply out modtime from days into seconds
1492             n->modtime *= 24;
1493             n->modtime += hour;
1494             n->modtime *= 60;
1495             n->modtime += minute;
1496             n->modtime *= 60;
1497             n->modtime += second;
1498             // Adjust modtime into GMT using the provided offset
1499             if(gmtofs >= -52 && gmtofs <= 52) {
1500                 n->modtime -= gmtofs * 15 * 60;
1501             }
1502         }
1503     }
1504     //
1505     // Success: Return the length of the directory record
1506     //
1507     return dr[DR_length];
1508 
1509 malformed:
1510     if(opt->enforce_fscheck || opt->verbose) {
1511         printf("%s: ",
1512             opt->enforce_fscheck ? "Error" : "Warning"
1513         );
1514         printnodename(n);
1515         printf(": Malformed directory record%s\n",
1516             opt->enforce_fscheck ? fsoverride : ""
1517         );
1518     }
1519     if(opt->enforce_fscheck) { return -1; }
1520     return dr[DR_length];
1521 error_mismatch:
1522     printf("Error: ");
1523     printnodename(n);
1524     printf(": Mismatched little vs. big-endian metadata\n");
1525     printf("Use -le or -be to override\n");
1526     return -1;
1527 }
1528 
1529 ////////////////////////////////////////////////////////////////////////////////
1530 //
1531 // Get the next directory entry in a directory
1532 // Returns nonzero on error
1533 // Returns zero, but NULL node, if nothing was found
1534 //
findnext(struct binfile * bin,struct node * n,struct node ** out,const struct cdpatch_options * opt)1535 static int findnext(
1536     struct binfile* bin,
1537     struct node* n,
1538     struct node** out,
1539     const struct cdpatch_options* opt
1540 ) {
1541     int returncode = 0;
1542     //
1543     // Allocate a new node
1544     //
1545     *out = malloc(sizeof(struct node));
1546     if(!(*out)) { goto error_mem; }
1547     (*out)->up = n;
1548     (*out)->depth = n->depth + 1;
1549     //
1550     // Read node data from DR
1551     //
1552     returncode = read_iso_dr(bin, *out, n->sector, n->dirread, n->size, opt);
1553     if(returncode < 0) { goto error; }
1554     if(returncode == 0) { goto notfound; }
1555     //
1556     // Advance
1557     //
1558     n->dirread += returncode;
1559     returncode = 0;
1560     goto done;
1561 
1562 error_mem:
1563     printf("%s", oom);
1564     goto error;
1565 error:
1566     returncode = 1;
1567     goto notfound;
1568 notfound:
1569     if(*out) { free(*out); *out = NULL; }
1570     goto done;
1571 done:
1572     return returncode;
1573 }
1574 
1575 ////////////////////////////////////////////////////////////////////////////////
1576 
pathsep(char c)1577 static int pathsep(char c) { return c == '/' || c == '\\'; }
pathend(char c)1578 static int pathend(char c) { return c == 0 || pathsep(c); }
1579 
1580 ////////////////////////////////////////////////////////////////////////////////
1581 //
1582 // Pass f=NULL to get the boot area
1583 //
dofile(struct binfile * bin,struct node * filenode,const struct cdpatch_options * opt,int automatic,uint32_t * numerrors,uint32_t * numsuccesses)1584 static void dofile(
1585     struct binfile* bin,
1586     struct node* filenode,
1587     const struct cdpatch_options* opt,
1588     int automatic,
1589     uint32_t* numerrors,
1590     uint32_t* numsuccesses
1591 ) {
1592     //
1593     // Construct base filename from base name and node name
1594     //
1595     char* filename = NULL;
1596     size_t basedirlen = strlen(opt->basedir);
1597     size_t addsep = !(basedirlen && pathsep(opt->basedir[basedirlen - 1]));
1598     size_t filenamelen = basedirlen + addsep +
1599         (filenode ? nodenamelen(filenode) : 4);
1600 
1601     filename = malloc(filenamelen + 1);
1602     if(!filename) { goto error_mem; }
1603 
1604     memmove(filename, opt->basedir, basedirlen);
1605     if(addsep) { filename[basedirlen] = '/'; }
1606     if(filenode) {
1607         copynodename(filename + filenamelen, filenode);
1608     } else {
1609         memmove(filename + filenamelen - 4, "boot", 4);
1610     }
1611     filename[filenamelen] = 0;
1612 
1613     if(opt->extract) {
1614         //
1615         // Ensure that all parent directories are created
1616         //
1617         char* t = filename;
1618         while(*t) {
1619             while(pathsep(*t)) { t++; }
1620             while(!pathend(*t)) { t++; }
1621             if(pathsep(*t)) {
1622                 char c = *t;
1623                 *t = 0;
1624                 mkdir(filename); // OK if it doesn't succeed
1625                 *t = c;
1626             }
1627         }
1628         //
1629         // Extract
1630         //
1631         if(filenode) {
1632             if(extract_file(
1633                 bin,
1634                 filenode->sector,
1635                 filenode->size,
1636                 filenode->modtime,
1637                 filename,
1638                 opt
1639             )) { goto error; }
1640         } else {
1641             if(extract_file(bin, 0, 32768, (time_t)(-1), filename, opt)) {
1642                 goto error;
1643             }
1644         }
1645         (*numsuccesses)++;
1646 
1647     } else if(opt->insert && (!automatic || exists(filename))) {
1648         //
1649         // Insert
1650         //
1651         if(filenode) {
1652             uint32_t newsize = filenode->size;
1653             if(insert_file(bin, filenode->sector, &newsize, filename, opt)) {
1654                 goto error;
1655             }
1656             //
1657             // If the size changed, update the size in the DR
1658             //
1659             if(newsize != filenode->size) {
1660                 uint8_t sz[8];
1661                 set32lsb(sz + 0, newsize);
1662                 set32msb(sz + 4, newsize);
1663                 if(write_cooked_data(
1664                     bin,
1665                     filenode->dr_sector,
1666                     filenode->dr_ofs + DR_size,
1667                     sz,
1668                     8,
1669                     opt
1670                 )) { goto error; }
1671             }
1672         } else {
1673             uint32_t newsize = 32768;
1674             if(insert_file(bin, 0, &newsize, filename, opt)) {
1675                 goto error;
1676             }
1677         }
1678         (*numsuccesses)++;
1679     }
1680 
1681     goto done;
1682 
1683 error_mem:
1684     printf("%s", oom);
1685     goto error;
1686 error:
1687     (*numerrors)++;
1688     goto done;
1689 done:
1690     if(filename) { free(filename); }
1691 }
1692 
1693 ////////////////////////////////////////////////////////////////////////////////
1694 //
1695 // Recursively walk a directory
1696 //
walkdirectory(struct binfile * bin,struct node * root,const struct cdpatch_options * opt,uint32_t * numerrors,uint32_t * numsuccesses)1697 static void walkdirectory(
1698     struct binfile* bin,
1699     struct node* root,
1700     const struct cdpatch_options* opt,
1701     uint32_t* numerrors,
1702     uint32_t* numsuccesses
1703 ) {
1704     struct node* n = root;
1705     struct node* f = NULL;
1706 
1707     rewindnode(n);
1708     for(;;) {
1709         if(findnext(bin, n, &f, opt)) { goto error; }
1710         if(!f) {
1711             if(n == root) {
1712                 // Done with entire tree walk
1713                 break;
1714             } else {
1715                 // Back to previous directory
1716                 f = n;
1717                 n = n->up;
1718                 freesubnodes(f, n);
1719                 f = NULL;
1720             }
1721         } else if(!f->name[0]) {
1722             //
1723             // Probably a malformed entry which was ignored
1724             //
1725 
1726         } else if(f->isdir) {
1727             if(
1728                 strcmp((const char*)(f->name), ".") &&
1729                 strcmp((const char*)(f->name), "..")
1730             ) {
1731                 // Ensure this directory didn't appear anywhere up the chain
1732                 struct node* t;
1733                 for(t = n; t != root; t = t->up) {
1734                     if(t->sector == f->sector) { break; }
1735                 }
1736                 if(t->sector == f->sector) {
1737                     if(opt->enforce_fscheck || opt->verbose) {
1738                         printf("%s: Infinite recursion: ",
1739                             opt->enforce_fscheck ? "Error" : "Warning"
1740                         );
1741                         printnodename(f);
1742                         printf(" points to ");
1743                         printnodename(t);
1744                         printf("%s\n",
1745                             opt->enforce_fscheck ? fsoverride : ""
1746                         );
1747                     }
1748                     if(opt->enforce_fscheck) { goto error; }
1749                     // Ignore the error, but we still can't descend into it
1750                 } else {
1751                     // Check depth limit.
1752                     // Allow out-of-ISO9660 spec, but impose our own limit
1753                     if(f->depth > max_path_depth) {
1754                         printf("Error: ");
1755                         printnodename(f);
1756                         printf(": Path is too deep\n");
1757                         goto error;
1758                     }
1759                     // Descend into this directory
1760                     n = f;
1761                 }
1762             }
1763         } else {
1764             //
1765             // This is a file
1766             //
1767             dofile(bin, f, opt, 1, numerrors, numsuccesses);
1768         }
1769         freesubnodes(f, n);
1770         f = NULL;
1771     }
1772     goto done;
1773 error:
1774     (*numerrors)++;
1775     goto done;
1776 done:
1777     freesubnodes(f, n);
1778     freesubnodes(n, root);
1779 }
1780 
1781 ////////////////////////////////////////////////////////////////////////////////
1782 
arenamesequal(const char * a,const char * b)1783 static int arenamesequal(const char* a, const char* b) {
1784     for(;;) {
1785         int ca = (*a++) & 0xff;
1786         int cb = (*b++) & 0xff;
1787         if(pathend(ca) && pathend(cb)) { return 1; }
1788         if(pathend(ca)) { return 0; }
1789         if(pathend(cb)) { return 0; }
1790         ca = tolower(ca);
1791         cb = tolower(cb);
1792         if(ca != cb) { return 0; }
1793     }
1794 }
1795 
1796 ////////////////////////////////////////////////////////////////////////////////
1797 //
1798 // Find a file or directory in the ISO9660 filesystem
1799 // Returns NULL if not found
1800 //
findnode(struct binfile * bin,struct node * root,const char * filename,const struct cdpatch_options * opt)1801 static struct node* findnode(
1802     struct binfile* bin,
1803     struct node* root,
1804     const char* filename,
1805     const struct cdpatch_options* opt
1806 ) {
1807     struct node* n = root;
1808     struct node* f = NULL;
1809     const char* p = filename;
1810     const char* next = p;
1811 
1812     while(pathsep(*p)) { p++; }
1813 
1814     for(; *p; p = next) {
1815         //
1816         // Figure out the next path component, if any
1817         //
1818         next = p;
1819         while(!pathend(*next)) { next++; }
1820         while(pathsep(*next)) { next++; }
1821         //
1822         // Special case "." and ".."
1823         //
1824         if(p[0] == '.' && pathend(p[1])) {
1825             continue;
1826         }
1827         if(p[0] == '.' && p[1] == '.' && pathend(p[2])) {
1828             if(n != root && n->up) {
1829                 f = n;
1830                 n = n->up;
1831                 freesubnodes(f, n);
1832                 f = NULL;
1833             }
1834             continue;
1835         }
1836         //
1837         // Find n = path component p in directory node n
1838         //
1839         rewindnode(n);
1840         for(;;) {
1841             if(findnext(bin, n, &f, opt)) { goto error; }
1842             if(!f) { goto error_pathnotfound; }
1843             if(
1844                 arenamesequal(p, (const char*)(f->name)) &&
1845                 (f->isdir || !(*next))
1846             ) {
1847                 n = f;
1848                 break;
1849             }
1850             freesubnodes(f, n);
1851             f = NULL;
1852         }
1853     }
1854 
1855     return n;
1856 
1857 error_pathnotfound:
1858     printf("Error: %s: Path not found in ISO\n", filename);
1859     goto error;
1860 error:
1861     freesubnodes(f, n);
1862     freesubnodes(n, root);
1863     return NULL;
1864 }
1865 
1866 ////////////////////////////////////////////////////////////////////////////////
1867 
visit_arg(struct binfile * bin,struct node * root,const char * filename,const struct cdpatch_options * opt,uint32_t * numerrors,uint32_t * numsuccesses)1868 static void visit_arg(
1869     struct binfile* bin,
1870     struct node* root,
1871     const char* filename,
1872     const struct cdpatch_options* opt,
1873     uint32_t* numerrors,
1874     uint32_t* numsuccesses
1875 ) {
1876     struct node* n = NULL;
1877 
1878     //
1879     // First, find the node in the ISO9660 filesystem
1880     //
1881     n = findnode(bin, root, filename, opt);
1882     if(!n) { goto error; }
1883 
1884     if(n->isdir) {
1885         //
1886         // If it's a directory:
1887         //
1888         // If we're not recursing, that's an error
1889         //
1890         if(!opt->recurse) {
1891             printf("Error: %s: Is a directory\n", filename);
1892             goto error;
1893         }
1894         //
1895         // Walk the directory
1896         //
1897         walkdirectory(bin, n, opt, numerrors, numsuccesses);
1898 
1899     } else {
1900         //
1901         // If it's a file, visit it
1902         //
1903         dofile(bin, n, opt, 0, numerrors, numsuccesses);
1904     }
1905 
1906     goto done;
1907 
1908 error:
1909     (*numerrors)++;
1910     goto done;
1911 done:
1912     freesubnodes(n, root);
1913 }
1914 
1915 ////////////////////////////////////////////////////////////////////////////////
1916 
cdpatch(const struct cdpatch_options * opt)1917 static int cdpatch(const struct cdpatch_options* opt) {
1918     int returncode = 0;
1919     struct binfile bin;
1920     struct node* root = NULL;
1921     int i;
1922     uint32_t numerrors    = 0;
1923     uint32_t numsuccesses = 0;
1924 
1925     if(bin_init(&bin)) { goto error; }
1926     bin.name = opt->binname;
1927 
1928     //
1929     // Attempt to open bin/iso file
1930     //
1931     bin.f = fopen(bin.name, opt->insert ? "r+b" : "rb");
1932     if(!bin.f) { goto error_bin; }
1933 
1934     if(bintype_detect(&bin)) { goto error; }
1935 
1936     if(opt->verbose) {
1937         printf("Image file: %s\n", opt->binname);
1938         printf("Format: ");
1939         switch(bin.type) {
1940         case BINTYPE_2048: printf("ISO (2048-byte sectors)\n"); break;
1941         case BINTYPE_2352: printf("BIN (2352-byte sectors)\n"); break;
1942         }
1943     }
1944 
1945     //
1946     // Read primary descriptor
1947     //
1948     {   uint8_t* data = read_cooked_sector(&bin, 16, opt);
1949         if(!data) { goto error; }
1950         if(opt->verbose) {
1951             printf("System ID: ");
1952             printsafestring(data + PD_system_id, 32);
1953             printf("\nVolume ID: ");
1954             printsafestring(data + PD_volume_id, 32);
1955             printf("\n");
1956         }
1957     }
1958 
1959     //
1960     // Insert or extract boot area, if desired
1961     //
1962     if(opt->boot) {
1963         dofile(&bin, NULL, opt, 0, &numerrors, &numsuccesses);
1964     }
1965 
1966     //
1967     // Retrive root directory info
1968     //
1969     root = malloc(sizeof(struct node));
1970     if(!root) { goto error_mem; }
1971     root->depth = 0;
1972     root->up = NULL;
1973     i = read_iso_dr(&bin, root, 16, PD_root_dir_record, 2048, opt);
1974     if(i < 0) { goto error; }
1975     if(i == 0) {
1976         printf("Error: Root directory descriptor is missing\n");
1977         goto error;
1978     }
1979 
1980     //
1981     // Visit each of the arguments
1982     //
1983     for(i = 0; i < opt->files_count; i++) {
1984         const char* file = opt->files[i];
1985         if(*file) { // If non-empty
1986             visit_arg(&bin, root, file, opt, &numerrors, &numsuccesses);
1987         }
1988     }
1989 
1990     if(numsuccesses || !numerrors) {
1991         printf("%lu file%s %s\n",
1992             (unsigned long)numsuccesses,
1993             numsuccesses != 1 ? "s" : "",
1994             opt->extract ? "extracted" : "inserted"
1995         );
1996     }
1997     if(numerrors) {
1998         printf("%lu error%s encountered\n",
1999             (unsigned long)numerrors,
2000             numerrors != 1 ? "s" : ""
2001         );
2002     }
2003 
2004     returncode = (numerrors != 0);
2005     goto done;
2006 
2007 error_mem:
2008     printf("%s", oom);
2009     goto error;
2010 error_bin:
2011     printfileerror(bin.f, bin.name);
2012     goto error;
2013 error:
2014     returncode = 1;
2015     goto done;
2016 done:
2017     if(root) { free(root); }
2018     bin_quit(&bin);
2019     return returncode;
2020 }
2021 
2022 ////////////////////////////////////////////////////////////////////////////////
2023 
checkboth(int a,int b,const char * opa,const char * opb)2024 static int checkboth(int a, int b, const char* opa, const char* opb) {
2025     if(a && b) {
2026         printf("Error: Cannot specify both %s and %s\n", opa, opb);
2027         return 1;
2028     }
2029     return 0;
2030 }
2031 
checkeither(int a,int b,const char * opa,const char * opb)2032 static int checkeither(int a, int b, const char* opa, const char* opb) {
2033     if((!a) && (!b)) {
2034         printf("Error: Must specify either %s or %s\n", opa, opb);
2035         return 1;
2036     }
2037     return 0;
2038 }
2039 
2040 ////////////////////////////////////////////////////////////////////////////////
2041 
main(int argc,char ** argv)2042 int main(int argc, char** argv) {
2043     int returncode = 0;
2044     static const char* default_files[1] = { "." };
2045     static const int   default_files_count = 1;
2046     const char* warn_missing = NULL;
2047 
2048     struct cdpatch_options opt;
2049     int i;
2050 
2051     normalize_argv0(argv[0]);
2052 
2053     memset(&opt, 0, sizeof(opt));
2054     //
2055     // Enforce filesystem checks by default (unless overridden with -f)
2056     //
2057     opt.enforce_fscheck = 1;
2058 
2059     //
2060     // Check options
2061     //
2062     if(argc == 1) { goto usage; }
2063     for(i = 1; i < argc; i++) {
2064         if(argv[i][0] == '-') {
2065             // An option
2066             if(!strcmp(argv[i], "--")) {
2067                 // No more options
2068                 i++;
2069                 break;
2070             } else if(!strcmp(argv[i], "-i")) {
2071                 if(opt.insert) { goto error_dup; }
2072                 if(i >= (argc - 1)) { goto error_missing; }
2073                 if(argv[i+1][0] == '-') { warn_missing = argv[i]; }
2074                 opt.insert = 1;
2075                 opt.binname = argv[++i];
2076                 continue;
2077             } else if(!strcmp(argv[i], "-x")) {
2078                 if(opt.extract) { goto error_dup; }
2079                 if(i >= (argc - 1)) { goto error_missing; }
2080                 if(argv[i+1][0] == '-') { warn_missing = argv[i]; }
2081                 opt.extract = 1;
2082                 opt.binname = argv[++i];
2083                 continue;
2084             } else if(!strcmp(argv[i], "-d")) {
2085                 if(opt.basedir) { goto error_dup; }
2086                 if(i >= (argc - 1)) { goto error_missing; }
2087                 if(argv[i+1][0] == '-') { warn_missing = argv[i]; }
2088                 opt.basedir = argv[++i];
2089                 continue;
2090             } else if(!strcmp(argv[i], "-be")) {
2091                 opt.big = 1;
2092                 continue;
2093             } else if(!strcmp(argv[i], "-le")) {
2094                 opt.little = 1;
2095                 continue;
2096             } else if(!strcmp(argv[i], "-boot")) {
2097                 opt.boot = 1;
2098                 continue;
2099             } else if(!strcmp(argv[i], "-f")) {
2100                 opt.enforce_fscheck = 0;
2101                 continue;
2102             } else if(!strcmp(argv[i], "-o")) {
2103                 opt.overwrite = 1;
2104                 continue;
2105             } else if(!strcmp(argv[i], "-v")) {
2106                 opt.verbose = 1;
2107                 continue;
2108             } else if(!strcmp(argv[i], "-r")) {
2109                 opt.recurse = 1;
2110                 continue;
2111             }
2112             printf("Unknown option: %s\n", argv[i]);
2113             goto error_usage;
2114         } else {
2115             // Not an option - stop here
2116             break;
2117         }
2118     }
2119 
2120     if(checkeither(opt.insert, opt.extract  ,"-i","-x")) { goto error_usage; }
2121     warn_missing = NULL;
2122     if(checkboth  (opt.big   , opt.little   ,"-be","-le")) { goto error_usage; }
2123     if(checkboth  (opt.insert, opt.extract  ,"-i","-x")) { goto error_usage; }
2124     if(checkboth  (opt.insert, opt.overwrite,"-i","-o")) { goto error_usage; }
2125 
2126     //
2127     // If base directory wasn't specified, default to "."
2128     //
2129     if(!opt.basedir) { opt.basedir = "."; }
2130 
2131     //
2132     // If no files or -boot were specified, default to "-r ."
2133     //
2134     if(i >= argc && !opt.boot) {
2135         opt.recurse = 1;
2136         opt.files       = default_files;
2137         opt.files_count = default_files_count;
2138     } else {
2139         opt.files       = (const char**)(argv + i);
2140         opt.files_count =               (argc - i);
2141     }
2142 
2143     //
2144     // Initialize ECC/EDC tables
2145     //
2146     eccedc_init();
2147 
2148     //
2149     // Go
2150     //
2151     returncode = cdpatch(&opt);
2152 
2153     goto done;
2154 
2155 error_dup:
2156     printf("Error: Specified %s twice\n", argv[i]);
2157     goto error_usage;
2158 error_missing:
2159     printf("Error: Missing parameter for %s\n", argv[i]);
2160     goto error_usage;
2161 error_usage:
2162     if(warn_missing) {
2163         printf("(Missing parameter after %s?)\n", warn_missing);
2164     }
2165     printf("\n");
2166     goto usage;
2167 usage:
2168     banner();
2169     printf(
2170         "Usage:\n"
2171         "  To insert:  %s -i bin_or_iso [options] [files...]\n"
2172         "  To extract: %s -x bin_or_iso [options] [files...]\n"
2173         "\nOptions:\n"
2174         "  -be       Favor big-endian values in ISO9660 metadata\n"
2175         "  -boot     Insert or extract boot area\n"
2176         "  -d dir    Set the base directory for inserted or extracted files\n"
2177         "            (defaults to .)\n"
2178         "  -f        Skip filesystem consistency checks\n"
2179         "  -le       Favor little-endian values in ISO9660 metadata\n"
2180         "  -o        Force overwrite when extracting files\n"
2181         "  -r        Recurse into subdirectories\n"
2182         "  -v        Verbose\n",
2183         argv[0],
2184         argv[0]
2185     );
2186     goto error;
2187 
2188 error:
2189     returncode = 1;
2190     goto done;
2191 
2192 done:
2193     return returncode;
2194 }
2195 
2196 ////////////////////////////////////////////////////////////////////////////////
2197