├── .gitignore ├── COPYING ├── Makefile ├── README.adoc ├── banner.h ├── bin2ecm.c ├── bin2ecm.txt ├── bincomp.c ├── bincomp.txt ├── brrrip.c ├── brrrip.txt ├── byteshuf.c ├── byteshuf.txt ├── cdpatch.c ├── cdpatch.txt ├── common.h ├── fakecrc.c ├── fakecrc.txt ├── hax65816.c ├── hax65816.txt ├── pecompat.c ├── pecompat.txt ├── rels.c ├── rels.txt ├── screamf.c ├── screamf.txt ├── uips.c ├── uips.txt ├── vb2rip.c ├── vb2rip.txt ├── wordadd.c └── wordadd.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#*\# 3 | .\#* 4 | *.1 5 | *.o 6 | /bin2ecm 7 | /bincomp 8 | /brrrip 9 | /byteshuf 10 | /cdpatch 11 | /fakecrc 12 | /hax65816 13 | /pecompat 14 | /rels 15 | /screamf 16 | /uips 17 | /vb2rip 18 | /wordadd 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = v$(shell git describe --dirty 2>/dev/null || echo 1.06) 2 | CPPFLAGS += -DVERSION=\"$(VERSION)\" 3 | 4 | PROGS = bin2ecm bincomp brrrip byteshuf cdpatch fakecrc hax65816 \ 5 | pecompat rels screamf uips vb2rip wordadd 6 | 7 | .PHONY: all clean install 8 | .SUFFIXES: .txt .1 9 | 10 | all: $(PROGS) 11 | 12 | clean: 13 | rm -f $(PROGS) *.1 14 | 15 | .txt.1: 16 | a2x -f manpage $*.txt 17 | 18 | prefix?=/usr/local 19 | bindir?=/bin 20 | mandir?=/share/man 21 | target=$(DESTDIR)$(prefix) 22 | 23 | install-%: % %.1 24 | install -D $< -t "$(target)$(bindir)" 25 | install -Dm644 $<.1 -t "$(target)$(mandir)/man1" 26 | 27 | install-bin2ecm: bin2ecm bin2ecm.1 28 | install -D bin2ecm -t "$(target)$(bindir)" 29 | install -Dm644 bin2ecm.1 -t "$(target)$(mandir)/man1" 30 | ln -fs bin2ecm "$(target)$(bindir)/ecm2bin" 31 | ln -fs bin2ecm.1 "$(target)$(mandir)/man1/ecm2bin.1" 32 | 33 | install: install-bin2ecm install-bincomp install-brrrip \ 34 | install-byteshuf install-cdpatch install-fakecrc \ 35 | install-hax65816 install-pecompat install-rels \ 36 | install-screamf install-uips install-vb2rip install-wordadd 37 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = cmdpack 2 | 3 | These are a set of utilities originally released by Neill Corlett 4 | (http://www.neillcorlett.com/cmdpack/), but the upstream has since 5 | vanished from the web. 6 | 7 | I don't plan on any major developments, but I'll try to clean up the 8 | utilities at least a little bit. So far, the most major change is 9 | ecm's name change to bin2ecm, and requires being called with a name of 10 | ecm2bin to do the reverse operation now. Neill's last version, 1.03, 11 | is completely preserved as the root commit and is tagged. 12 | 13 | == Obsoleted utilities 14 | 15 | Past versions of cmdpack included some utilities that have been 16 | deleted, deemed redundant by, and worse than, alternative programs. 17 | 18 | * +bin2iso+: Bob Doiron's +bin2iso+ or full CD image mounting 19 | programs like +cdemu+ can do the job better. 20 | * +byteswap+: +dd+. 21 | * +id3point+: Any number of replacements, from the command line +id3+ 22 | program to full internet database-based bulk taggers like 23 | Picard. Take your pick. 24 | * +subfile+: +dd+. 25 | * +usfv+: +rhash+. 26 | * +zerofill+: +truncate+ or +dd+. 27 | -------------------------------------------------------------------------------- /banner.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | 3 | void banner_ok(void) { 4 | printf(TITLE "\n" 5 | " " COPYR "\n" 6 | " from Command-Line Pack " 7 | VERSION 8 | "\n\n", 9 | (sizeof(off_t) > 4 && sizeof(off_t) > sizeof(size_t)) ? ", large file support" : "" 10 | ); 11 | } 12 | 13 | void banner_error(void) { 14 | printf("Configuration error\n"); 15 | exit(1); 16 | } 17 | 18 | static void banner(void) { 19 | ((sizeof(off_t) >= sizeof(size_t)) ? banner_ok : banner_error)(); 20 | // 21 | // If we've displayed the banner, we'll also want to warn that this is a 22 | // command-line app when we exit 23 | // 24 | atexit(commandlinewarning); 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | -------------------------------------------------------------------------------- /bin2ecm.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "bin2ecm - Encoder/decoder for Error Code Modeler format" 4 | #define COPYR "Copyright (C) 2002-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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Sector types 27 | // 28 | // Mode 1 29 | // ----------------------------------------------------- 30 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 31 | // 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 01 32 | // 0010h [---DATA... 33 | // ... 34 | // 0800h ...DATA---] 35 | // 0810h [---EDC---] 00 00 00 00 00 00 00 00 [---ECC... 36 | // ... 37 | // 0920h ...ECC---] 38 | // ----------------------------------------------------- 39 | // 40 | // Mode 2 (XA), form 1 41 | // ----------------------------------------------------- 42 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 43 | // 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02 44 | // 0010h [--FLAGS--] [--FLAGS--] [---DATA... 45 | // ... 46 | // 0810h ...DATA---] [---EDC---] [---ECC... 47 | // ... 48 | // 0920h ...ECC---] 49 | // ----------------------------------------------------- 50 | // 51 | // Mode 2 (XA), form 2 52 | // ----------------------------------------------------- 53 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 54 | // 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02 55 | // 0010h [--FLAGS--] [--FLAGS--] [---DATA... 56 | // ... 57 | // 0920h ...DATA---] [---EDC---] 58 | // ----------------------------------------------------- 59 | // 60 | // ADDR: Sector address, encoded as minutes:seconds:frames in BCD 61 | // FLAGS: Used in Mode 2 (XA) sectors describing the type of sector; repeated 62 | // twice for redundancy 63 | // DATA: Area of the sector which contains the actual data itself 64 | // EDC: Error Detection Code 65 | // ECC: Error Correction Code 66 | // 67 | 68 | //////////////////////////////////////////////////////////////////////////////// 69 | 70 | static uint32_t get32lsb(const uint8_t* src) { 71 | return 72 | (((uint32_t)(src[0])) << 0) | 73 | (((uint32_t)(src[1])) << 8) | 74 | (((uint32_t)(src[2])) << 16) | 75 | (((uint32_t)(src[3])) << 24); 76 | } 77 | 78 | static void put32lsb(uint8_t* dest, uint32_t value) { 79 | dest[0] = (uint8_t)(value ); 80 | dest[1] = (uint8_t)(value >> 8); 81 | dest[2] = (uint8_t)(value >> 16); 82 | dest[3] = (uint8_t)(value >> 24); 83 | } 84 | 85 | //////////////////////////////////////////////////////////////////////////////// 86 | // 87 | // LUTs used for computing ECC/EDC 88 | // 89 | static uint8_t ecc_f_lut[256]; 90 | static uint8_t ecc_b_lut[256]; 91 | static uint32_t edc_lut [256]; 92 | 93 | static void eccedc_init(void) { 94 | size_t i; 95 | for(i = 0; i < 256; i++) { 96 | uint32_t edc = i; 97 | size_t j = (i << 1) ^ (i & 0x80 ? 0x11D : 0); 98 | ecc_f_lut[i] = j; 99 | ecc_b_lut[i ^ j] = i; 100 | for(j = 0; j < 8; j++) { 101 | edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0); 102 | } 103 | edc_lut[i] = edc; 104 | } 105 | } 106 | 107 | //////////////////////////////////////////////////////////////////////////////// 108 | // 109 | // Compute EDC for a block 110 | // 111 | static uint32_t edc_compute( 112 | uint32_t edc, 113 | const uint8_t* src, 114 | size_t size 115 | ) { 116 | for(; size; size--) { 117 | edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF]; 118 | } 119 | return edc; 120 | } 121 | 122 | //////////////////////////////////////////////////////////////////////////////// 123 | // 124 | // Check ECC block (either P or Q) 125 | // Returns true if the ECC data is an exact match 126 | // 127 | static int8_t ecc_checkpq( 128 | const uint8_t* address, 129 | const uint8_t* data, 130 | size_t major_count, 131 | size_t minor_count, 132 | size_t major_mult, 133 | size_t minor_inc, 134 | const uint8_t* ecc 135 | ) { 136 | size_t size = major_count * minor_count; 137 | size_t major; 138 | for(major = 0; major < major_count; major++) { 139 | size_t index = (major >> 1) * major_mult + (major & 1); 140 | uint8_t ecc_a = 0; 141 | uint8_t ecc_b = 0; 142 | size_t minor; 143 | for(minor = 0; minor < minor_count; minor++) { 144 | uint8_t temp; 145 | if(index < 4) { 146 | temp = address[index]; 147 | } else { 148 | temp = data[index - 4]; 149 | } 150 | index += minor_inc; 151 | if(index >= size) { index -= size; } 152 | ecc_a ^= temp; 153 | ecc_b ^= temp; 154 | ecc_a = ecc_f_lut[ecc_a]; 155 | } 156 | ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b]; 157 | if( 158 | ecc[major ] != (ecc_a ) || 159 | ecc[major + major_count] != (ecc_a ^ ecc_b) 160 | ) { 161 | return 0; 162 | } 163 | } 164 | return 1; 165 | } 166 | 167 | // 168 | // Write ECC block (either P or Q) 169 | // 170 | static void ecc_writepq( 171 | const uint8_t* address, 172 | const uint8_t* data, 173 | size_t major_count, 174 | size_t minor_count, 175 | size_t major_mult, 176 | size_t minor_inc, 177 | uint8_t* ecc 178 | ) { 179 | size_t size = major_count * minor_count; 180 | size_t major; 181 | for(major = 0; major < major_count; major++) { 182 | size_t index = (major >> 1) * major_mult + (major & 1); 183 | uint8_t ecc_a = 0; 184 | uint8_t ecc_b = 0; 185 | size_t minor; 186 | for(minor = 0; minor < minor_count; minor++) { 187 | uint8_t temp; 188 | if(index < 4) { 189 | temp = address[index]; 190 | } else { 191 | temp = data[index - 4]; 192 | } 193 | index += minor_inc; 194 | if(index >= size) { index -= size; } 195 | ecc_a ^= temp; 196 | ecc_b ^= temp; 197 | ecc_a = ecc_f_lut[ecc_a]; 198 | } 199 | ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b]; 200 | ecc[major ] = (ecc_a ); 201 | ecc[major + major_count] = (ecc_a ^ ecc_b); 202 | } 203 | } 204 | 205 | // 206 | // Check ECC P and Q codes for a sector 207 | // Returns true if the ECC data is an exact match 208 | // 209 | static int8_t ecc_checksector( 210 | const uint8_t *address, 211 | const uint8_t *data, 212 | const uint8_t *ecc 213 | ) { 214 | return 215 | ecc_checkpq(address, data, 86, 24, 2, 86, ecc) && // P 216 | ecc_checkpq(address, data, 52, 43, 86, 88, ecc + 0xAC); // Q 217 | } 218 | 219 | // 220 | // Write ECC P and Q codes for a sector 221 | // 222 | static void ecc_writesector( 223 | const uint8_t *address, 224 | const uint8_t *data, 225 | uint8_t *ecc 226 | ) { 227 | ecc_writepq(address, data, 86, 24, 2, 86, ecc); // P 228 | ecc_writepq(address, data, 52, 43, 86, 88, ecc + 0xAC); // Q 229 | } 230 | 231 | //////////////////////////////////////////////////////////////////////////////// 232 | 233 | static const uint8_t zeroaddress[4] = {0, 0, 0, 0}; 234 | 235 | //////////////////////////////////////////////////////////////////////////////// 236 | // 237 | // Check if this is a sector we can compress 238 | // 239 | // Sector types: 240 | // 0: Literal bytes (not a sector) 241 | // 1: 2352 mode 1 predict sync, mode, reserved, edc, ecc 242 | // 2: 2336 mode 2 form 1 predict redundant flags, edc, ecc 243 | // 3: 2336 mode 2 form 2 predict redundant flags, edc 244 | // 245 | static int8_t detect_sector(const uint8_t* sector, size_t size_available) { 246 | if( 247 | size_available >= 2352 && 248 | sector[0x000] == 0x00 && // sync (12 bytes) 249 | sector[0x001] == 0xFF && 250 | sector[0x002] == 0xFF && 251 | sector[0x003] == 0xFF && 252 | sector[0x004] == 0xFF && 253 | sector[0x005] == 0xFF && 254 | sector[0x006] == 0xFF && 255 | sector[0x007] == 0xFF && 256 | sector[0x008] == 0xFF && 257 | sector[0x009] == 0xFF && 258 | sector[0x00A] == 0xFF && 259 | sector[0x00B] == 0x00 && 260 | sector[0x00F] == 0x01 && // mode (1 byte) 261 | sector[0x814] == 0x00 && // reserved (8 bytes) 262 | sector[0x815] == 0x00 && 263 | sector[0x816] == 0x00 && 264 | sector[0x817] == 0x00 && 265 | sector[0x818] == 0x00 && 266 | sector[0x819] == 0x00 && 267 | sector[0x81A] == 0x00 && 268 | sector[0x81B] == 0x00 269 | ) { 270 | // 271 | // Might be Mode 1 272 | // 273 | if( 274 | ecc_checksector( 275 | sector + 0xC, 276 | sector + 0x10, 277 | sector + 0x81C 278 | ) && 279 | edc_compute(0, sector, 0x810) == get32lsb(sector + 0x810) 280 | ) { 281 | return 1; // Mode 1 282 | } 283 | 284 | } else if( 285 | size_available >= 2336 && 286 | sector[0] == sector[4] && // flags (4 bytes) 287 | sector[1] == sector[5] && // versus redundant copy 288 | sector[2] == sector[6] && 289 | sector[3] == sector[7] 290 | ) { 291 | // 292 | // Might be Mode 2, Form 1 or 2 293 | // 294 | if( 295 | ecc_checksector( 296 | zeroaddress, 297 | sector, 298 | sector + 0x80C 299 | ) && 300 | edc_compute(0, sector, 0x808) == get32lsb(sector + 0x808) 301 | ) { 302 | return 2; // Mode 2, Form 1 303 | } 304 | // 305 | // Might be Mode 2, Form 2 306 | // 307 | if( 308 | edc_compute(0, sector, 0x91C) == get32lsb(sector + 0x91C) 309 | ) { 310 | return 3; // Mode 2, Form 2 311 | } 312 | } 313 | 314 | // 315 | // Nothing 316 | // 317 | return 0; 318 | } 319 | 320 | //////////////////////////////////////////////////////////////////////////////// 321 | // 322 | // Reconstruct a sector based on type 323 | // 324 | static void reconstruct_sector( 325 | uint8_t* sector, // must point to a full 2352-byte sector 326 | int8_t type 327 | ) { 328 | // 329 | // Sync 330 | // 331 | sector[0x000] = 0x00; 332 | sector[0x001] = 0xFF; 333 | sector[0x002] = 0xFF; 334 | sector[0x003] = 0xFF; 335 | sector[0x004] = 0xFF; 336 | sector[0x005] = 0xFF; 337 | sector[0x006] = 0xFF; 338 | sector[0x007] = 0xFF; 339 | sector[0x008] = 0xFF; 340 | sector[0x009] = 0xFF; 341 | sector[0x00A] = 0xFF; 342 | sector[0x00B] = 0x00; 343 | 344 | switch(type) { 345 | case 1: 346 | // 347 | // Mode 348 | // 349 | sector[0x00F] = 0x01; 350 | // 351 | // Reserved 352 | // 353 | sector[0x814] = 0x00; 354 | sector[0x815] = 0x00; 355 | sector[0x816] = 0x00; 356 | sector[0x817] = 0x00; 357 | sector[0x818] = 0x00; 358 | sector[0x819] = 0x00; 359 | sector[0x81A] = 0x00; 360 | sector[0x81B] = 0x00; 361 | break; 362 | case 2: 363 | case 3: 364 | // 365 | // Mode 366 | // 367 | sector[0x00F] = 0x02; 368 | // 369 | // Flags 370 | // 371 | sector[0x010] = sector[0x014]; 372 | sector[0x011] = sector[0x015]; 373 | sector[0x012] = sector[0x016]; 374 | sector[0x013] = sector[0x017]; 375 | break; 376 | } 377 | 378 | // 379 | // Compute EDC 380 | // 381 | switch(type) { 382 | case 1: put32lsb(sector+0x810, edc_compute(0, sector , 0x810)); break; 383 | case 2: put32lsb(sector+0x818, edc_compute(0, sector+0x10, 0x808)); break; 384 | case 3: put32lsb(sector+0x92C, edc_compute(0, sector+0x10, 0x91C)); break; 385 | } 386 | 387 | // 388 | // Compute ECC 389 | // 390 | switch(type) { 391 | case 1: ecc_writesector(sector+0xC , sector+0x10, sector+0x81C); break; 392 | case 2: ecc_writesector(zeroaddress, sector+0x10, sector+0x81C); break; 393 | } 394 | 395 | // 396 | // Done 397 | // 398 | } 399 | 400 | //////////////////////////////////////////////////////////////////////////////// 401 | // 402 | // Encode a type/count combo 403 | // 404 | // Returns nonzero on error 405 | // 406 | static int8_t write_type_count( 407 | const char* outfilename, 408 | FILE *out, 409 | int8_t type, 410 | uint32_t count 411 | ) { 412 | int8_t returncode = 0; 413 | 414 | count--; 415 | if(fputc(((count >= 32) << 7) | ((count & 31) << 2) | type, out) == EOF) { 416 | goto error_out; 417 | } 418 | count >>= 5; 419 | while(count) { 420 | if(fputc(((count >= 128) << 7) | (count & 127), out) == EOF) { 421 | goto error_out; 422 | } 423 | count >>= 7; 424 | } 425 | // 426 | // Success 427 | // 428 | returncode = 0; 429 | goto done; 430 | 431 | error_out: 432 | printfileerror(out, outfilename); 433 | goto error; 434 | 435 | error: 436 | returncode = 1; 437 | goto done; 438 | 439 | done: 440 | return returncode; 441 | } 442 | 443 | //////////////////////////////////////////////////////////////////////////////// 444 | 445 | static uint8_t sector_buffer[2352]; 446 | 447 | //////////////////////////////////////////////////////////////////////////////// 448 | 449 | static off_t mycounter_analyze = (off_t)-1; 450 | static off_t mycounter_encode = (off_t)-1; 451 | static off_t mycounter_decode = (off_t)-1; 452 | static off_t mycounter_total = 0; 453 | 454 | static void resetcounter(off_t total) { 455 | mycounter_analyze = (off_t)-1; 456 | mycounter_encode = (off_t)-1; 457 | mycounter_decode = (off_t)-1; 458 | mycounter_total = total; 459 | } 460 | 461 | static void encode_progress(void) { 462 | off_t a = (mycounter_analyze + 64) / 128; 463 | off_t e = (mycounter_encode + 64) / 128; 464 | off_t t = (mycounter_total + 64) / 128; 465 | if(!t) { t = 1; } 466 | fprintf(stderr, 467 | "Analyze(%02u%%) Encode(%02u%%)\r", 468 | (unsigned)((((off_t)100) * a) / t), 469 | (unsigned)((((off_t)100) * e) / t) 470 | ); 471 | } 472 | 473 | static void decode_progress(void) { 474 | off_t d = (mycounter_decode + 64) / 128; 475 | off_t t = (mycounter_total + 64) / 128; 476 | if(!t) { t = 1; } 477 | fprintf(stderr, 478 | "Decode(%02u%%)\r", 479 | (unsigned)((((off_t)100) * d) / t) 480 | ); 481 | } 482 | 483 | static void setcounter_analyze(off_t n) { 484 | int8_t p = ((n >> 20) != (mycounter_analyze >> 20)); 485 | mycounter_analyze = n; 486 | if(p) { encode_progress(); } 487 | } 488 | 489 | static void setcounter_encode(off_t n) { 490 | int8_t p = ((n >> 20) != (mycounter_encode >> 20)); 491 | mycounter_encode = n; 492 | if(p) { encode_progress(); } 493 | } 494 | 495 | static void setcounter_decode(off_t n) { 496 | int8_t p = ((n >> 20) != (mycounter_decode >> 20)); 497 | mycounter_decode = n; 498 | if(p) { decode_progress(); } 499 | } 500 | 501 | //////////////////////////////////////////////////////////////////////////////// 502 | // 503 | // Encode a run of sectors/literals of the same type 504 | // 505 | // Returns nonzero on error 506 | // 507 | static int8_t write_sectors( 508 | int8_t type, 509 | uint32_t count, 510 | const char* infilename, 511 | const char* outfilename, 512 | FILE* in, 513 | FILE* out 514 | ) { 515 | int8_t returncode = 0; 516 | 517 | if(write_type_count(outfilename, out, type, count)) { goto error; } 518 | 519 | if(type == 0) { 520 | while(count) { 521 | uint32_t b = count; 522 | if(b > sizeof(sector_buffer)) { b = sizeof(sector_buffer); } 523 | if(fread(sector_buffer, 1, b, in) != b) { goto error_in; } 524 | if(fwrite(sector_buffer, 1, b, out) != b) { goto error_out; } 525 | count -= b; 526 | setcounter_encode(ftello(in)); 527 | } 528 | return 0; 529 | } 530 | for(; count; count--) { 531 | switch(type) { 532 | case 1: 533 | if(fread(sector_buffer, 1, 2352, in) != 2352) { goto error_in; } 534 | if(fwrite(sector_buffer + 0x00C, 1, 0x003, out) != 0x003) { goto error_out; } 535 | if(fwrite(sector_buffer + 0x010, 1, 0x800, out) != 0x800) { goto error_out; } 536 | break; 537 | case 2: 538 | if(fread(sector_buffer, 1, 2336, in) != 2336) { goto error_in; } 539 | if(fwrite(sector_buffer + 0x004, 1, 0x804, out) != 0x804) { goto error_out; } 540 | break; 541 | case 3: 542 | if(fread(sector_buffer, 1, 2336, in) != 2336) { goto error_in; } 543 | if(fwrite(sector_buffer + 0x004, 1, 0x918, out) != 0x918) { goto error_out; } 544 | break; 545 | } 546 | setcounter_encode(ftello(in)); 547 | } 548 | // 549 | // Success 550 | // 551 | returncode = 0; 552 | goto done; 553 | 554 | error_in: 555 | printfileerror(in, infilename); 556 | goto error; 557 | 558 | error_out: 559 | printfileerror(out, outfilename); 560 | goto error; 561 | 562 | error: 563 | returncode = 1; 564 | goto done; 565 | 566 | done: 567 | return returncode; 568 | } 569 | 570 | //////////////////////////////////////////////////////////////////////////////// 571 | // 572 | // Returns nonzero on error 573 | // 574 | static int8_t ecmify( 575 | const char* infilename, 576 | const char* outfilename 577 | ) { 578 | int8_t returncode = 0; 579 | 580 | FILE* in = NULL; 581 | FILE* out = NULL; 582 | 583 | uint8_t* queue = NULL; 584 | size_t queue_start_ofs = 0; 585 | size_t queue_bytes_available = 0; 586 | 587 | uint32_t input_edc = 0; 588 | 589 | // 590 | // Current sector type (run) 591 | // 592 | int8_t curtype = -1; // not a valid type 593 | uint32_t curtype_count = 0; 594 | off_t curtype_in_start = 0; 595 | 596 | uint32_t literal_skip = 0; 597 | 598 | off_t input_file_length; 599 | off_t input_bytes_checked = 0; 600 | off_t input_bytes_queued = 0; 601 | 602 | off_t typetally[4] = {0,0,0,0}; 603 | 604 | static const size_t sectorsize[4] = { 605 | 1, 606 | 2352, 607 | 2336, 608 | 2336 609 | }; 610 | 611 | size_t queue_size = ((size_t)(-1)) - 4095; 612 | if((unsigned long)queue_size > 0x40000lu) { 613 | queue_size = (size_t)0x40000lu; 614 | } 615 | 616 | // 617 | // Allocate space for queue 618 | // 619 | queue = malloc(queue_size); 620 | if(!queue) { 621 | printf("Out of memory\n"); 622 | goto error; 623 | } 624 | 625 | // 626 | // Ensure the output file doesn't already exist 627 | // 628 | out = fopen(outfilename, "rb"); 629 | if(out) { 630 | printf("Error: %s exists; refusing to overwrite\n", outfilename); 631 | goto error; 632 | } 633 | 634 | // 635 | // Open both files 636 | // 637 | in = fopen(infilename, "rb"); 638 | if(!in) { goto error_in; } 639 | 640 | out = fopen(outfilename, "wb"); 641 | if(!out) { goto error_out; } 642 | 643 | printf("Encoding %s to %s...\n", infilename, outfilename); 644 | 645 | // 646 | // Get the length of the input file 647 | // 648 | if(fseeko(in, 0, SEEK_END) != 0) { goto error_in; } 649 | input_file_length = ftello(in); 650 | if(input_file_length < 0) { goto error_in; } 651 | 652 | resetcounter(input_file_length); 653 | 654 | // 655 | // Magic identifier 656 | // 657 | if(fputc('E' , out) == EOF) { goto error_out; } 658 | if(fputc('C' , out) == EOF) { goto error_out; } 659 | if(fputc('M' , out) == EOF) { goto error_out; } 660 | if(fputc(0x00, out) == EOF) { goto error_out; } 661 | 662 | for(;;) { 663 | int8_t detecttype; 664 | 665 | // 666 | // Refill queue if necessary 667 | // 668 | if( 669 | (queue_bytes_available < 2352) && 670 | (((off_t)queue_bytes_available) < (input_file_length - input_bytes_queued)) 671 | ) { 672 | // 673 | // We need to read more data 674 | // 675 | off_t willread = input_file_length - input_bytes_queued; 676 | off_t maxread = queue_size - queue_bytes_available; 677 | if(willread > maxread) { 678 | willread = maxread; 679 | } 680 | 681 | if(queue_start_ofs > 0) { 682 | memmove(queue, queue + queue_start_ofs, queue_bytes_available); 683 | queue_start_ofs = 0; 684 | } 685 | if(willread) { 686 | setcounter_analyze(input_bytes_queued); 687 | 688 | if(fseeko(in, input_bytes_queued, SEEK_SET) != 0) { 689 | goto error_in; 690 | } 691 | if(fread(queue + queue_bytes_available, 1, willread, in) != (size_t)willread) { 692 | goto error_in; 693 | } 694 | 695 | input_edc = edc_compute( 696 | input_edc, 697 | queue + queue_bytes_available, 698 | willread 699 | ); 700 | 701 | input_bytes_queued += willread; 702 | queue_bytes_available += willread; 703 | } 704 | } 705 | 706 | if(queue_bytes_available == 0) { 707 | // 708 | // No data left to read -> quit 709 | // 710 | detecttype = -1; 711 | 712 | } else if(literal_skip > 0) { 713 | // 714 | // Skipping through literal bytes 715 | // 716 | literal_skip--; 717 | detecttype = 0; 718 | 719 | } else { 720 | // 721 | // Heuristic to skip past CD sync after a mode 2 sector 722 | // 723 | if( 724 | curtype >= 2 && 725 | queue_bytes_available >= 0x10 && 726 | queue[queue_start_ofs + 0x0] == 0x00 && 727 | queue[queue_start_ofs + 0x1] == 0xFF && 728 | queue[queue_start_ofs + 0x2] == 0xFF && 729 | queue[queue_start_ofs + 0x3] == 0xFF && 730 | queue[queue_start_ofs + 0x4] == 0xFF && 731 | queue[queue_start_ofs + 0x5] == 0xFF && 732 | queue[queue_start_ofs + 0x6] == 0xFF && 733 | queue[queue_start_ofs + 0x7] == 0xFF && 734 | queue[queue_start_ofs + 0x8] == 0xFF && 735 | queue[queue_start_ofs + 0x9] == 0xFF && 736 | queue[queue_start_ofs + 0xA] == 0xFF && 737 | queue[queue_start_ofs + 0xB] == 0x00 && 738 | queue[queue_start_ofs + 0xF] == 0x02 739 | ) { 740 | // Treat this byte as a literal... 741 | detecttype = 0; 742 | // ...and skip the next 15 743 | literal_skip = 15; 744 | } else { 745 | // 746 | // Detect the sector type at the current offset 747 | // 748 | detecttype = detect_sector(queue + queue_start_ofs, queue_bytes_available); 749 | } 750 | } 751 | 752 | if( 753 | (detecttype == curtype) && 754 | (curtype_count <= 0x7FFFFFFF) // avoid overflow 755 | ) { 756 | // 757 | // Same type as last sector 758 | // 759 | curtype_count++; 760 | 761 | } else { 762 | // 763 | // Changing types: Flush the input 764 | // 765 | if(curtype_count > 0) { 766 | if(fseeko(in, curtype_in_start, SEEK_SET) != 0) { goto error_in; } 767 | typetally[curtype] += curtype_count; 768 | if(write_sectors( 769 | curtype, 770 | curtype_count, 771 | infilename, 772 | outfilename, 773 | in, 774 | out 775 | )) { goto error; } 776 | } 777 | curtype = detecttype; 778 | curtype_in_start = input_bytes_checked; 779 | curtype_count = 1; 780 | 781 | } 782 | 783 | // 784 | // Current type is negative ==> quit 785 | // 786 | if(curtype < 0) { break; } 787 | 788 | // 789 | // Advance to the next sector 790 | // 791 | input_bytes_checked += sectorsize[curtype]; 792 | queue_start_ofs += sectorsize[curtype]; 793 | queue_bytes_available -= sectorsize[curtype]; 794 | 795 | } 796 | 797 | // 798 | // Store the end-of-records indicator 799 | // 800 | if(write_type_count(outfilename, out, 0, 0)) { goto error; } 801 | 802 | // 803 | // Store the EDC of the input file 804 | // 805 | put32lsb(sector_buffer, input_edc); 806 | if(fwrite(sector_buffer, 1, 4, out) != 4) { goto error_out; } 807 | 808 | // 809 | // Show report 810 | // 811 | printf("Literal bytes........... "); fprintdec(stdout, typetally[0]); printf("\n"); 812 | printf("Mode 1 sectors.......... "); fprintdec(stdout, typetally[1]); printf("\n"); 813 | printf("Mode 2 form 1 sectors... "); fprintdec(stdout, typetally[2]); printf("\n"); 814 | printf("Mode 2 form 2 sectors... "); fprintdec(stdout, typetally[3]); printf("\n"); 815 | printf("Encoded "); 816 | fprintdec(stdout, input_file_length); 817 | printf(" bytes -> "); 818 | fprintdec(stdout, ftello(out)); 819 | printf(" bytes\n"); 820 | 821 | // 822 | // Success 823 | // 824 | printf("Done\n"); 825 | returncode = 0; 826 | goto done; 827 | 828 | error_in: 829 | printfileerror(in, infilename); 830 | goto error; 831 | 832 | error_out: 833 | printfileerror(out, outfilename); 834 | goto error; 835 | 836 | error: 837 | returncode = 1; 838 | goto done; 839 | 840 | done: 841 | if(queue != NULL) { free(queue); } 842 | if(in != NULL) { fclose(in ); } 843 | if(out != NULL) { fclose(out); } 844 | 845 | return returncode; 846 | } 847 | 848 | //////////////////////////////////////////////////////////////////////////////// 849 | // 850 | // Returns nonzero on error 851 | // 852 | static int8_t unecmify( 853 | const char* infilename, 854 | const char* outfilename 855 | ) { 856 | int8_t returncode = 0; 857 | 858 | FILE* in = NULL; 859 | FILE* out = NULL; 860 | 861 | off_t input_file_length; 862 | 863 | uint32_t output_edc = 0; 864 | int8_t type; 865 | uint32_t num; 866 | 867 | // 868 | // Ensure the output file doesn't already exist 869 | // 870 | out = fopen(outfilename, "rb"); 871 | if(out) { 872 | printf("Error: %s exists; refusing to overwrite\n", outfilename); 873 | goto error; 874 | } 875 | 876 | // 877 | // Open both files 878 | // 879 | in = fopen(infilename, "rb"); 880 | if(!in) { goto error_in; } 881 | 882 | // 883 | // Get the length of the input file 884 | // 885 | if(fseeko(in, 0, SEEK_END) != 0) { goto error_in; } 886 | input_file_length = ftello(in); 887 | if(input_file_length < 0) { goto error_in; } 888 | 889 | resetcounter(input_file_length); 890 | 891 | if(fseeko(in, 0, SEEK_SET) != 0) { goto error_in; } 892 | 893 | // 894 | // Magic header 895 | // 896 | if( 897 | (fgetc(in) != 'E') || 898 | (fgetc(in) != 'C') || 899 | (fgetc(in) != 'M') || 900 | (fgetc(in) != 0x00) 901 | ) { 902 | printf("Header missing; does not appear to be an ECM file\n"); 903 | goto error; 904 | } 905 | 906 | // 907 | // Open output file 908 | // 909 | out = fopen(outfilename, "wb"); 910 | if(!out) { goto error_out; } 911 | 912 | printf("Decoding %s to %s...\n", infilename, outfilename); 913 | 914 | for(;;) { 915 | int c = fgetc(in); 916 | int bits = 5; 917 | if(c == EOF) { goto error_in; } 918 | type = c & 3; 919 | num = (c >> 2) & 0x1F; 920 | while(c & 0x80) { 921 | c = fgetc(in); 922 | if(c == EOF) { goto error_in; } 923 | if( 924 | (bits > 31) || 925 | ((uint32_t)(c & 0x7F)) >= (((uint32_t)0x80000000LU) >> (bits-1)) 926 | ) { 927 | printf("Corrupt ECM file; invalid sector count\n"); 928 | goto error; 929 | } 930 | num |= ((uint32_t)(c & 0x7F)) << bits; 931 | bits += 7; 932 | } 933 | if(num == 0xFFFFFFFF) { 934 | // End indicator 935 | break; 936 | } 937 | num++; 938 | if(type == 0) { 939 | while(num) { 940 | uint32_t b = num; 941 | if(b > sizeof(sector_buffer)) { b = sizeof(sector_buffer); } 942 | if(fread(sector_buffer, 1, b, in) != b) { 943 | goto error_in; 944 | } 945 | output_edc = edc_compute(output_edc, sector_buffer, b); 946 | if(fwrite(sector_buffer, 1, b, out) != b) { 947 | goto error_out; 948 | } 949 | num -= b; 950 | setcounter_decode(ftello(in)); 951 | } 952 | } else { 953 | for(; num; num--) { 954 | switch(type) { 955 | case 1: 956 | if(fread(sector_buffer + 0x00C, 1, 0x003, in) != 0x003) { goto error_in; } 957 | if(fread(sector_buffer + 0x010, 1, 0x800, in) != 0x800) { goto error_in; } 958 | reconstruct_sector(sector_buffer, 1); 959 | output_edc = edc_compute(output_edc, sector_buffer, 2352); 960 | if(fwrite(sector_buffer, 1, 2352, out) != 2352) { goto error_out; } 961 | break; 962 | case 2: 963 | if(fread(sector_buffer + 0x014, 1, 0x804, in) != 0x804) { goto error_in; } 964 | reconstruct_sector(sector_buffer, 2); 965 | output_edc = edc_compute(output_edc, sector_buffer + 0x10, 2336); 966 | if(fwrite(sector_buffer + 0x10, 1, 2336, out) != 2336) { goto error_out; } 967 | break; 968 | case 3: 969 | if(fread(sector_buffer + 0x014, 1, 0x918, in) != 0x918) { goto error_in; } 970 | reconstruct_sector(sector_buffer, 3); 971 | output_edc = edc_compute(output_edc, sector_buffer + 0x10, 2336); 972 | if(fwrite(sector_buffer + 0x10, 1, 2336, out) != 2336) { goto error_out; } 973 | break; 974 | } 975 | setcounter_decode(ftello(in)); 976 | } 977 | } 978 | } 979 | // 980 | // Verify the EDC of the entire output file 981 | // 982 | if(fread(sector_buffer, 1, 4, in) != 4) { goto error_in; } 983 | 984 | printf("Decoded "); 985 | fprintdec(stdout, ftello(in)); 986 | printf(" bytes -> "); 987 | fprintdec(stdout, ftello(out)); 988 | printf(" bytes\n"); 989 | 990 | if(get32lsb(sector_buffer) != output_edc) { 991 | printf("Checksum error (0x%08lX, should be 0x%08lX)\n", 992 | (unsigned long)output_edc, 993 | (unsigned long)get32lsb(sector_buffer) 994 | ); 995 | goto error; 996 | } 997 | 998 | // 999 | // Success 1000 | // 1001 | printf("Done\n"); 1002 | returncode = 0; 1003 | goto done; 1004 | 1005 | error_in: 1006 | printfileerror(in, infilename); 1007 | goto error; 1008 | 1009 | error_out: 1010 | printfileerror(out, outfilename); 1011 | goto error; 1012 | 1013 | error: 1014 | returncode = 1; 1015 | goto done; 1016 | 1017 | done: 1018 | if(in != NULL) { fclose(in ); } 1019 | if(out != NULL) { fclose(out); } 1020 | 1021 | return returncode; 1022 | } 1023 | 1024 | //////////////////////////////////////////////////////////////////////////////// 1025 | 1026 | int main(int argc, char** argv) { 1027 | int returncode = 0; 1028 | int8_t encode = 0; 1029 | char* infilename = NULL; 1030 | char* outfilename = NULL; 1031 | char* tempfilename = NULL; 1032 | 1033 | normalize_argv0(argv[0]); 1034 | 1035 | // 1036 | // Check command line 1037 | // 1038 | switch(argc) { 1039 | case 2: 1040 | // 1041 | // bin2ecm source 1042 | // ecm2bin source 1043 | // 1044 | encode = (strcmp(argv[0], "ecm2bin") != 0); 1045 | infilename = argv[1]; 1046 | 1047 | tempfilename = malloc(strlen(infilename) + 7); 1048 | if(!tempfilename) { 1049 | printf("Out of memory\n"); 1050 | goto error; 1051 | } 1052 | 1053 | strcpy(tempfilename, infilename); 1054 | 1055 | if(encode) { 1056 | // 1057 | // Append ".ecm" to the input filename 1058 | // 1059 | strcat(tempfilename, ".ecm"); 1060 | } else { 1061 | // 1062 | // Remove ".ecm" from the input filename 1063 | // 1064 | size_t l = strlen(tempfilename); 1065 | if( 1066 | (l > 4) && 1067 | tempfilename[l - 4] == '.' && 1068 | tolower(tempfilename[l - 3]) == 'e' && 1069 | tolower(tempfilename[l - 2]) == 'c' && 1070 | tolower(tempfilename[l - 1]) == 'm' 1071 | ) { 1072 | tempfilename[l - 4] = 0; 1073 | } else { 1074 | // 1075 | // If that fails, append ".unecm" to the input filename 1076 | // 1077 | strcat(tempfilename, ".unecm"); 1078 | } 1079 | } 1080 | outfilename = tempfilename; 1081 | break; 1082 | 1083 | case 3: 1084 | // 1085 | // bin2ecm source dest 1086 | // ecm2bin source dest 1087 | // 1088 | encode = (strcmp(argv[0], "ecm2bin") != 0); 1089 | infilename = argv[1]; 1090 | outfilename = argv[2]; 1091 | break; 1092 | 1093 | default: 1094 | goto usage; 1095 | } 1096 | 1097 | // 1098 | // Initialize the ECC/EDC tables 1099 | // 1100 | eccedc_init(); 1101 | 1102 | // 1103 | // Go! 1104 | // 1105 | if(encode) { 1106 | if(ecmify(infilename, outfilename)) { goto error; } 1107 | } else { 1108 | if(unecmify(infilename, outfilename)) { goto error; } 1109 | } 1110 | 1111 | // 1112 | // Success 1113 | // 1114 | returncode = 0; 1115 | goto done; 1116 | 1117 | usage: 1118 | banner(); 1119 | printf( 1120 | "Usage:\n" 1121 | "\n" 1122 | "To encode:\n" 1123 | " bin2ecm cdimagefile\n" 1124 | " bin2ecm cdimagefile ecmfile\n" 1125 | "\n" 1126 | "To decode:\n" 1127 | " ecm2bin ecmfile\n" 1128 | " ecm2bin ecmfile cdimagefile\n" 1129 | ); 1130 | 1131 | error: 1132 | returncode = 1; 1133 | goto done; 1134 | 1135 | done: 1136 | if(tempfilename) { free(tempfilename); } 1137 | return returncode; 1138 | } 1139 | 1140 | //////////////////////////////////////////////////////////////////////////////// 1141 | -------------------------------------------------------------------------------- /bin2ecm.txt: -------------------------------------------------------------------------------- 1 | bin2ecm(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | bin2ecm - encoder and decoder for the error code modeler format 8 | 9 | SYNOPSIS 10 | -------- 11 | *bin2ecm* 'cdimagefile' ['ecmfile'] 12 | 13 | *ecm2bin* 'ecmfile' ['cdimagefile'] 14 | 15 | DESCRIPTION 16 | ----------- 17 | ECM is a compression format for raw CD images (those with 2352-byte 18 | sectors) which removes ECC/EDC data where it is possible to do so 19 | losslessly. Compressing a CD image with bin2ecm first then 20 | compressing the ECM file with a general-purpose compressor such as 21 | gzip(1) or xz(1) can result in better compression than gzip or xz 22 | alone. 23 | 24 | This works because raw CD-ROM images contain a lot of redundant data 25 | that, if constructed fully to the standard specification, can be 26 | regenerated without data loss. Some discs contain invalid ECC data 27 | normally, usually as copy protection means. ECM will preserve this 28 | invalid data as-is. 29 | 30 | ecm2bin reverses the process and recreates the original CD-ROM image 31 | from an ECM file. 32 | 33 | bin2ecm followed by ecm2bin should be lossless for any kind of file, 34 | but it is only intended for and works properly with 2352-byte sector 35 | CD images. 36 | 37 | TECHNICAL BACKGROUND 38 | -------------------- 39 | 40 | Raw CD-ROM sectors, 2352 bytes each, contain five main segments in 41 | them: 42 | 43 | 1. *Sync* - a special code used by the drive firmware to tell where 44 | the sector begins. 45 | 2. *Address* - informs the drive firmware of which sector on the disc 46 | this is. 47 | 3. *Data* - the 2048-byte block of data returned to software on normal 48 | reads. This is usually a file system (such as ISO-9660 or UDF) block. 49 | 4. *EDC* - Error Detection Code, a checksum to detect if the data is 50 | corrupt. 51 | 5. *ECC* - Error Correction Code, parity data used in an attempt to 52 | repair a damaged data sector. 53 | 54 | The EDC and ECC segments sector are effectively random noise to a 55 | general-purpose compressor and will make it difficult to gain much in 56 | the compression process. 57 | 58 | When the sync, EDC, and ECC data are verifiably reproducable by 59 | standard means, bin2ecm will remove them and leave only the address 60 | and data portions, potentially providing better compression results on 61 | that sector. If these segments deviate from the standard, which is 62 | usually a result of the disc having copy protection employed on it, 63 | ECM preserves it as-is. Copy protection schemes usually leave only a 64 | few sectors with invalid data, such as at the very beginning or end of 65 | the disc, so that the bulk of the disc can properly take advantage of 66 | the CD-ROM format's capability for self-repair on read. Copy 67 | protection and preserving this invalid data is also one reason why 68 | backing up the entire 2352-byte sector, instead of the 2048-byte data 69 | segments, can be useful. 70 | 71 | ecm2bin reverses the process, recalculating the sync, EDC, and ECC 72 | segments for all the sectors that bin2ecm had trimmed. 73 | -------------------------------------------------------------------------------- /bincomp.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "bincomp - Compare binary files" 4 | #define COPYR "Copyright (C) 2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static void dumpline(off_t ofs, uint16_t mask, uint8_t* bytes, off_t limit) { 27 | off_t i; 28 | fprinthex(stdout, ofs, 8); 29 | printf(": "); 30 | for(i = 0; i < 16; i++) { 31 | if((mask >> i) & 1) { 32 | printf("%02X", (int)bytes[i]); 33 | } else { 34 | printf((ofs + i) < limit ? ". " : " "); 35 | } 36 | } 37 | printf(" "); 38 | for(i = 0; i < 16; i++) { 39 | if((mask >> i) & 1) { 40 | printf("%02X", (int)bytes[16 + i]); 41 | } else { 42 | printf((ofs + i) < limit ? ". " : " "); 43 | } 44 | } 45 | printf("\n"); 46 | } 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | 50 | int main(int argc, char** argv) { 51 | int returncode = 0; 52 | FILE* fa = NULL; 53 | FILE* fb = NULL; 54 | off_t ofs; 55 | int8_t is_different = 0; 56 | int longform = 0; 57 | off_t line_ofs = -1; 58 | uint16_t line_mask = 0; 59 | uint8_t line_bytes[32]; 60 | 61 | normalize_argv0(argv[0]); 62 | 63 | if(argc == 4) { 64 | if(strcmp(argv[3], "-l")) { goto usage; } 65 | longform = 1; 66 | } else if(argc == 3) { 67 | longform = 0; 68 | } else { 69 | goto usage; 70 | } 71 | 72 | if(!strcmp(argv[1], argv[2])) { 73 | printf("You specified the same file\n"); 74 | goto done; 75 | } 76 | 77 | fa = fopen(argv[1], "rb"); 78 | if(!fa) { goto error_fa; } 79 | 80 | fb = fopen(argv[2], "rb"); 81 | if(!fb) { goto error_fb; } 82 | 83 | for(ofs = 0;; ofs++) { 84 | int ca; 85 | int cb; 86 | 87 | if(line_mask && ((ofs | 0xF) != (line_ofs | 0xF))) { 88 | dumpline(line_ofs, line_mask, line_bytes, line_ofs + 0x10); 89 | line_mask = 0; 90 | } 91 | 92 | ca = fgetc(fa); 93 | if(ca == EOF && ferror(fa)) { goto error_fa; } 94 | 95 | cb = fgetc(fb); 96 | if(cb == EOF && ferror(fb)) { goto error_fb; } 97 | 98 | is_different |= (ca != cb); 99 | 100 | if(ca == EOF && cb != EOF) { 101 | if(line_mask) { dumpline(line_ofs, line_mask, line_bytes, ofs); } 102 | printf("%s is longer than %s\n", argv[2], argv[1]); 103 | break; 104 | } 105 | if(cb == EOF && ca != EOF) { 106 | if(line_mask) { dumpline(line_ofs, line_mask, line_bytes, ofs); } 107 | printf("%s is longer than %s\n", argv[1], argv[2]); 108 | break; 109 | } 110 | if(ca == EOF && cb == EOF) { 111 | if(line_mask) { dumpline(line_ofs, line_mask, line_bytes, ofs); } 112 | break; 113 | } 114 | if(ca != cb) { 115 | if(longform) { 116 | fprinthex(stdout, ofs, 8); 117 | printf(": %02X %02X\n", ca, cb); 118 | } else { 119 | line_ofs = (ofs | 0xF) ^ 0xF; 120 | line_mask |= 1 << (ofs & 0xF); 121 | line_bytes[ (ofs & 0xF)] = (uint8_t)ca; 122 | line_bytes[16 + (ofs & 0xF)] = (uint8_t)cb; 123 | } 124 | } 125 | 126 | } 127 | if(!is_different) { 128 | printf("Files match\n"); 129 | } 130 | 131 | returncode = is_different; 132 | goto done; 133 | 134 | error_fa: printfileerror(fa, argv[1]); goto error; 135 | error_fb: printfileerror(fb, argv[2]); goto error; 136 | 137 | usage: 138 | banner(); 139 | printf( 140 | "Usage: %s file1 file2 [-l]\n" 141 | " -l Use long format\n", 142 | argv[0] 143 | ); 144 | goto error; 145 | 146 | error: 147 | returncode = 1; 148 | 149 | done: 150 | if(fa != NULL) { fclose(fa); } 151 | if(fb != NULL) { fclose(fb); } 152 | return returncode; 153 | } 154 | 155 | //////////////////////////////////////////////////////////////////////////////// 156 | -------------------------------------------------------------------------------- /bincomp.txt: -------------------------------------------------------------------------------- 1 | bincomp(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | bincomp - compare binary files 8 | 9 | SYNOPSIS 10 | -------- 11 | *bincomp* 'file1' 'file2' ['-l'] 12 | 13 | DESCRIPTION 14 | ----------- 15 | Compares two binary files in a side-by-side table showing 16 bytes per 16 | line. The '-l' flag will show one byte per line instead. This 17 | command is similar to the DOS `FC /B` command. 18 | 19 | OPTION 20 | ------ 21 | *-l*:: Use long format 22 | -------------------------------------------------------------------------------- /brrrip.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "brrrip - Rip SNES BRR sound samples" 4 | #define COPYR "Copyright (C) 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static void set32lsb(uint8_t* dest, uint32_t n) { 27 | dest[0] = (uint8_t)(n >> 0); 28 | dest[1] = (uint8_t)(n >> 8); 29 | dest[2] = (uint8_t)(n >> 16); 30 | dest[3] = (uint8_t)(n >> 24); 31 | } 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | 35 | static int writewav( 36 | FILE* src, 37 | const char* srcfilename, 38 | off_t startpos, 39 | off_t endpos, 40 | off_t samplerate 41 | ) { 42 | int returncode = 0; 43 | static uint8_t wavhdr[] = { 44 | 0x52,0x49,0x46,0x46,0x16,0x63,0x01,0x00, 45 | 0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20, 46 | 0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00, 47 | 0x44,0xAC,0x00,0x00,0x88,0x58,0x01,0x00, 48 | 0x02,0x00,0x10,0x00,0x64,0x61,0x74,0x61, 49 | 0x40,0x62,0x01,0x00 50 | }; 51 | FILE *wav = NULL; 52 | char wavfilename[24]; 53 | int32_t p1 = 0; 54 | int32_t p2 = 0; 55 | 56 | if(fseeko(src, startpos, SEEK_SET) != 0) { goto error_src; } 57 | 58 | sprintf(wavfilename, "%06lx.wav", (unsigned long)((uint32_t)(startpos))); 59 | 60 | fprinthex(stdout, startpos, 6); 61 | printf(".."); 62 | fprinthex(stdout, endpos , 6); 63 | printf(" -> %s: ", wavfilename); 64 | fflush(stdout); 65 | 66 | wav = fopen(wavfilename, "wb"); 67 | if(!wav) { goto error_wav; } 68 | 69 | set32lsb(wavhdr + 0x04, (uint32_t)(0x24+32*((endpos - startpos) / 9))); 70 | set32lsb(wavhdr + 0x28, (uint32_t)( 32*((endpos - startpos) / 9))); 71 | set32lsb(wavhdr + 0x18, (uint32_t)(samplerate )); 72 | set32lsb(wavhdr + 0x1C, (uint32_t)(samplerate * 2)); 73 | if(fwrite(wavhdr, 1, 0x2C, wav) != 0x2C) { goto error_wav; } 74 | 75 | for(; startpos < endpos; startpos += 9) { 76 | static const int8_t coef1[4] = { -128, -68, -6, -13 }; 77 | static const int8_t coef2[4] = { 0, 0, -15, -13 }; 78 | int32_t c1, c2; 79 | uint8_t block[9]; 80 | uint8_t shift; 81 | size_t s = fread(block, 1, 9, src); 82 | if(s != 9) { goto error_src; } 83 | shift = block[0] >> 4; 84 | c1 = coef1[(block[0] >> 2) & 3]; 85 | c2 = coef2[(block[0] >> 2) & 3]; 86 | for(s = 0; s < 16; s++) { 87 | int32_t sample = block[1 + (s >> 1)]; 88 | sample <<= 24 + 4 * (s & 1); 89 | sample >>= 28; 90 | if(shift <= 12) { 91 | sample <<= shift; 92 | sample >>= 1; 93 | } else { 94 | sample &= -0x800; 95 | } 96 | sample += (p1 << 1) + ((c1 * p1) >> 6); 97 | sample += (c2 * p2) >> 4; 98 | if(sample < -32768) { sample = -32768; } 99 | if(sample > 32767) { sample = 32767; } 100 | fputc((sample ) & 0xFF, wav); 101 | fputc((sample >> 8) & 0xFF, wav); 102 | p2 = p1; 103 | p1 = sample; 104 | } 105 | } 106 | 107 | printf("ok\n"); 108 | goto done; 109 | 110 | error_wav: printfileerror(wav, wavfilename); goto error; 111 | error_src: printfileerror(src, srcfilename); goto error; 112 | error: 113 | returncode = 1; 114 | goto done; 115 | done: 116 | if(wav) { fclose(wav); } 117 | return returncode; 118 | } 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | 122 | static int isvalidblock(const uint8_t* block) { 123 | if(block[0] >= 0xD0) { return 0; } 124 | if( (!block[1]) && (!block[2]) && (!block[3]) && (!block[4]) && 125 | (!block[5]) && (!block[6]) && (!block[7]) && (!block[8]) 126 | ) { return 0; } 127 | return 1; 128 | } 129 | 130 | //////////////////////////////////////////////////////////////////////////////// 131 | 132 | static int analyze( 133 | FILE* f, 134 | const char* filename, 135 | off_t* nsamples, 136 | off_t samplerate, 137 | off_t minblocks 138 | ) { 139 | int returncode = 0; 140 | 141 | clearerr(f); 142 | 143 | for(;;) { 144 | uint8_t block[9]; 145 | size_t s; 146 | off_t startpos, endpos; 147 | 148 | startpos = ftello(f); 149 | if(startpos == -1) { goto error_f; } 150 | 151 | // 152 | // Find a sample beginning 153 | // 154 | for(;; startpos += 9) { 155 | s = fread(block, 1, 9, f); 156 | if(s != 9) { 157 | if(ferror(f)) { goto error_f; } 158 | goto done; 159 | } 160 | if(!isvalidblock(block)) { continue; } 161 | break; 162 | } 163 | 164 | endpos = ftello(f); 165 | if(endpos == -1) { goto error_f; } 166 | 167 | // 168 | // Find a sample end 169 | // 170 | for(;; endpos += 9) { 171 | s = fread(block, 1, 9, f); 172 | if(s != 9) { 173 | if(ferror(f)) { goto error_f; } 174 | break; 175 | } 176 | if(!isvalidblock(block)) { break; } // end before this block 177 | if(block[0] & 1) { endpos += 9; break; } // end after this block 178 | } 179 | 180 | // 181 | // If the sample is long enough, write it out 182 | // 183 | if(((endpos - startpos) / 9) >= minblocks) { 184 | if(writewav(f, filename, startpos, endpos, samplerate)) { 185 | goto error; 186 | } 187 | (*nsamples)++; 188 | } 189 | 190 | if(fseeko(f, endpos, SEEK_SET) != 0) { goto error_f; } 191 | } 192 | 193 | error_f: 194 | printfileerror(f, filename); 195 | goto error; 196 | error: 197 | returncode = 1; 198 | goto done; 199 | done: 200 | return returncode; 201 | } 202 | 203 | //////////////////////////////////////////////////////////////////////////////// 204 | 205 | int main(int argc, char** argv) { 206 | int returncode = 0; 207 | off_t samplerate = 16000; 208 | off_t minblocks = 50; 209 | off_t nsamples = 0; 210 | FILE *f = NULL; 211 | int i; 212 | 213 | normalize_argv0(argv[0]); 214 | 215 | if(argc < 2) { goto usage; } 216 | if(argc >= 3) { samplerate = strtoofft(argv[2], NULL, 0); } 217 | if(argc >= 4) { minblocks = strtoofft(argv[3], NULL, 0); } 218 | if(argc >= 5) { goto usage; } 219 | 220 | f = fopen(argv[1], "rb"); 221 | if(!f) { goto error_f; } 222 | 223 | for(i = 0; i < 9; i++) { 224 | printf("Pass %d/9:\n", (int)(i + 1)); 225 | if(fseeko(f, i, SEEK_SET) != 0) { goto error_f; } 226 | if(analyze(f, argv[1], &nsamples, samplerate, minblocks)) { 227 | goto error; 228 | } 229 | } 230 | 231 | goto done; 232 | usage: 233 | banner(); 234 | printf("Usage: %s romfile [samplerate [minblocks]]\n", argv[0]); 235 | goto error; 236 | error_f: 237 | printfileerror(f, argv[1]); 238 | error: 239 | returncode = 1; 240 | goto done; 241 | done: 242 | if(nsamples) { 243 | printf("Ripped "); 244 | fprintdec(stdout, nsamples); 245 | printf(" sample%s\n", nsamples != 1 ? "s" : ""); 246 | } 247 | if(f) { fclose(f); } 248 | return returncode; 249 | } 250 | 251 | //////////////////////////////////////////////////////////////////////////////// 252 | -------------------------------------------------------------------------------- /brrrip.txt: -------------------------------------------------------------------------------- 1 | brrrip(1) 2 | ========= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | brrrip - rip SNES BRR sound samples 8 | 9 | SYNOPSIS 10 | -------- 11 | *brrrip* 'rom' ['samplerate' ['minblocks']] 12 | 13 | DESCRIPTION 14 | ----------- 15 | Automatically detects and rips sound samples from Super Nintendo 16 | Entertainment System ROM images. 17 | 18 | 'samplerate' should be given in Hz. Samples will only be detected if 19 | they are at least 'minblocks' long (default=50). The output files 20 | will be named 'hex_address'.wav. 21 | -------------------------------------------------------------------------------- /byteshuf.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "byteshuf - Shuffle or unshuffle bytes in a file" 4 | #define COPYR "Copyright (C) 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static int shuffle( 27 | int shuffling, 28 | const char* mainfile, 29 | const char** subfiles, 30 | size_t subfiles_count, 31 | int overwrite 32 | ) { 33 | int returncode = 0; 34 | size_t i; 35 | int* chars = calloc(1, sizeof(int) * subfiles_count); 36 | FILE* mf = NULL; 37 | FILE** sf = calloc(1, sizeof(FILE*) * subfiles_count); 38 | if(!sf || !chars) { 39 | printf("Error: Out of memory\n"); 40 | goto error; 41 | } 42 | if(shuffling && !overwrite) { 43 | // 44 | // Ensure main file doesn't already exist 45 | // 46 | mf = fopen(mainfile, "rb"); 47 | if(mf) { 48 | printf("Error: %s already exists (use -o to overwrite)\n", 49 | mainfile 50 | ); 51 | goto error; 52 | } 53 | } 54 | if(!shuffling && !overwrite) { 55 | // 56 | // Ensure sub files don't already exist 57 | // 58 | for(i = 0; i < subfiles_count; i++) { 59 | sf[i] = fopen(subfiles[i], "rb"); 60 | if(sf[i]) { 61 | printf("Error: %s already exists (use -o to overwrite)\n", 62 | subfiles[i] 63 | ); 64 | goto error; 65 | } 66 | } 67 | } 68 | if(shuffling) { 69 | size_t num_chars_last = subfiles_count; 70 | 71 | // 72 | // Open sub files 73 | // 74 | for(i = 0; i < subfiles_count; i++) { 75 | sf[i] = fopen(subfiles[i], "rb"); 76 | if(!sf[i]) { goto error_sf_i; } 77 | clearerr(sf[i]); 78 | } 79 | // 80 | // Open main file 81 | // 82 | mf = fopen(mainfile, "wb"); 83 | if(!mf) { goto error_mf; } 84 | clearerr(mf); 85 | 86 | for(;;) { 87 | size_t num_chars = 0; 88 | for(i = 0; i < subfiles_count; i++) { 89 | chars[i] = fgetc(sf[i]); 90 | if(chars[i] == EOF) { 91 | if(ferror(sf[i])) { goto error_sf_i; } 92 | chars[i] = 0; 93 | } else { 94 | chars[i] &= 0xFF; 95 | num_chars = i + 1; 96 | } 97 | } 98 | if(!num_chars) { break; } 99 | for(i = num_chars_last; i < subfiles_count; i++) { 100 | if(fputc(0, mf) == EOF) { goto error_mf; } 101 | } 102 | for(i = 0; i < num_chars; i++) { 103 | if(fputc(chars[i], mf) == EOF) { goto error_mf; } 104 | } 105 | num_chars_last = num_chars; 106 | } 107 | } else { 108 | // 109 | // Open main file 110 | // 111 | mf = fopen(mainfile, "rb"); 112 | if(!mf) { goto error_mf; } 113 | clearerr(mf); 114 | // 115 | // Open sub files 116 | // 117 | for(i = 0; i < subfiles_count; i++) { 118 | sf[i] = fopen(subfiles[i], "wb"); 119 | if(!sf[i]) { goto error_sf_i; } 120 | clearerr(sf[i]); 121 | } 122 | 123 | for(;;) { 124 | for(i = 0; i < subfiles_count; i++) { 125 | int c = fgetc(mf); 126 | if(c == EOF) { 127 | if(ferror(mf)) { goto error_mf; } 128 | break; 129 | } 130 | if(fputc(c & 0xFF, sf[i]) == EOF) { goto error_sf_i; } 131 | } 132 | if(i < subfiles_count) { break; } 133 | } 134 | } 135 | goto done; 136 | 137 | error_mf: 138 | printfileerror(mf, mainfile); 139 | goto error; 140 | error_sf_i: 141 | printfileerror(sf[i], subfiles[i]); 142 | goto error; 143 | error: 144 | returncode = 1; 145 | goto done; 146 | done: 147 | if(mf) { fclose(mf); } 148 | if(sf) { 149 | for(i = 0; i < subfiles_count; i++) { 150 | if(sf[i]) { fclose(sf[i]); } 151 | } 152 | free(sf); 153 | } 154 | if(chars) { free(chars); } 155 | return returncode; 156 | } 157 | 158 | //////////////////////////////////////////////////////////////////////////////// 159 | 160 | static int checkboth(int a, int b, const char* ab) { 161 | if(a && b) { 162 | printf("Error: Cannot specify both -%c and -%c\n", ab[0], ab[1]); 163 | return 1; 164 | } 165 | return 0; 166 | } 167 | 168 | static int checkeither(int a, int b, const char* ab) { 169 | if((!a) && (!b)) { 170 | printf("Error: Must specify either -%c or -%c\n", ab[0], ab[1]); 171 | return 1; 172 | } 173 | return 0; 174 | } 175 | 176 | //////////////////////////////////////////////////////////////////////////////// 177 | 178 | int main(int argc, char** argv) { 179 | int returncode = 0; 180 | struct { 181 | int8_t shuffle; 182 | int8_t unshuffle; 183 | int8_t overwrite; 184 | const char* mainfile; 185 | const char** files; 186 | size_t files_count; 187 | } opt; 188 | int i; 189 | 190 | normalize_argv0(argv[0]); 191 | 192 | memset(&opt, 0, sizeof(opt)); 193 | 194 | // 195 | // Check options 196 | // 197 | if(argc == 1) { goto usage; } 198 | for(i = 1; i < argc; i++) { 199 | if(argv[i][0] == '-') { 200 | // An option 201 | if(argv[i][1] == '-' && argv[i][2] == 0) { 202 | // No more options 203 | i++; 204 | break; 205 | } else if(argv[i][1] == 's' && argv[i][2] == 0) { 206 | if(opt.shuffle) { goto error_dup; } 207 | if(i >= (argc - 1)) { goto error_missing; } 208 | opt.shuffle = 1; 209 | opt.mainfile = argv[++i]; 210 | continue; 211 | } else if(argv[i][1] == 'u' && argv[i][2] == 0) { 212 | if(opt.unshuffle) { goto error_dup; } 213 | if(i >= (argc - 1)) { goto error_missing; } 214 | opt.unshuffle = 1; 215 | opt.mainfile = argv[++i]; 216 | continue; 217 | } else if(argv[i][1] == 'o' && argv[i][2] == 0) { 218 | opt.overwrite = 1; 219 | continue; 220 | } 221 | printf("Error: Unknown option: %s\n", argv[i]); 222 | goto error_usage; 223 | } else { 224 | // Not an option - stop here 225 | break; 226 | } 227 | } 228 | 229 | if(checkeither(opt.shuffle, opt.unshuffle, "su")) { goto error_usage; } 230 | if(checkboth (opt.shuffle, opt.unshuffle, "su")) { goto error_usage; } 231 | // 232 | // At least two files must be specified 233 | // 234 | opt.files = (const char**)(argv + i); 235 | opt.files_count = (argc - i); 236 | if(opt.files_count < 2) { 237 | printf("Error: Must specify at least two subfiles\n"); 238 | goto error_usage; 239 | } 240 | 241 | // 242 | // Go 243 | // 244 | if(opt.shuffle) { 245 | returncode = shuffle( 246 | 1, opt.mainfile, opt.files, opt.files_count, opt.overwrite 247 | ); 248 | } else if(opt.unshuffle) { 249 | returncode = shuffle( 250 | 0, opt.mainfile, opt.files, opt.files_count, opt.overwrite 251 | ); 252 | } 253 | goto done; 254 | 255 | error_dup: 256 | printf("Error: Specified %s twice\n", argv[i]); 257 | goto error_usage; 258 | error_missing: 259 | printf("Error: Missing parameter for %s\n", argv[i]); 260 | goto error_usage; 261 | error_usage: 262 | printf("\n"); 263 | goto usage; 264 | usage: 265 | banner(); 266 | printf( 267 | "Usage:\n" 268 | " To unshuffle: %s [-o] -u source [subfiles...]\n" 269 | " To shuffle: %s [-o] -s destination [subfiles...]\n" 270 | "\n" 271 | "Options:\n" 272 | " -o Force overwrite\n" 273 | "\n" 274 | "For example, \"%s -u abc def0 def1\" will split all the even bytes from\n" 275 | "\"abc\" into \"def0\", and the odd bytes into \"def1\".\n", 276 | argv[0], argv[0], argv[0] 277 | ); 278 | goto error; 279 | 280 | error: 281 | returncode = 1; 282 | goto done; 283 | 284 | done: 285 | return returncode; 286 | } 287 | 288 | //////////////////////////////////////////////////////////////////////////////// 289 | -------------------------------------------------------------------------------- /byteshuf.txt: -------------------------------------------------------------------------------- 1 | byteshuf(1) 2 | =========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | byteshuf - Shuffle or unshuffle bytes in a file 8 | 9 | SYNOPSIS 10 | -------- 11 | *byteshuf* ['-o'] '-u' 'source' ['subfiles'...] 12 | 13 | *byteshuf* ['-o'] '-s' 'destination' ['subfiles'...] 14 | 15 | DESCRIPTION 16 | ----------- 17 | Divides a file into its interleaved even/odd bytes, or recombines 18 | them. Can also divide every third, fourth, etc byte, depending on how 19 | many subfiles you specify. Useful for dealing with certain types of 20 | ROM images. 21 | 22 | OPTION 23 | ------ 24 | *-o*:: Force overwrite of files 25 | -------------------------------------------------------------------------------- /cdpatch.txt: -------------------------------------------------------------------------------- 1 | cdpatch(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | cdpatch - CD-XA image insert/extract utility 8 | 9 | SYNOPSIS 10 | -------- 11 | 'Insertion'::: 12 | *cdpatch* '-i' 'imagefile' ['-be'|'-boot'|'-d' 'dir'|'-f'|'-le'|'-o'|'-r'|'-v'] 13 | 14 | 'Extraction'::: 15 | *cdpatch* '-x' 'imagefile' ['-be'|'-boot'|'-d' 'dir'|'-f'|'-le'|'-o'|'-r'|'-v'] 16 | 17 | DESCRIPTION 18 | ----------- 19 | Inserts or extracts files in-place into CD-ROM images in either .BIN 20 | or .ISO format. Properly handles CD-XA streams from Mode 2 CDs (video 21 | CDs, PlayStation CDs, etc) and patches EDC/ECC data as appropriate. 22 | 23 | By default, cdpatch will attempt to insert or extract every file in 24 | the CD image, the same as if you'd specified '-r .'. The '-boot' 25 | option will insert or extract the 32KiB boot sector to a file named 26 | 'boot'. 27 | 28 | Some CD images may have conflicting sets big- and little-endian 29 | metadata as a platform-specific quirk or copy protection scheme; the 30 | '-be' and '-le' options can be used to override this. 31 | 32 | Specifying both '-f' and '-v' will list all file system errors, while 33 | still ignoring them for extraction purposes. 34 | 35 | When extracting files with '-x', cdpatch preserves file modification 36 | times when possible. 37 | 38 | OPTIONS 39 | ------- 40 | *-be*:: Favor big-endian values in ISO-9660 metadata. 41 | 42 | *-le*:: Favor little-endian values in ISO-9660 metadata. 43 | 44 | *-boot*:: Insert or extract the boot area 45 | 46 | *-d dir*:: Set the base directory for inserted or extracted files 47 | (defaults to .) 48 | 49 | *-f*:: Skip file system consistency checks 50 | 51 | *-o*:: Force overwrite when extracting files 52 | 53 | *-r*:: Recurse into subdirectories 54 | 55 | *-v*:: Verbose 56 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef __CMDPACK_COMMON_H__ 2 | #define __CMDPACK_COMMON_H__ 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // 6 | // Common headers for Command-Line Pack programs 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | // 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | // Disable fopen() warnings on VC++. It means well... 24 | #define _CRT_SECURE_NO_WARNINGS 25 | 26 | // Try to enable 64-bit file offsets on platforms where it's optional 27 | #define _LARGEFILE64_SOURCE 1 28 | #define __USE_FILE_OFFSET64 1 29 | #define __USE_LARGEFILE64 1 30 | #define _FILE_OFFSET_BITS 64 31 | 32 | // Try to enable long filename support on Watcom 33 | #define __WATCOM_LFN__ 1 34 | 35 | // Convince MinGW that we want to glob arguments 36 | #ifdef __MINGW32__ 37 | int _dowildcard = -1; 38 | #endif 39 | 40 | //////////////////////////////////////////////////////////////////////////////// 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | // MSC toolchains use sys/utime.h; everything else uses utime.h 54 | #if defined(_MSC_VER) 55 | #include 56 | #else 57 | #include 58 | #endif 59 | 60 | // Try to bring in unistd.h if possible 61 | #if !defined(__TURBOC__) && !defined(_MSC_VER) 62 | #include 63 | #endif 64 | 65 | // Bring in direct.h if we need to; sometimes mkdir/rmdir is defined here 66 | #if defined(__WATCOMC__) || defined(_MSC_VER) 67 | #include 68 | #endif 69 | 70 | // Fill in S_ISDIR 71 | #if !defined(_POSIX_VERSION) && !defined(S_ISDIR) 72 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 73 | #endif 74 | 75 | #if defined(__TURBOC__) || defined(__WATCOMC__) || defined(__MINGW32__) || defined(_MSC_VER) 76 | // 77 | // Already have a single-argument mkdir() 78 | // 79 | #else 80 | // 81 | // Provide a single-argument mkdir() 82 | // 83 | #define mkdir(a) mkdir(a, S_IRWXU | S_IRWXG | S_IRWXO) 84 | #endif 85 | 86 | //////////////////////////////////////////////////////////////////////////////// 87 | // 88 | // Enforce large memory model for 16-bit DOS targets 89 | // 90 | #if defined(__MSDOS__) || defined(MSDOS) 91 | #if defined(__TURBOC__) || defined(__WATCOMC__) 92 | #if !defined(__LARGE__) 93 | #error This is not the memory model we should be using! 94 | #endif 95 | #endif 96 | #endif 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | // 100 | // Try to figure out integer types 101 | // 102 | #if defined(_STDINT_H) || defined(_EXACT_WIDTH_INTS) 103 | 104 | // _STDINT_H_ - presume stdint.h has already been included 105 | // _EXACT_WIDTH_INTS - OpenWatcom already provides int*_t in sys/types.h 106 | 107 | #elif defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L 108 | 109 | // Assume C99 compliance when the compiler specifically tells us it is 110 | #include 111 | 112 | #elif defined(_MSC_VER) 113 | 114 | // On Visual Studio, use its integral types 115 | typedef signed __int8 int8_t; 116 | typedef unsigned __int8 uint8_t; 117 | typedef signed __int16 int16_t; 118 | typedef unsigned __int16 uint16_t; 119 | typedef signed __int32 int32_t; 120 | typedef unsigned __int32 uint32_t; 121 | 122 | #else 123 | 124 | // Guess integer sizes from limits.h 125 | 126 | // 127 | // int8_t 128 | // 129 | #ifndef __int8_t_defined 130 | #if SCHAR_MIN == -128 && SCHAR_MAX == 127 && UCHAR_MAX == 255 131 | typedef signed char int8_t; 132 | #else 133 | #error Unknown how to define int8_t! 134 | #endif 135 | #endif 136 | 137 | // 138 | // uint8_t 139 | // 140 | #ifndef __uint8_t_defined 141 | #if SCHAR_MIN == -128 && SCHAR_MAX == 127 && UCHAR_MAX == 255 142 | typedef unsigned char uint8_t; 143 | #else 144 | #error Unknown how to define uint8_t! 145 | #endif 146 | #endif 147 | 148 | // 149 | // int16_t 150 | // 151 | #ifndef __int16_t_defined 152 | #if SHRT_MIN == -32768 && SHRT_MAX == 32767 && USHRT_MAX == 65535 153 | typedef signed short int16_t; 154 | #else 155 | #error Unknown how to define int16_t! 156 | #endif 157 | #endif 158 | 159 | // 160 | // uint16_t 161 | // 162 | #ifndef __uint16_t_defined 163 | #if SHRT_MIN == -32768 && SHRT_MAX == 32767 && USHRT_MAX == 65535 164 | typedef unsigned short uint16_t; 165 | #else 166 | #error Unknown how to define uint16_t! 167 | #endif 168 | #endif 169 | 170 | // 171 | // int32_t 172 | // 173 | #ifndef __int32_t_defined 174 | #if INT_MIN == -2147483648 && INT_MAX == 2147483647 && UINT_MAX == 4294967295 175 | typedef signed int int32_t; 176 | #elif LONG_MIN == -2147483648 && LONG_MAX == 2147483647 && ULONG_MAX == 4294967295 177 | typedef signed long int32_t; 178 | #else 179 | #error Unknown how to define int32_t! 180 | #endif 181 | #endif 182 | 183 | // 184 | // uint32_t 185 | // 186 | #ifndef __uint32_t_defined 187 | #if INT_MIN == -2147483648 && INT_MAX == 2147483647 && UINT_MAX == 4294967295 188 | typedef unsigned int uint32_t; 189 | #elif LONG_MIN == -2147483648 && LONG_MAX == 2147483647 && ULONG_MAX == 4294967295 190 | typedef unsigned long uint32_t; 191 | #else 192 | #error Unknown how to define uint32_t! 193 | #endif 194 | #endif 195 | 196 | #endif 197 | 198 | // 199 | // There are some places in the code where it's assumed 'long' can hold at least 200 | // 32 bits. Verify that here: 201 | // 202 | #if LONG_MAX < 2147483647 || ULONG_MAX < 4294967295 203 | #error long type must be at least 32 bits! 204 | #endif 205 | 206 | //////////////////////////////////////////////////////////////////////////////// 207 | // 208 | // Figure out how big file offsets should be 209 | // 210 | #if defined(_OFF64_T_) || defined(_OFF64_T_DEFINED) || defined(__off64_t_defined) 211 | // 212 | // We have off64_t 213 | // Regular off_t may be smaller, so check this first 214 | // 215 | 216 | #ifdef off_t 217 | #undef off_t 218 | #endif 219 | #ifdef fseeko 220 | #undef fseeko 221 | #endif 222 | #ifdef ftello 223 | #undef ftello 224 | #endif 225 | 226 | #define off_t off64_t 227 | #define fseeko fseeko64 228 | #define ftello ftello64 229 | 230 | #elif defined(_OFF_T) || defined(__OFF_T_TYPE) || defined(__off_t_defined) || defined(_OFF_T_DEFINED_) 231 | // 232 | // We have off_t 233 | // 234 | 235 | #else 236 | // 237 | // Assume offsets are just 'long' 238 | // 239 | #ifdef off_t 240 | #undef off_t 241 | #endif 242 | #ifdef fseeko 243 | #undef fseeko 244 | #endif 245 | #ifdef ftello 246 | #undef ftello 247 | #endif 248 | 249 | #define off_t long 250 | #define fseeko fseek 251 | #define ftello ftell 252 | 253 | #endif 254 | 255 | // 256 | // Add the ability to read off_t 257 | // (assumes off_t is a signed type) 258 | // 259 | off_t strtoofft(const char* s_start, char** endptr, int base) { 260 | off_t max = 261 | ((((off_t)1) << ((sizeof(off_t)*8)-2)) - 1) + 262 | ((((off_t)1) << ((sizeof(off_t)*8)-2)) ); 263 | off_t min = ((-1) - max); 264 | const char* s = s_start; 265 | off_t accumulator; 266 | off_t limit_tens; 267 | off_t limit_ones; 268 | int c; 269 | int negative = 0; 270 | int anyinput; 271 | do { 272 | c = *s++; 273 | } while(isspace(c)); 274 | if(c == '-') { 275 | negative = 1; 276 | c = *s++; 277 | } else if (c == '+') { 278 | c = *s++; 279 | } 280 | if( 281 | (base == 0 || base == 16) && 282 | c == '0' && (*s == 'x' || *s == 'X') 283 | ) { 284 | c = s[1]; 285 | s += 2; 286 | base = 16; 287 | } 288 | if(!base) { 289 | base = (c == '0') ? 8 : 10; 290 | } 291 | limit_ones = max % ((off_t)base); 292 | limit_tens = max / ((off_t)base); 293 | if(negative) { 294 | limit_ones++; 295 | if(limit_ones >= base) { limit_ones = 0; limit_tens++; } 296 | } 297 | for(accumulator = 0, anyinput = 0;; c = *s++) { 298 | if(isdigit(c)) { 299 | c -= '0'; 300 | } else if(isalpha(c)) { 301 | c -= isupper(c) ? 'A' - 10 : 'a' - 10; 302 | } else { 303 | break; 304 | } 305 | if(c >= base) { break; } 306 | if( 307 | (anyinput < 0) || 308 | (accumulator < 0) || 309 | (accumulator > limit_tens) || 310 | (accumulator == limit_tens && c > limit_ones) 311 | ) { 312 | anyinput = -1; 313 | } else { 314 | anyinput = 1; 315 | accumulator *= base; 316 | accumulator += c; 317 | } 318 | } 319 | if(anyinput < 0) { 320 | accumulator = negative ? min : max; 321 | errno = ERANGE; 322 | } else if(negative) { 323 | accumulator = -accumulator; 324 | } 325 | if(endptr) { 326 | *endptr = (char*)(anyinput ? (char*)s - 1 : s_start); 327 | } 328 | return accumulator; 329 | } 330 | 331 | // 332 | // Add the ability to print off_t 333 | // 334 | void fprinthex(FILE* f, off_t off, int min_digits) { 335 | unsigned anydigit = 0; 336 | int place; 337 | for(place = 2 * sizeof(off_t) - 1; place >= 0; place--) { 338 | if(sizeof(off_t) > (((size_t)(place)) / 2)) { 339 | unsigned digit = (off >> (4 * place)) & 0xF; 340 | anydigit |= digit; 341 | if(anydigit || place < min_digits) { 342 | fputc("0123456789ABCDEF"[digit], f); 343 | } 344 | } 345 | } 346 | } 347 | 348 | static void fprintdec_digit(FILE* f, off_t off) { 349 | if(off == 0) { return; } 350 | if(off >= 10) { 351 | fprintdec_digit(f, off / ((off_t)10)); 352 | off %= ((off_t)10); 353 | } 354 | fputc('0' + off, f); 355 | } 356 | 357 | void fprintdec(FILE* f, off_t off) { 358 | if(off == 0) { 359 | fputc('0', f); 360 | return; 361 | } 362 | if(off < 0) { 363 | fputc('-', f); 364 | off = -off; 365 | if(off < 0) { 366 | off_t ones = off % ((off_t)10); 367 | off /= ((off_t)10); 368 | off = -off; 369 | fprintdec_digit(f, off); 370 | fputc('0' - ones, f); 371 | return; 372 | } 373 | } 374 | fprintdec_digit(f, off); 375 | } 376 | 377 | //////////////////////////////////////////////////////////////////////////////// 378 | // 379 | // Define truncate() for systems that don't have it 380 | // 381 | #if !defined(_POSIX_VERSION) 382 | 383 | #if (defined(__MSDOS__) || defined(MSDOS)) && (defined(__TURBOC__) || defined(__WATCOMC__)) 384 | 385 | #include 386 | #include 387 | #include 388 | int truncate(const char *filename, off_t size) { 389 | if(size < 0) { 390 | errno = EINVAL; 391 | return -1; 392 | } 393 | // 394 | // Extend (or do nothing) if necessary 395 | // 396 | { off_t end; 397 | FILE* f = fopen(filename, "rb"); 398 | if(!f) { 399 | return -1; 400 | } 401 | if(fseeko(f, 0, SEEK_END) != 0) { 402 | fclose(f); 403 | return -1; 404 | } 405 | end = ftello(f); 406 | if(end <= size) { 407 | for(; end < size; end++) { 408 | if(fputc(0, f) == EOF) { 409 | fclose(f); 410 | return -1; 411 | } 412 | } 413 | fclose(f); 414 | return 0; 415 | } 416 | fclose(f); 417 | } 418 | // 419 | // Shrink if necessary (DOS-specific call) 420 | // 421 | { int doshandle = 0; 422 | unsigned nwritten = 0; 423 | if(_dos_open(filename, O_WRONLY, &doshandle)) { 424 | return -1; 425 | } 426 | if(lseek(doshandle, size, SEEK_SET) == -1L) { 427 | _dos_close(doshandle); 428 | return -1; 429 | } 430 | if(_dos_write(doshandle, &doshandle, 0, &nwritten)) { 431 | _dos_close(doshandle); 432 | return -1; 433 | } 434 | _dos_close(doshandle); 435 | } 436 | // 437 | // Success 438 | // 439 | return 0; 440 | } 441 | 442 | #elif (defined(_WIN32) && defined(_MSC_VER)) 443 | 444 | #if defined(_MSC_VER) 445 | // Disable extension warnings for and friends 446 | #pragma warning (disable: 4226) 447 | #endif 448 | 449 | #include 450 | 451 | #ifndef INVALID_SET_FILE_POINTER 452 | #define INVALID_SET_FILE_POINTER ((DWORD)(-1)) 453 | #endif 454 | 455 | int truncate(const char *filename, off_t size) { 456 | if(size < 0) { 457 | errno = EINVAL; 458 | return -1; 459 | } 460 | // 461 | // Extend (or do nothing) if necessary 462 | // 463 | { off_t end; 464 | FILE* f = fopen(filename, "rb"); 465 | if(!f) { 466 | return -1; 467 | } 468 | if(fseeko(f, 0, SEEK_END) != 0) { 469 | fclose(f); 470 | return -1; 471 | } 472 | end = ftello(f); 473 | if(end <= size) { 474 | for(; end < size; end++) { 475 | if(fputc(0, f) == EOF) { 476 | fclose(f); 477 | return -1; 478 | } 479 | } 480 | fclose(f); 481 | return 0; 482 | } 483 | fclose(f); 484 | } 485 | // 486 | // Shrink if necessary (Windows-specific call) 487 | // 488 | { HANDLE f = CreateFile( 489 | filename, 490 | GENERIC_WRITE, 491 | 0, 492 | NULL, 493 | OPEN_EXISTING, 494 | FILE_ATTRIBUTE_NORMAL, 495 | NULL 496 | ); 497 | if(f == INVALID_HANDLE_VALUE) { 498 | return -1; 499 | } 500 | if(size > ((off_t)0x7FFFFFFFL)) { 501 | // use fancy 64-bit SetFilePointer 502 | LONG lo = size; 503 | LONG hi = size >> 32; 504 | if(SetFilePointer(f, lo, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { 505 | CloseHandle(f); 506 | return -1; 507 | } 508 | } else { 509 | // use plain 32-bit SetFilePointer 510 | if(SetFilePointer(f, size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { 511 | CloseHandle(f); 512 | return -1; 513 | } 514 | } 515 | if(!SetEndOfFile(f)) { 516 | CloseHandle(f); 517 | return -1; 518 | } 519 | } 520 | // 521 | // Success 522 | // 523 | return 0; 524 | } 525 | 526 | #endif 527 | 528 | #endif // !defined(_POSIX_VERSION) 529 | 530 | //////////////////////////////////////////////////////////////////////////////// 531 | // 532 | // Normalize argv[0] 533 | // 534 | void normalize_argv0(char* argv0) { 535 | size_t i; 536 | size_t start = 0; 537 | int c; 538 | for(i = 0; argv0[i]; i++) { 539 | if(argv0[i] == '/' || argv0[i] == '\\') { 540 | start = i + 1; 541 | } 542 | } 543 | i = 0; 544 | do { 545 | c = ((unsigned char)(argv0[start + i])); 546 | if(c == '.') { c = 0; } 547 | if(c != 0) { c = tolower(c); } 548 | argv0[i++] = c; 549 | } while(c != 0); 550 | } 551 | 552 | //////////////////////////////////////////////////////////////////////////////// 553 | 554 | void printfileerror(FILE* f, const char* name) { 555 | int e = errno; 556 | printf("Error: "); 557 | if(name) { printf("%s: ", name); } 558 | printf("%s\n", f && feof(f) ? "Unexpected end-of-file" : strerror(e)); 559 | } 560 | 561 | //////////////////////////////////////////////////////////////////////////////// 562 | 563 | #if defined(_WIN32) 564 | 565 | // 566 | // Detect if the user double-clicked on the .exe rather than executing this from 567 | // the command line, and if so, display a warning and wait for input before 568 | // exiting 569 | // 570 | #include 571 | 572 | static HWND getconsolewindow(void) { 573 | HWND hConsoleWindow = NULL; 574 | HANDLE k32; 575 | // 576 | // See if GetConsoleWindow is available (Windows 2000 or later) 577 | // 578 | k32 = GetModuleHandle(TEXT("kernel32.dll")); 579 | if(k32) { 580 | typedef HWND (* WINAPI gcw_t)(void); 581 | gcw_t gcw = (gcw_t)GetProcAddress(k32, TEXT("GetConsoleWindow")); 582 | if(gcw) { 583 | hConsoleWindow = gcw(); 584 | } 585 | } 586 | // 587 | // There is an alternative method that involves FindWindow, but it's too 588 | // cumbersome for just printing a warning. 589 | // 590 | return hConsoleWindow; 591 | } 592 | 593 | void commandlinewarning(void) { 594 | HWND hConsoleWindow; 595 | DWORD processId = 0; 596 | // 597 | // This trick doesn't work in Win9x 598 | // 599 | if(GetVersion() >= ((DWORD)0x80000000LU)) { return; } 600 | // 601 | // See if the console window belongs to my own process 602 | // 603 | hConsoleWindow = getconsolewindow(); 604 | if(!hConsoleWindow) { return; } 605 | GetWindowThreadProcessId(hConsoleWindow, &processId); 606 | if(GetCurrentProcessId() == processId) { 607 | printf( 608 | "\n" 609 | "Note: This is a command-line application.\n" 610 | "It was meant to run from a Windows command prompt.\n\n" 611 | "Press ENTER to close this window..." 612 | ); 613 | fflush(stdout); 614 | fgetc(stdin); 615 | } 616 | } 617 | 618 | #else 619 | 620 | void commandlinewarning(void) {} 621 | 622 | #endif 623 | 624 | //////////////////////////////////////////////////////////////////////////////// 625 | // 626 | // Work around some problems with the Mariko CC toolchain 627 | // 628 | #ifdef MARIKO_CC 629 | 630 | // 32-bit signed and unsigned mod seem buggy; this solves it 631 | unsigned long __umodsi3(unsigned long a, unsigned long b) { return a - (a / b) * b; } 632 | signed long __modsi3(signed long a, signed long b) { return a - (a / b) * b; } 633 | 634 | // Some kind of soft float linkage issue? 635 | void __cmpdf2(void) {} 636 | 637 | #endif 638 | 639 | //////////////////////////////////////////////////////////////////////////////// 640 | 641 | #endif 642 | -------------------------------------------------------------------------------- /fakecrc.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "fakecrc - Fake the CRC32 of a file" 4 | #define COPYR "Copyright (C) 2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static uint32_t crctable_f[256]; 27 | static uint32_t crctable_r[256]; 28 | 29 | static void crc_init(void) { 30 | uint32_t i, j, k; 31 | for(i = 0; i < 256; i++) { 32 | j = i; 33 | for(k = 0; k < 8; k++) { 34 | j = (j >> 1) ^ ((j & 1) ? 0x80000000 : 0x6DB88320); 35 | } 36 | crctable_f[i] = j; 37 | j = i << 24; 38 | for(k = 0; k < 8; k++) { 39 | j = (j << 1) ^ ((((int32_t)j) < 0) ? 0x00000001 : 0xDB710640); 40 | } 41 | crctable_r[i] = j; 42 | } 43 | } 44 | 45 | static uint32_t crc_nextbyte(uint32_t crc, uint8_t byte) { 46 | return (crc >> 8) ^ crctable_f[(crc ^ byte) & 0xFF]; 47 | } 48 | 49 | static uint32_t crc_prevbyte(uint32_t crc, uint8_t byte) { 50 | return (crc << 8) ^ byte ^ crctable_r[crc >> 24]; 51 | } 52 | 53 | //////////////////////////////////////////////////////////////////////////////// 54 | // 55 | // Compute CRC forward for a section of a file 56 | // 57 | static uint8_t crc_buffer[4096]; 58 | 59 | // Returns nonzero on error 60 | static int crc_f(uint32_t crc, FILE* f, off_t start, off_t end, uint32_t* result) { 61 | if(start < 0) { printf("error - start is negative\n"); return 1; } 62 | if(end < 0) { printf("error - end is negative\n"); return 1; } 63 | if(start > end) { printf("error - start > end\n"); return 1; } 64 | 65 | if(fseeko(f, start, SEEK_SET) != 0) { 66 | printf("seek error\n"); 67 | return 1; 68 | } 69 | while(start < end) { 70 | off_t i; 71 | off_t diff = end - start; 72 | if(diff > ((off_t)sizeof(crc_buffer))) { diff = sizeof(crc_buffer); } 73 | 74 | if(fread(crc_buffer, 1, (size_t)diff, f) != (size_t)diff) { 75 | printf("read error\n"); 76 | return 1; 77 | } 78 | for(i = 0; i < diff; i++) { 79 | crc = crc_nextbyte(crc, crc_buffer[i]); 80 | } 81 | 82 | start += diff; 83 | } 84 | if(result) { *result = crc; } 85 | return 0; 86 | } 87 | 88 | // 89 | // Compute CRC reverse for a section of a file 90 | // 91 | // Returns nonzero on error 92 | static int crc_r(uint32_t crc, FILE* f, off_t start, off_t end, uint32_t* result) { 93 | if(start < 0) { printf("error - start is negative\n"); return 1; } 94 | if(end < 0) { printf("error - end is negative\n"); return 1; } 95 | if(start > end) { printf("error - start > end\n"); return 1; } 96 | 97 | while(start < end) { 98 | off_t i; 99 | off_t diff = end - start; 100 | if(diff > ((off_t)sizeof(crc_buffer))) { diff = sizeof(crc_buffer); } 101 | end -= diff; 102 | 103 | if(fseeko(f, end, SEEK_SET) != 0) { 104 | printf("seek error\n"); 105 | return 1; 106 | } 107 | if(fread(crc_buffer, 1, (size_t)diff, f) != (size_t)diff) { 108 | printf("read error\n"); 109 | return 1; 110 | } 111 | 112 | for(i = diff; i > 0; i--) { 113 | crc = crc_prevbyte(crc, crc_buffer[i - 1]); 114 | } 115 | } 116 | if(result) { *result = crc; } 117 | return 0; 118 | } 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | // 122 | // Returns the 4 data bytes necessary to go from current->desired 123 | // 124 | static uint32_t crc32_preimage(uint32_t current_crc, uint32_t desired_crc) { 125 | uint32_t x = desired_crc ^ 0x2144DF1Clu; 126 | return current_crc ^ 127 | ( x & 0x47FF01FFlu) ^ (((x<< 1)|(x>>31)) & 0x8F0003C1lu) ^ 128 | (((x<< 2)|(x>>30)) & 0xFE0FFE03lu) ^ (((x<< 3)|(x>>29)) & 0x3FE003C0lu) ^ 129 | (((x<< 4)|(x>>28)) & 0x7830FE00lu) ^ (((x<< 5)|(x>>27)) & 0xF01F03DFlu) ^ 130 | (((x<< 6)|(x>>26)) & 0x00C001C0lu) ^ (((x<< 7)|(x>>25)) & 0x3E00FC40lu) ^ 131 | (((x<< 8)|(x>>24)) & 0x7F0FFEC0lu) ^ (((x<< 9)|(x>>23)) & 0xF9EF03FFlu) ^ 132 | (((x<<10)|(x>>22)) & 0x0FDFFE00lu) ^ (((x<<11)|(x>>21)) & 0x07CF05C0lu) ^ 133 | (((x<<12)|(x>>20)) & 0x30E00C00lu) ^ (((x<<13)|(x>>19)) & 0x7ECF0000lu) ^ 134 | (((x<<14)|(x>>18)) & 0xC1E001FFlu) ^ (((x<<15)|(x>>17)) & 0x78CF03C0lu) ^ 135 | (((x<<16)|(x>>16)) & 0xF61FFFFFlu) ^ (((x<<17)|(x>>15)) & 0x0F310000lu) ^ 136 | (((x<<18)|(x>>14)) & 0x39E0FE00lu) ^ (((x<<19)|(x>>13)) & 0x4F37FC00lu) ^ 137 | (((x<<20)|(x>>12)) & 0x8610003Flu) ^ (((x<<21)|(x>>11)) & 0xCF30FFFFlu) ^ 138 | (((x<<22)|(x>>10)) & 0x79EFFFC0lu) ^ (((x<<23)|(x>> 9)) & 0xCF2001FFlu) ^ 139 | (((x<<24)|(x>> 8)) & 0x793FFC00lu) ^ (((x<<25)|(x>> 7)) & 0xCF0F01FFlu) ^ 140 | (((x<<26)|(x>> 6)) & 0x7AE003C0lu) ^ (((x<<27)|(x>> 5)) & 0xC9C0003Flu) ^ 141 | (((x<<28)|(x>> 4)) & 0x78000000lu) ^ (((x<<29)|(x>> 3)) & 0xD70FFE3Flu) ^ 142 | (((x<<30)|(x>> 2)) & 0x4E1F0200lu) ^ (((x<<31)|(x>> 1)) & 0xC03FFC3Flu); 143 | } 144 | 145 | //////////////////////////////////////////////////////////////////////////////// 146 | 147 | int main( 148 | int argc, 149 | char **argv 150 | ) { 151 | int returncode = 0; 152 | 153 | FILE *f = NULL; 154 | uint32_t i; 155 | uint32_t desired_crc = 0; 156 | 157 | off_t file_size; 158 | off_t patch_offset; 159 | 160 | uint32_t pre_crc = 0; 161 | uint32_t post_crc = 0; 162 | uint32_t preimage = 0; 163 | 164 | uint8_t initial_bytes[4]; 165 | uint8_t patched_bytes[4]; 166 | 167 | normalize_argv0(argv[0]); 168 | 169 | crc_init(); 170 | 171 | if(argc != 2 && argc != 3 && argc != 4) { 172 | banner(); 173 | printf( 174 | "Usage:\n" 175 | "To obtain the CRC32 of a file:\n" 176 | " %s file\n" 177 | "To modify the CRC32 of a file:\n" 178 | " %s file desired_crc [offset]\n" 179 | "Patches 4 consecutive bytes at the given offset to force the file's CRC32.\n" 180 | "If no offset is given, the last 4 bytes of the file are used.\n", 181 | argv[0], 182 | argv[0] 183 | ); 184 | goto error; 185 | } 186 | 187 | if(argc == 2) { 188 | // 189 | // Just get the file's CRC 190 | // 191 | f = fopen(argv[1], "rb"); 192 | if(!f) { goto error_f; } 193 | 194 | if(fseeko(f, 0, SEEK_END) != 0) { goto error_f; } 195 | file_size = ftello(f); 196 | if(fseeko(f, 0, SEEK_SET) != 0) { goto error_f; } 197 | 198 | if(crc_f(0, f, 0, file_size, &pre_crc)) { goto error; } 199 | 200 | printf("0x%08lX\n", (unsigned long)pre_crc); 201 | goto done; 202 | } 203 | 204 | if(argc >= 3) { 205 | desired_crc = strtoul(argv[2], NULL, 0); 206 | } 207 | 208 | f = fopen(argv[1], "r+b"); 209 | if(!f) { goto error_f; } 210 | 211 | if(fseeko(f, 0, SEEK_END) != 0) { goto error_f; } 212 | file_size = ftello(f); 213 | if(fseeko(f, 0, SEEK_SET) != 0) { goto error_f; } 214 | 215 | if(file_size < 4) { 216 | printf("error: file must be at least 4 bytes\n"); 217 | goto error; 218 | } 219 | patch_offset = file_size - 4; 220 | if(argc >= 4) { 221 | patch_offset = strtoofft(argv[3], NULL, 0); 222 | if(patch_offset < 0) { 223 | printf("patch offset must not be negative\n"); 224 | goto error; 225 | } 226 | } 227 | if(patch_offset > (file_size - 4)) { 228 | printf("offset 0x"); 229 | fprinthex(stdout, patch_offset, 1); 230 | printf(" is out of range of the file\n"); 231 | goto error; 232 | } 233 | 234 | // 235 | // Compute pre- and post-crc 236 | // 237 | if(crc_f(0x00000000 , f, 0 , patch_offset, &pre_crc )) { goto error; } 238 | if(crc_r(desired_crc, f, patch_offset + 4, file_size , &post_crc)) { goto error; } 239 | 240 | // 241 | // Compute preimage from pre to post 242 | // 243 | preimage = crc32_preimage(pre_crc, post_crc); 244 | 245 | patched_bytes[0] = (uint8_t)(preimage >> 0); 246 | patched_bytes[1] = (uint8_t)(preimage >> 8); 247 | patched_bytes[2] = (uint8_t)(preimage >> 16); 248 | patched_bytes[3] = (uint8_t)(preimage >> 24); 249 | // 250 | // Write preimage at the patch offset 251 | // 252 | if(fseeko(f, patch_offset, SEEK_SET) != 0) { goto error_f; } 253 | if(fread (initial_bytes, 1, 4, f) != 4) { goto error_f; } 254 | if(fseeko(f, patch_offset, SEEK_SET) != 0) { goto error_f; } 255 | if(fwrite(patched_bytes, 1, 4, f) != 4) { goto error_f; } 256 | fflush(f); 257 | // 258 | // Done 259 | // 260 | for(i = 0; i < 4; i++) { 261 | printf("0x"); 262 | fprinthex(stdout, patch_offset + i, 1); 263 | printf(": 0x%02X -> 0x%02X\n", initial_bytes[i], patched_bytes[i]); 264 | } 265 | printf("Done! CRC32 is now 0x%08lX\n", (unsigned long)desired_crc); 266 | 267 | goto done; 268 | 269 | error_f: 270 | printfileerror(f, argv[1]); 271 | error: 272 | returncode = 1; 273 | 274 | done: 275 | if(f != NULL) { fclose(f); } 276 | return returncode; 277 | } 278 | 279 | //////////////////////////////////////////////////////////////////////////////// 280 | -------------------------------------------------------------------------------- /fakecrc.txt: -------------------------------------------------------------------------------- 1 | fakecrc(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | fakecrc - fake the CRC32 of a file 8 | 9 | SYNOPSIS 10 | -------- 11 | *fakecrc* 'file' ['desired_crc32' ['offset']] 12 | 13 | DESCRIPTION 14 | ----------- 15 | Modifies the file to force it to have a particular CRC32 checksum, by 16 | patching four consecutive bytes anywhere in the file. Can also obtain 17 | the current CRC32 checksum of a file. 18 | -------------------------------------------------------------------------------- /hax65816.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "hax65816 - Simple 65816 disassembler" 4 | #define COPYR "Copyright (C) 1998,2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static char aluop[8][4] = 27 | {"ora", "and", "eor", "adc", "sta", "lda", "cmp", "sbc" }; 28 | static char rmwop[8][4] = 29 | {"asl", "rol", "lsr", "ror", "???", "???", "dec", "inc" }; 30 | 31 | static int8_t mflag_default = 1; 32 | static int8_t xflag_default = 0; 33 | static int8_t flag_return = 1; 34 | static int8_t flag_guess = 1; 35 | static int8_t flag_follow = 1; 36 | 37 | static uint32_t dasm_address, ins_address; 38 | static int8_t mflag, xflag; 39 | 40 | static int getbyte(void); 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | static int fetchlist[10]; 45 | static int unfetchstack[10]; 46 | static int fetchn, unfetchn; 47 | 48 | static int fetchbyte(void) { 49 | int b; 50 | dasm_address++; 51 | if(unfetchn) b = unfetchstack[--unfetchn]; else b = getbyte(); 52 | fetchlist[fetchn++] = b; 53 | return b; 54 | } 55 | 56 | static void unfetchbyte(int b) { 57 | dasm_address--; 58 | fetchn--; 59 | if(fetchn < 0) fetchn = 0; 60 | unfetchstack[unfetchn++] = b; 61 | } 62 | 63 | enum { INS_COLUMN = 23 }; 64 | 65 | static void ins(const char* fmt, ...) { 66 | static char outputstr[100]; 67 | int outputn = INS_COLUMN; 68 | va_list ap; 69 | va_start(ap, fmt); 70 | for(;;) { 71 | char c = *fmt++; 72 | if(!c) break; 73 | if(c == '%') { 74 | c = *fmt++; 75 | switch(c) { 76 | case '%': { 77 | outputstr[outputn++] = '%'; 78 | break; } 79 | case 'A': { 80 | int n = (va_arg(ap, int) >> 5) & 7; 81 | outputstr[outputn++] = aluop[n][0]; 82 | outputstr[outputn++] = aluop[n][1]; 83 | outputstr[outputn++] = aluop[n][2]; 84 | break; } 85 | case 'M': { 86 | int n = (va_arg(ap, int) >> 5) & 7; 87 | outputstr[outputn++] = rmwop[n][0]; 88 | outputstr[outputn++] = rmwop[n][1]; 89 | outputstr[outputn++] = rmwop[n][2]; 90 | break; } 91 | case 'B': { 92 | int n = fetchbyte(); 93 | outputstr[outputn++] = '$'; 94 | if(n < 0) { 95 | outputstr[outputn++] = '-'; 96 | outputstr[outputn++] = '-'; 97 | } else { 98 | sprintf( 99 | outputstr + outputn, 100 | "%02lX", (unsigned long)n 101 | ); 102 | outputn += 2; 103 | } 104 | break; } 105 | case 'R': { 106 | int n = fetchbyte(); 107 | outputstr[outputn++] = '$'; 108 | if(n < 0) { 109 | outputstr[outputn++] = '-'; 110 | outputstr[outputn++] = '-'; 111 | outputstr[outputn++] = '-'; 112 | outputstr[outputn++] = '-'; 113 | } else { 114 | n = ((int8_t)(n)) + 115 | dasm_address; 116 | sprintf( 117 | outputstr + outputn, 118 | "%04lX", (unsigned long)(n & 0xFFFF) 119 | ); 120 | outputn += 4; 121 | } 122 | break; } 123 | case 'Z': { 124 | int n1 = fetchbyte(); 125 | int n2 = fetchbyte(); 126 | outputstr[outputn++] = '$'; 127 | if((n1 < 0) || (n2 < 0)) { 128 | for(n1 = 0; n1 < 6; n1++) { 129 | outputstr[outputn++] = '-'; 130 | } 131 | } else { 132 | int n = (n2 << 8) | (n1 & 0xFF); 133 | n = ((int16_t)(n)) + 134 | dasm_address; 135 | sprintf( 136 | outputstr + outputn, 137 | "%04lX", (unsigned long)(n & 0xFFFF) 138 | ); 139 | outputn += 6; 140 | } 141 | break; } 142 | case 'G': { 143 | int n = va_arg(ap, int); 144 | outputstr[outputn++] = '$'; 145 | if(n < 0) { 146 | outputstr[outputn++] = '-'; 147 | outputstr[outputn++] = '-'; 148 | } else { 149 | sprintf( 150 | outputstr + outputn, 151 | "%02lX", (unsigned long)(n) 152 | ); 153 | outputn += 2; 154 | } 155 | break; } 156 | case 'W': { 157 | int q, n[2]; 158 | n[1] = fetchbyte(); 159 | n[0] = fetchbyte(); 160 | outputstr[outputn++] = '$'; 161 | for(q = 0; q < 2; q++) { 162 | if(n[q] < 0) { 163 | outputstr[outputn++] = '-'; 164 | outputstr[outputn++] = '-'; 165 | } else { 166 | sprintf( 167 | outputstr + outputn, 168 | "%02lX", (unsigned long)(n[q]) 169 | ); 170 | outputn += 2; 171 | } 172 | } 173 | break; } 174 | case 'L': { 175 | int q, n[3]; 176 | n[2] = fetchbyte(); 177 | n[1] = fetchbyte(); 178 | n[0] = fetchbyte(); 179 | outputstr[outputn++] = '$'; 180 | for(q = 0; q < 3; q++) { 181 | if(n[q] < 0) { 182 | outputstr[outputn++] = '-'; 183 | outputstr[outputn++] = '-'; 184 | } else { 185 | sprintf( 186 | outputstr + outputn, 187 | "%02lX", (unsigned long)(n[q]) 188 | ); 189 | outputn += 2; 190 | } 191 | } 192 | break; } 193 | case 'I': case 'X': { 194 | int q, n[3]; 195 | int8_t* sflag = (c == 'I') ? (&mflag) : (&xflag); 196 | outputstr[outputn++] = '$'; 197 | if(*sflag) { 198 | n[0] = fetchbyte(); 199 | n[1] = fetchbyte(); 200 | // 201 | // BRK/COP/WDM/STP after this? 202 | // 203 | switch(n[1]) { 204 | case 0x00: case 0x02: 205 | case 0x42: case 0xDB: 206 | if(flag_follow && flag_guess 207 | ) { 208 | (*sflag) = 0; 209 | q = 2; 210 | break; 211 | } 212 | default: 213 | unfetchbyte(n[1]); 214 | q = 1; 215 | break; 216 | } 217 | } else { 218 | n[0] = fetchbyte(); 219 | n[1] = fetchbyte(); 220 | n[2] = fetchbyte(); 221 | // 222 | // BRK/COP/WDM/STP after this? 223 | // 224 | switch(n[2]) { 225 | case 0x00: case 0x02: 226 | case 0x42: case 0xDB: 227 | if(flag_follow && flag_guess 228 | ) { 229 | (*sflag) = 1; 230 | unfetchbyte(n[2]); 231 | unfetchbyte(n[1]); 232 | q = 1; 233 | break; 234 | } 235 | default: 236 | unfetchbyte(n[2]); 237 | q = 2; 238 | break; 239 | } 240 | } 241 | while(q--) { 242 | if(n[q] < 0) { 243 | outputstr[outputn++] = '-'; 244 | outputstr[outputn++] = '-'; 245 | } else { 246 | sprintf( 247 | outputstr + outputn, 248 | "%02lX", (unsigned long)(n[q]) 249 | ); 250 | outputn += 2; 251 | } 252 | } 253 | break; } 254 | } 255 | } else { 256 | outputstr[outputn++] = c; 257 | } 258 | } 259 | outputstr[outputn] = 0; 260 | sprintf(outputstr, "%02lX/%04lX:", 261 | (unsigned long)(ins_address >> 16), 262 | (unsigned long)(ins_address & 0xFFFF) 263 | ); 264 | memset(outputstr + 8, ' ', INS_COLUMN - 8); 265 | 266 | outputn = 9; 267 | { int i; 268 | for(i = 0; i < fetchn; i++) { 269 | int n = fetchlist[i]; 270 | if(n < 0) { 271 | outputstr[outputn ] = '-'; 272 | outputstr[outputn+1] = '-'; 273 | } else { 274 | sprintf(outputstr + outputn, "%02X", n); 275 | outputstr[outputn+2] = ' '; 276 | } 277 | outputn += 3; 278 | } 279 | } 280 | fetchn = 0; 281 | printf("%s\n", outputstr); 282 | 283 | va_end(ap); 284 | } 285 | 286 | static void disassemble_one(void) { 287 | int opcode; 288 | ins_address = dasm_address; 289 | opcode = fetchbyte(); 290 | if(opcode < 0) return; 291 | 292 | if (opcode == 0x00) { ins("brk %B"); 293 | } else if(opcode == 0x20) { ins("jsr %W"); 294 | } else if(opcode == 0x40) { ins("rti"); if(flag_return) { mflag = mflag_default; xflag = xflag_default; } 295 | } else if(opcode == 0x60) { ins("rts"); if(flag_return) { mflag = mflag_default; xflag = xflag_default; } 296 | } else if(opcode == 0x80) { ins("bra %R"); 297 | } else if(opcode == 0xA0) { ins("ldy #%X"); 298 | } else if(opcode == 0xC0) { ins("cpy #%X"); 299 | } else if(opcode == 0xE0) { ins("cpx #%X"); 300 | } else if(opcode == 0x10) { ins("bpl %R"); 301 | } else if(opcode == 0x30) { ins("bmi %R"); 302 | } else if(opcode == 0x50) { ins("bvc %R"); 303 | } else if(opcode == 0x70) { ins("bvs %R"); 304 | } else if(opcode == 0x90) { ins("bcc %R"); 305 | } else if(opcode == 0xB0) { ins("bcs %R"); 306 | } else if(opcode == 0xD0) { ins("bne %R"); 307 | } else if(opcode == 0xF0) { ins("beq %R"); 308 | 309 | } else if((opcode & 0x1F) == 0x01) { ins("%A (%B,x)" , opcode); 310 | } else if((opcode & 0x1F) == 0x11) { ins("%A (%B),y" , opcode); 311 | 312 | } else if(opcode == 0x02) { ins("cop %B"); 313 | } else if(opcode == 0x22) { ins("jsr %L"); 314 | } else if(opcode == 0x42) { ins("wdm %B"); 315 | } else if(opcode == 0x62) { ins("per %Z"); 316 | } else if(opcode == 0x82) { ins("brl %Z"); 317 | } else if(opcode == 0xA2) { ins("ldx #%X"); 318 | } else if(opcode == 0xC2) { 319 | int n = fetchbyte(); 320 | if(flag_follow) if(n >= 0) { 321 | if(n & 0x10) xflag = 0; 322 | if(n & 0x20) mflag = 0; 323 | } 324 | ins("rep #%G", n); 325 | } else if(opcode == 0xE2) { 326 | int n = fetchbyte(); 327 | if(flag_follow) if(n >= 0) { 328 | if(n & 0x10) xflag = 1; 329 | if(n & 0x20) mflag = 1; 330 | } 331 | ins("sep #%G", n); 332 | } else if((opcode & 0x1F) == 0x12) { ins("%A (%B)" , opcode); 333 | 334 | } else if((opcode & 0x1F) == 0x03) { ins("%A %B,s" , opcode); 335 | } else if((opcode & 0x1F) == 0x13) { ins("%A (%B,s),y", opcode); 336 | 337 | } else if(opcode == 0x04) { ins("tsb %B"); 338 | } else if(opcode == 0x24) { ins("bit %B"); 339 | } else if(opcode == 0x44) { ins("mvp %B,%B"); 340 | } else if(opcode == 0x64) { ins("stz %B"); 341 | } else if(opcode == 0x84) { ins("sty %B"); 342 | } else if(opcode == 0xA4) { ins("ldy %B"); 343 | } else if(opcode == 0xC4) { ins("cpy %B"); 344 | } else if(opcode == 0xE4) { ins("cpx %B"); 345 | } else if(opcode == 0x14) { ins("trb %B"); 346 | } else if(opcode == 0x34) { ins("bit %B,x"); 347 | } else if(opcode == 0x54) { ins("mvn %B,%B"); 348 | } else if(opcode == 0x74) { ins("stz %B,x"); 349 | } else if(opcode == 0x94) { ins("sty %B,x"); 350 | } else if(opcode == 0xB4) { ins("ldy %B,x"); 351 | } else if(opcode == 0xD4) { ins("pei (%B)"); 352 | } else if(opcode == 0xF4) { ins("pea %W"); 353 | 354 | } else if((opcode & 0x1F) == 0x05) { ins("%A %B" , opcode); 355 | } else if((opcode & 0x1F) == 0x15) { ins("%A %B,x" , opcode); 356 | 357 | } else if(((opcode & 0x1F) == 0x06) && ((opcode & 0xC0) != 0x80)) { 358 | ins("%M %B", opcode); 359 | } else if(opcode == 0x86) { ins("stx %B"); 360 | } else if(opcode == 0xA6) { ins("ldx %B"); 361 | } else if(((opcode & 0x1F) == 0x16) && ((opcode & 0xC0) != 0x80)) { 362 | ins("%M %B,x", opcode); 363 | } else if(opcode == 0x96) { ins("stx %B,y"); 364 | } else if(opcode == 0xB6) { ins("ldx %B,y"); 365 | 366 | } else if((opcode & 0x1F) == 0x07) { ins("%A [%B]" , opcode); 367 | } else if((opcode & 0x1F) == 0x17) { ins("%A [%B],y" , opcode); 368 | 369 | } else if(opcode == 0x08) { ins("php"); 370 | } else if(opcode == 0x28) { ins("plp"); 371 | } else if(opcode == 0x48) { ins("pha"); 372 | } else if(opcode == 0x68) { ins("pla"); 373 | } else if(opcode == 0x88) { ins("dey"); 374 | } else if(opcode == 0xA8) { ins("tay"); 375 | } else if(opcode == 0xC8) { ins("iny"); 376 | } else if(opcode == 0xE8) { ins("inx"); 377 | } else if(opcode == 0x18) { ins("clc"); 378 | } else if(opcode == 0x38) { ins("sec"); 379 | } else if(opcode == 0x58) { ins("cli"); 380 | } else if(opcode == 0x78) { ins("sei"); 381 | } else if(opcode == 0x98) { ins("tya"); 382 | } else if(opcode == 0xB8) { ins("clv"); 383 | } else if(opcode == 0xD8) { ins("cld"); 384 | } else if(opcode == 0xF8) { ins("sed"); 385 | 386 | } else if((opcode & 0x1F) == 0x09) { 387 | if(opcode == 0x89) ins("bit #%I"); 388 | else ins("%A #%I", opcode); 389 | } else if((opcode & 0x1F) == 0x19) { ins("%A %W,y" , opcode); 390 | 391 | } else if(opcode == 0x0A) { ins("asl"); 392 | } else if(opcode == 0x2A) { ins("rol"); 393 | } else if(opcode == 0x4A) { ins("lsr"); 394 | } else if(opcode == 0x6A) { ins("ror"); 395 | } else if(opcode == 0x8A) { ins("txa"); 396 | } else if(opcode == 0xAA) { ins("tax"); 397 | } else if(opcode == 0xCA) { ins("dex"); 398 | } else if(opcode == 0xEA) { ins("nop"); 399 | } else if(opcode == 0x1A) { ins("inc"); 400 | } else if(opcode == 0x3A) { ins("dec"); 401 | } else if(opcode == 0x5A) { ins("phy"); 402 | } else if(opcode == 0x7A) { ins("ply"); 403 | } else if(opcode == 0x9A) { ins("txs"); 404 | } else if(opcode == 0xBA) { ins("tsx"); 405 | } else if(opcode == 0xDA) { ins("phx"); 406 | } else if(opcode == 0xFA) { ins("plx"); 407 | 408 | } else if(opcode == 0x0B) { ins("phd"); 409 | } else if(opcode == 0x2B) { ins("pld"); 410 | } else if(opcode == 0x4B) { ins("phk"); 411 | } else if(opcode == 0x6B) { ins("rtl"); if(flag_return) { mflag = mflag_default; xflag = xflag_default; } 412 | } else if(opcode == 0x8B) { ins("phb"); 413 | } else if(opcode == 0xAB) { ins("plb"); 414 | } else if(opcode == 0xCB) { ins("wai"); 415 | } else if(opcode == 0xEB) { ins("xba"); 416 | } else if(opcode == 0x1B) { ins("tcs"); 417 | } else if(opcode == 0x3B) { ins("tsc"); 418 | } else if(opcode == 0x5B) { ins("tcd"); 419 | } else if(opcode == 0x7B) { ins("tdc"); 420 | } else if(opcode == 0x9B) { ins("txy"); 421 | } else if(opcode == 0xBB) { ins("tyx"); 422 | } else if(opcode == 0xDB) { ins("stp"); 423 | } else if(opcode == 0xFB) { ins("xce"); 424 | 425 | } else if(opcode == 0x0C) { ins("tsb %W"); 426 | } else if(opcode == 0x2C) { ins("bit %W"); 427 | } else if(opcode == 0x4C) { ins("jmp %W"); 428 | } else if(opcode == 0x6C) { ins("jmp (%W)"); 429 | } else if(opcode == 0x8C) { ins("sty %W"); 430 | } else if(opcode == 0xAC) { ins("ldy %W"); 431 | } else if(opcode == 0xCC) { ins("cpy %W"); 432 | } else if(opcode == 0xEC) { ins("cpx %W"); 433 | } else if(opcode == 0x1C) { ins("trb %W"); 434 | } else if(opcode == 0x3C) { ins("bit %W,x"); 435 | } else if(opcode == 0x5C) { ins("jmp %L"); 436 | } else if(opcode == 0x7C) { ins("jmp (%W,x)"); 437 | } else if(opcode == 0x9C) { ins("stz %W"); 438 | } else if(opcode == 0xBC) { ins("ldy %W,x"); 439 | } else if(opcode == 0xDC) { ins("jmp [%W]"); 440 | } else if(opcode == 0xFC) { ins("jsr (%W,x)"); 441 | 442 | } else if((opcode & 0x1F) == 0x0D) { ins("%A %W" , opcode); 443 | } else if((opcode & 0x1F) == 0x1D) { ins("%A %W,x" , opcode); 444 | 445 | } else if(((opcode & 0x1F) == 0x0E) && ((opcode & 0xC0) != 0x80)) { 446 | ins("%M %W", opcode); 447 | } else if(opcode == 0x8E) { ins("stx %W"); 448 | } else if(opcode == 0xAE) { ins("ldx %W"); 449 | } else if(((opcode & 0x1F) == 0x1E) && ((opcode & 0xC0) != 0x80)) { 450 | ins("%M %W,x", opcode); 451 | } else if(opcode == 0x9E) { ins("stz %W,x"); 452 | } else if(opcode == 0xBE) { ins("ldx %W,y"); 453 | 454 | } else if((opcode & 0x1F) == 0x0F) { ins("%A %L" , opcode); 455 | } else if((opcode & 0x1F) == 0x1F) { ins("%A %L,x" , opcode); 456 | } else { ins("???"); } 457 | } 458 | 459 | //////////////////////////////////////////////////////////////////////////////// 460 | 461 | static FILE* infile = NULL; 462 | static uint32_t infile_bytes_left; 463 | 464 | static int getbyte(void) { 465 | if(infile_bytes_left) { 466 | int n = fgetc(infile); 467 | if(n == EOF) { 468 | infile_bytes_left = 0; 469 | return -1; 470 | } else { 471 | infile_bytes_left--; 472 | return (n & 0xFF); 473 | } 474 | } 475 | return -1; 476 | } 477 | 478 | static void disasm_range(uint32_t fileoffset, uint32_t len, uint32_t addr) { 479 | fseeko(infile, fileoffset, SEEK_SET); 480 | dasm_address = addr; 481 | infile_bytes_left = len; 482 | mflag = mflag_default; 483 | xflag = xflag_default; 484 | fetchn = unfetchn = 0; 485 | while(infile_bytes_left > 0) { 486 | disassemble_one(); 487 | } 488 | } 489 | 490 | static uint32_t gethex(const char* s) { 491 | if(!s[0]) return 0; 492 | if(s[0] == '$') s++; 493 | if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2; 494 | return (uint32_t)strtoul(s, NULL, 16); 495 | } 496 | 497 | //////////////////////////////////////////////////////////////////////////////// 498 | 499 | int main(int argc, char** argv) { 500 | const char* infilename; 501 | uint32_t arg_start; 502 | uint32_t arg_len; 503 | uint32_t arg_addr; 504 | int option_argn = 4; 505 | 506 | normalize_argv0(argv[0]); 507 | 508 | if(argc < 4) { 509 | banner(); 510 | printf( 511 | "Usage: %s imagefile start address [length] [options]\n" 512 | "Output is written to stdout. All values must be given in hex.\n" 513 | "If no length is given, disassembly will stop at the end of the bank.\n" 514 | "Options:\n" 515 | " -m0 Assume M flag = 0\n" 516 | " -m1 Assume M flag = 1 (default)\n" 517 | " -x0 Assume X flag = 0 (default)\n" 518 | " -x1 Assume X flag = 1\n" 519 | " -noreturn Disable flag reset after RTS/RTL/RTI\n" 520 | " -noguess Disable flag guess on BRK/COP/WDM/STP\n" 521 | " -nofollow Disable REP/SEP following (not recommended)\n", 522 | argv[0] 523 | ); 524 | return 1; 525 | } 526 | 527 | infilename = argv[1]; 528 | arg_start = gethex(argv[2]); 529 | arg_addr = gethex(argv[3]); 530 | 531 | arg_len = ((arg_addr & 0xFFFF) ^ 0xFFFF) + 1; 532 | 533 | if((argc >= 5) && (argv[4][0] != '-')) { 534 | arg_len = gethex(argv[4]); 535 | option_argn = 5; 536 | } 537 | 538 | while(option_argn < argc) { 539 | const char* s = argv[option_argn]; 540 | if (!strcmp(s, "-m0")) { mflag_default = 0; 541 | } else if(!strcmp(s, "-m1")) { mflag_default = 1; 542 | } else if(!strcmp(s, "-x0")) { xflag_default = 0; 543 | } else if(!strcmp(s, "-x1")) { xflag_default = 1; 544 | } else if(!strcmp(s, "-noreturn")) { flag_return = 0; 545 | } else if(!strcmp(s, "-noguess" )) { flag_guess = 0; 546 | } else if(!strcmp(s, "-nofollow")) { flag_follow = 0; 547 | } else { 548 | printf("unknown option: %s\n", s); 549 | return 1; 550 | } 551 | option_argn++; 552 | } 553 | 554 | printf( 555 | "Disassembly of %s\n" 556 | "Starting at offset $%lX for $%lX bytes\n" 557 | "65816 address starts at $%lX\n" 558 | "return=%s guess=%s follow=%s\n" 559 | "\n", 560 | infilename, 561 | (unsigned long)arg_start, 562 | (unsigned long)arg_len, 563 | (unsigned long)arg_addr, 564 | flag_return ? "on" : "off", 565 | flag_guess ? "on" : "off", 566 | flag_follow ? "on" : "off" 567 | ); 568 | 569 | infile = fopen(infilename, "rb"); 570 | if(!infile) { 571 | fprintf(stderr, "Error: %s: %s\n", infilename, strerror(errno)); 572 | return 1; 573 | } 574 | 575 | disasm_range(arg_start, arg_len, arg_addr); 576 | 577 | fclose(infile); 578 | 579 | return 0; 580 | } 581 | 582 | //////////////////////////////////////////////////////////////////////////////// 583 | -------------------------------------------------------------------------------- /hax65816.txt: -------------------------------------------------------------------------------- 1 | hax65816(1) 2 | =========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | hax65816 - Simple 65816 disassembler 8 | 9 | SYNOPSIS 10 | -------- 11 | *hax65816* 'imagefile' 'start' 'address' ['length'] 12 | ['-m{0,1}'|'-x{0,1}'|'-noreturn'|'-noguess'|'-nofollow'] 13 | 14 | DESCRIPTION 15 | ----------- 16 | A simple 65816 disassembler with basic flag following. This is the 17 | disassembler used for the Seiken Densetsu 3 project, among others. 18 | 19 | Output is written on stdout, all values must be given in hexadecimal. 20 | If no length is given, disassembly will stop at the end of the bank. 21 | 22 | OPTIONS 23 | ------- 24 | *-m0*:: Assume M flag = 0 25 | 26 | *-m1*:: Assume M flag = 1 (default) 27 | 28 | *-x0*:: Assume X flag = 0 (default) 29 | 30 | *-x1*:: Assume X flag = 1 31 | 32 | *-noreturn*:: Disable flag reset after RTS/RTL/RTI 33 | 34 | *-noguess*:: Disable flag guess on BRK/COP/WDM/STP 35 | 36 | *-nofollow*:: Disable REP/SEP following (not recommendeD) 37 | -------------------------------------------------------------------------------- /pecompat.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "pecompat - Maximize compatibility of a Win32 PE file" 4 | #define COPYR "Copyright (C) 2012 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Seek in a way that doesn't involve seeking past the end of the file 27 | // 28 | void safe_seek(FILE* f, unsigned long offset) { 29 | long limit; 30 | 31 | clearerr(f); 32 | fseek(f, 0, SEEK_END); 33 | if(ferror(f)) { return; } 34 | 35 | limit = ftell(f); 36 | if(ferror(f)) { return; } 37 | 38 | if(limit >= 0) { 39 | if(offset > ((unsigned long)limit)) { 40 | fgetc(f); // force EOF 41 | return; 42 | } 43 | } 44 | 45 | fseek(f, offset, SEEK_SET); 46 | } 47 | 48 | //////////////////////////////////////////////////////////////////////////////// 49 | 50 | unsigned get16lsb(FILE* f) { 51 | int c0, c1; 52 | clearerr(f); 53 | c0 = fgetc(f); 54 | if(ferror(f) || feof(f)) { return 0; } 55 | c1 = fgetc(f); 56 | if(ferror(f) || feof(f)) { return 0; } 57 | return 58 | (((unsigned)(c1 & 0xff)) << 8) + 59 | (((unsigned)(c0 & 0xff)) ); 60 | } 61 | 62 | unsigned get16lsb_at(FILE* f, unsigned long offset) { 63 | safe_seek(f, offset); 64 | if(ferror(f) || feof(f)) { return 0; } 65 | return get16lsb(f); 66 | } 67 | 68 | unsigned long get32lsb(FILE* f) { 69 | int c0, c1, c2, c3; 70 | clearerr(f); 71 | c0 = fgetc(f); 72 | if(ferror(f) || feof(f)) { return 0; } 73 | c1 = fgetc(f); 74 | if(ferror(f) || feof(f)) { return 0; } 75 | c2 = fgetc(f); 76 | if(ferror(f) || feof(f)) { return 0; } 77 | c3 = fgetc(f); 78 | if(ferror(f) || feof(f)) { return 0; } 79 | return 80 | (((unsigned long)(c3 & 0xff)) << 24) + 81 | (((unsigned long)(c2 & 0xff)) << 16) + 82 | (((unsigned long)(c1 & 0xff)) << 8) + 83 | (((unsigned long)(c0 & 0xff)) ); 84 | } 85 | 86 | unsigned long get32lsb_at(FILE* f, unsigned long offset) { 87 | safe_seek(f, offset); 88 | if(ferror(f) || feof(f)) { return 0; } 89 | return get32lsb(f); 90 | } 91 | 92 | int put16lsb(unsigned value, FILE* f) { 93 | clearerr(f); 94 | fputc(((value >> 0) & 0xff), f); 95 | if(ferror(f) || feof(f)) { return 1; } 96 | fputc(((value >> 8) & 0xff), f); 97 | if(ferror(f) || feof(f)) { return 1; } 98 | fflush(f); 99 | return 0; 100 | } 101 | 102 | int put16lsb_at(unsigned value, FILE* f, unsigned long offset) { 103 | safe_seek(f, offset); 104 | if(ferror(f) || feof(f)) { return 0; } 105 | return put16lsb(value, f); 106 | } 107 | 108 | int put32lsb(unsigned long value, FILE* f) { 109 | clearerr(f); 110 | fputc(((value >> 0) & 0xff), f); 111 | if(ferror(f) || feof(f)) { return 1; } 112 | fputc(((value >> 8) & 0xff), f); 113 | if(ferror(f) || feof(f)) { return 1; } 114 | fputc(((value >> 16) & 0xff), f); 115 | if(ferror(f) || feof(f)) { return 1; } 116 | fputc(((value >> 24) & 0xff), f); 117 | if(ferror(f) || feof(f)) { return 1; } 118 | fflush(f); 119 | return 0; 120 | } 121 | 122 | int put32lsb_at(unsigned long value, FILE* f, unsigned long offset) { 123 | safe_seek(f, offset); 124 | if(ferror(f) || feof(f)) { return 0; } 125 | return put32lsb(value, f); 126 | } 127 | 128 | //////////////////////////////////////////////////////////////////////////////// 129 | // 130 | // Convert virtual address to file offset 131 | // Returns 0 if not found 132 | // 133 | unsigned long virtual_to_fileoffset(FILE* f, unsigned long v) { 134 | unsigned long pe; 135 | unsigned long imagebase; 136 | unsigned long obj; 137 | unsigned long nobjs; 138 | unsigned long i; 139 | 140 | pe = get32lsb_at(f, 0x3c); 141 | if(ferror(f) || feof(f)) { return 0; } 142 | 143 | imagebase = get32lsb_at(f, pe + 0x34); 144 | if(ferror(f) || feof(f)) { return 0; } 145 | 146 | nobjs = get16lsb_at(f, pe + 0x06); 147 | if(ferror(f) || feof(f)) { return 0; } 148 | 149 | obj = pe + 0x18 + get16lsb_at(f, pe + 0x14); 150 | if(ferror(f) || feof(f)) { return 0; } 151 | 152 | for(i = 0; i < nobjs; i++) { 153 | unsigned long record = 0x28 * i + obj; 154 | unsigned long vsize; 155 | unsigned long vaddr; 156 | unsigned long psize; 157 | unsigned long fileoffset; 158 | unsigned long size; 159 | vsize = get32lsb_at(f, record + 0x08); 160 | if(ferror(f) || feof(f)) { return 0; } 161 | vaddr = get32lsb_at(f, record + 0x0c) + imagebase; 162 | if(ferror(f) || feof(f)) { return 0; } 163 | psize = get32lsb_at(f, record + 0x10); 164 | if(ferror(f) || feof(f)) { return 0; } 165 | fileoffset = get32lsb_at(f, record + 0x14); 166 | if(ferror(f) || feof(f)) { return 0; } 167 | size = vsize; 168 | if(size > psize) { size = psize; } 169 | if((v >= vaddr) && (v < (vaddr + size))) { 170 | return (v - vaddr) + fileoffset; 171 | } 172 | } 173 | return 0; 174 | } 175 | 176 | //////////////////////////////////////////////////////////////////////////////// 177 | 178 | void dumpstring(FILE* f, unsigned long offset) { 179 | safe_seek(f, offset); 180 | if(ferror(f) || feof(f)) { return; } 181 | for(;;) { 182 | int c = fgetc(f); 183 | if((c == 0) || (c == EOF) || (!isprint(c))) { break; } 184 | fputc(c, stdout); 185 | } 186 | } 187 | 188 | //////////////////////////////////////////////////////////////////////////////// 189 | 190 | int fixpe(const char* filename) { 191 | int returnvalue = 1; 192 | FILE* f = NULL; 193 | unsigned long pe_header; 194 | unsigned cpu_type; 195 | 196 | unsigned long old_pe_checksum; 197 | unsigned long new_pe_checksum; 198 | 199 | f = fopen(filename, "r+b"); 200 | if(!f) { goto fileerror; } 201 | 202 | // 203 | // Check MZ header 204 | // 205 | { unsigned mz = get16lsb_at(f, 0); 206 | if(ferror(f) || feof(f)) { goto fileerror; } 207 | if(mz != 0x5a4d) { 208 | printf("%s: Error: Incorrect MZ signature\n", filename); 209 | goto error; 210 | } 211 | } 212 | 213 | // 214 | // Check PE header 215 | // 216 | pe_header = get32lsb_at(f, 0x3c); 217 | if(ferror(f)) { goto fileerror; } 218 | if(feof(f)) { 219 | printf("%s: Error: Too short, not a PE file\n", filename); 220 | goto error; 221 | } 222 | { unsigned long pe = get32lsb_at(f, pe_header); 223 | if(ferror(f)) { goto fileerror; } 224 | if(feof(f)) { 225 | printf("%s: Error: PE signature not found\n", filename); 226 | goto error; 227 | } 228 | if(pe != 0x00004550) { 229 | printf("%s: Error: Incorrect PE signature\n", filename); 230 | goto error; 231 | } 232 | } 233 | 234 | // 235 | // Check CPU type (but don't patch it) 236 | // 237 | cpu_type = get16lsb_at(f, pe_header+0x04); 238 | if(ferror(f) || feof(f)) { goto fileerror; } 239 | switch(cpu_type) { 240 | case 0x014c: 241 | // 80386 - OK 242 | break; 243 | case 0x014d: 244 | case 0x014e: 245 | // 80486 or 80586 - warn 246 | printf("%s: Warning: CPU type of 0x%x is not strictly 80386 compatible\n", filename, cpu_type); 247 | break; 248 | case 0x8664: 249 | // x86-64 - will not work 250 | printf("%s: Error: x86-64 architecture; not supported\n", filename); 251 | goto error; 252 | default: 253 | // Unknown architecture 254 | printf("%s: Error: Unknown CPU architecture 0x%x\n", filename, cpu_type); 255 | goto error; 256 | } 257 | 258 | // 259 | // Patch the linker timestamp 260 | // 261 | { unsigned long old_timestamp; 262 | unsigned long new_timestamp = 0; 263 | old_timestamp = get32lsb_at(f, pe_header+0x08); 264 | if(ferror(f) || feof(f)) { goto fileerror; } 265 | if(old_timestamp != new_timestamp) { 266 | printf("%s: Patching linker timestamp: 0x%lx -> 0x%lx\n", 267 | filename, 268 | old_timestamp, 269 | new_timestamp 270 | ); 271 | if(put32lsb_at(new_timestamp, f, pe_header+0x08)) { goto fileerror; } 272 | } 273 | } 274 | 275 | // 276 | // Patch the OS version 277 | // 278 | { unsigned old_os_major; 279 | unsigned old_os_minor; 280 | unsigned new_os_major = 4; 281 | unsigned new_os_minor = 0; 282 | old_os_major = get16lsb_at(f, pe_header+0x40); 283 | if(ferror(f) || feof(f)) { goto fileerror; } 284 | old_os_minor = get16lsb_at(f, pe_header+0x42); 285 | if(ferror(f) || feof(f)) { goto fileerror; } 286 | if( 287 | old_os_major != new_os_major || 288 | old_os_minor != new_os_minor 289 | ) { 290 | printf("%s: Patching OS version: %u.%u -> %u.%u\n", 291 | filename, 292 | old_os_major, old_os_minor, 293 | new_os_major, new_os_minor 294 | ); 295 | if(put16lsb_at(new_os_major, f, pe_header+0x40)) { goto fileerror; } 296 | if(put16lsb_at(new_os_minor, f, pe_header+0x42)) { goto fileerror; } 297 | } 298 | } 299 | 300 | // 301 | // Patch the subsystem version 302 | // 303 | { unsigned old_sub_major; 304 | unsigned old_sub_minor; 305 | unsigned new_sub_major = 4; 306 | unsigned new_sub_minor = 0; 307 | old_sub_major = get16lsb_at(f, pe_header+0x48); 308 | if(ferror(f) || feof(f)) { goto fileerror; } 309 | old_sub_minor = get16lsb_at(f, pe_header+0x4a); 310 | if(ferror(f) || feof(f)) { goto fileerror; } 311 | if( 312 | old_sub_major != new_sub_major || 313 | old_sub_minor != new_sub_minor 314 | ) { 315 | printf("%s: Patching subsystem version: %u.%u -> %u.%u\n", 316 | filename, 317 | old_sub_major, old_sub_minor, 318 | new_sub_major, new_sub_minor 319 | ); 320 | if(put16lsb_at(new_sub_major, f, pe_header+0x48)) { goto fileerror; } 321 | if(put16lsb_at(new_sub_minor, f, pe_header+0x4a)) { goto fileerror; } 322 | } 323 | } 324 | 325 | // 326 | // Grab the old PE checksum 327 | // 328 | old_pe_checksum = get32lsb_at(f, pe_header+0x58); 329 | if(ferror(f) || feof(f)) { goto fileerror; } 330 | 331 | // 332 | // Calculate the new PE checksum 333 | // 334 | new_pe_checksum = 0; 335 | { unsigned long offset = 0; 336 | 337 | safe_seek(f, 0); 338 | if(ferror(f) || feof(f)) { goto fileerror; } 339 | 340 | for(;;) { 341 | unsigned long len = 0; 342 | int c0, c1; 343 | c0 = fgetc(f); 344 | if(ferror(f)) { goto fileerror; } 345 | if(c0 == EOF) { break; } else { len++; } 346 | c1 = fgetc(f); 347 | if(ferror(f)) { goto fileerror; } 348 | if(c1 == EOF) { c1 = 0; } else { len++; } 349 | // 350 | // Treat existing checksum field as zero for purposes of checksumming 351 | // 352 | if( 353 | offset >= (pe_header + 0x58) && 354 | offset < (pe_header + 0x5c) 355 | ) { c0 = c1 = 0; } 356 | // 357 | // Add word with carry 358 | // 359 | { unsigned long word = 360 | (((unsigned long)(c0 & 0xff)) << 0) + 361 | (((unsigned long)(c1 & 0xff)) << 8); 362 | new_pe_checksum += word; 363 | new_pe_checksum += (new_pe_checksum >> 16); 364 | new_pe_checksum &= ((unsigned long)(0xffff)); 365 | } 366 | offset += len; 367 | } 368 | // 369 | // Finally, add the file size 370 | // 371 | new_pe_checksum += offset; 372 | } 373 | 374 | // 375 | // Patch checksum if necessary 376 | // 377 | if(old_pe_checksum != new_pe_checksum) { 378 | printf("%s: Patching checksum: 0x%lx -> 0x%lx\n", 379 | filename, 380 | old_pe_checksum, 381 | new_pe_checksum 382 | ); 383 | if(put32lsb_at(new_pe_checksum, f, pe_header+0x58)) { goto fileerror; } 384 | } 385 | 386 | // 387 | // Look at the import table 388 | // 389 | { unsigned long imagebase; 390 | unsigned long import_table_vaddr; 391 | unsigned long import_table_size; 392 | unsigned long import_table_fileoffset; 393 | 394 | unsigned long i; 395 | unsigned long warned_functions = 0; 396 | unsigned long max_warned_functions = 5; 397 | 398 | imagebase = get32lsb_at(f, pe_header+0x34); 399 | if(ferror(f) || feof(f)) { goto fileerror; } 400 | import_table_vaddr = get32lsb_at(f, pe_header+0x80) + imagebase; 401 | if(ferror(f) || feof(f)) { goto fileerror; } 402 | import_table_size = get32lsb_at(f, pe_header+0x84); 403 | if(ferror(f) || feof(f)) { goto fileerror; } 404 | 405 | import_table_fileoffset = virtual_to_fileoffset(f, import_table_vaddr); 406 | if(ferror(f) || feof(f)) { goto fileerror; } 407 | 408 | if(!import_table_fileoffset) { 409 | printf("%s: Error: Unable to find import table\n", filename); 410 | goto error; 411 | } 412 | 413 | for(i = 0; i < import_table_size; i += 0x14) { 414 | unsigned long function_list_vaddr; 415 | unsigned long dll_name_vaddr; 416 | unsigned long function_list_fileoffset; 417 | unsigned long dll_name_fileoffset; 418 | function_list_vaddr = get32lsb_at(f, import_table_fileoffset + i + 0x00); 419 | if(ferror(f) || feof(f)) { goto fileerror; } 420 | dll_name_vaddr = get32lsb_at(f, import_table_fileoffset + i + 0x0c); 421 | if(ferror(f) || feof(f)) { goto fileerror; } 422 | 423 | // 424 | // These lists seem to be NULL-terminated 425 | // 426 | if(function_list_vaddr == 0) { break; } 427 | if(dll_name_vaddr == 0) { break; } 428 | 429 | function_list_vaddr += imagebase; 430 | dll_name_vaddr += imagebase; 431 | 432 | function_list_fileoffset = virtual_to_fileoffset(f, function_list_vaddr); 433 | if(ferror(f) || feof(f)) { goto fileerror; } 434 | dll_name_fileoffset = virtual_to_fileoffset(f, dll_name_vaddr); 435 | if(ferror(f) || feof(f)) { goto fileerror; } 436 | 437 | if(!function_list_fileoffset) { 438 | printf("%s: Error: Malformed import table: Unable to map function list address 0x%lx\n", 439 | filename, 440 | function_list_vaddr 441 | ); 442 | goto error; 443 | } 444 | if(!dll_name_fileoffset) { 445 | printf("%s: Error: Malformed import table: Unable to map DLL filename address 0x%lx\n", 446 | filename, 447 | dll_name_vaddr 448 | ); 449 | goto error; 450 | } 451 | 452 | for(;;) { 453 | int c0 = 0, c1 = 0; 454 | unsigned long methodname_vaddr; 455 | unsigned long methodname_fileoffset; 456 | 457 | methodname_vaddr = get32lsb_at(f, function_list_fileoffset); 458 | if(ferror(f) || feof(f)) { goto fileerror; } 459 | 460 | if(methodname_vaddr == 0) { break; } 461 | methodname_vaddr += imagebase; 462 | 463 | function_list_fileoffset += 4; 464 | 465 | methodname_fileoffset = virtual_to_fileoffset(f, methodname_vaddr); 466 | if(ferror(f) || feof(f)) { goto fileerror; } 467 | if(!methodname_fileoffset) { 468 | printf("%s: Error: Malformed import table: Unable to map method name address 0x%lx\n", 469 | filename, 470 | methodname_vaddr 471 | ); 472 | goto error; 473 | } 474 | 475 | // 476 | // Method name ending in 'W'? 477 | // 478 | safe_seek(f, methodname_fileoffset + 2); 479 | if(ferror(f) || feof(f)) { goto fileerror; } 480 | 481 | for(;;) { 482 | c0 = c1; 483 | c1 = fgetc(f); 484 | if(c1 == 0 || c1 == EOF) { break; } 485 | } 486 | if(c0 == 'W') { 487 | if(warned_functions < max_warned_functions) { 488 | if(warned_functions == 0) { 489 | printf("%s: Warning: The following Unicode imports are used:\n", 490 | filename 491 | ); 492 | } 493 | printf(" "); 494 | dumpstring(f, dll_name_fileoffset); 495 | printf(": "); 496 | dumpstring(f, methodname_fileoffset + 2); 497 | printf("\n"); 498 | } 499 | warned_functions++; 500 | } 501 | } 502 | } 503 | if(warned_functions > max_warned_functions) { 504 | printf(" ...plus %lu more\n", (warned_functions - max_warned_functions)); 505 | } 506 | } 507 | 508 | printf("%s: Done\n", filename); 509 | 510 | returnvalue = 0; 511 | goto done; 512 | fileerror: 513 | if((!f) || ferror(f)) { 514 | printf("%s: Error: %s\n", filename, strerror(errno)); 515 | } else if(feof(f)) { 516 | printf("%s: Error: Unexpected end of file\n", filename); 517 | } else { 518 | printf("%s: Error: Unknown error\n", filename); 519 | } 520 | goto error; 521 | error: 522 | returnvalue = 1; 523 | done: 524 | if(f) { fclose(f); } 525 | return returnvalue; 526 | } 527 | 528 | //////////////////////////////////////////////////////////////////////////////// 529 | 530 | int main(int argc, char** argv) { 531 | int error = 0; 532 | int i; 533 | 534 | normalize_argv0(argv[0]); 535 | 536 | if(argc < 2) { 537 | banner(); 538 | printf("Usage: %s pe_exe_file(s)\n", argv[0]); 539 | return 1; 540 | } 541 | 542 | for(i = 1; i < argc; i++) { 543 | if(fixpe(argv[i])) { error = 1; } 544 | } 545 | 546 | return error; 547 | } 548 | 549 | //////////////////////////////////////////////////////////////////////////////// 550 | -------------------------------------------------------------------------------- /pecompat.txt: -------------------------------------------------------------------------------- 1 | pecompat(1) 2 | =========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | pecompat - Maximize compatibility of a Windows PE file 8 | 9 | SYNOPSIS 10 | -------- 11 | *pecompat* 'exe_file' 12 | 13 | DESCRIPTION 14 | ----------- 15 | Patches a Windows 32-bit PE executable file to maximize its 16 | compatibility with older versions of Windows. 17 | 18 | Specifically, it: 19 | 20 | * Clears the linker time stamp 21 | * Sets the required OS version and subsystem to ``4.00'' (Windows 95) 22 | * Recalculates the PE checksum 23 | * Warns if any Unicode import functions are used 24 | -------------------------------------------------------------------------------- /rels.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "rels - Relative Searcher" 4 | #define COPYR "Copyright (C) 2002,2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | void report( 27 | off_t filepos, 28 | const uint8_t* src, 29 | size_t len, 30 | const char* type 31 | ) { 32 | fprinthex(stdout, filepos, 8); 33 | printf(": "); 34 | while(len--) { 35 | uint8_t c = *src++; 36 | printf("%02X ", c); 37 | } 38 | printf("(%s)\n", type); 39 | } 40 | 41 | //////////////////////////////////////////////////////////////////////////////// 42 | 43 | int matchtest( 44 | const uint8_t* string, 45 | const uint8_t* buffer, 46 | size_t increment, 47 | int shift 48 | ) { 49 | int16_t stringstart; 50 | int16_t bufferstart; 51 | for(;;) { 52 | uint8_t c = *string; 53 | if(!c) { return 1; } 54 | if(c != '.') { 55 | stringstart = ((int16_t)( c )) & 0xFF; 56 | bufferstart = ((int16_t)(*buffer)) & 0xFF; 57 | break; 58 | } 59 | string++; 60 | buffer += increment; 61 | } 62 | for(;;) { 63 | uint8_t c = *string; 64 | if(!c) { break; } 65 | if(c != '.') { 66 | int16_t stringnow = ((int16_t)( c )) & 0xFF; 67 | int16_t buffernow = ((int16_t)(*buffer)) & 0xFF; 68 | stringnow -= stringstart; 69 | buffernow -= bufferstart; 70 | stringnow <<= shift; 71 | if(stringnow != buffernow) { return 0; } 72 | } 73 | string++; 74 | buffer += increment; 75 | } 76 | return 1; 77 | } 78 | 79 | //////////////////////////////////////////////////////////////////////////////// 80 | 81 | off_t relsearch( 82 | const uint8_t* string, 83 | const char* filename 84 | ) { 85 | FILE* f = NULL; 86 | uint8_t* buffer = NULL; 87 | 88 | size_t stringlen; 89 | size_t buffersize; 90 | off_t matchesfound = 0; 91 | off_t bufferbase; 92 | size_t bufferpos; 93 | size_t bufferlen; 94 | 95 | // 96 | // Examine length of search string 97 | // 98 | stringlen = strlen((const char*)string); 99 | // Avoid overflow 100 | if(stringlen > (((size_t)(-1)) / 2)) { 101 | printf("String is too long\n"); // very rare case 102 | goto done; // very rare case 103 | } 104 | // 105 | // Allocate buffer 106 | // 107 | buffersize = 2 * stringlen - 1; 108 | if(buffersize < 4096) { 109 | buffersize = 4096; 110 | } 111 | buffer = malloc(buffersize); 112 | if(!buffer) { 113 | printf("Out of memory\n"); 114 | goto done; 115 | } 116 | 117 | f = fopen(filename, "rb"); 118 | if(!f) { goto error_f; } 119 | 120 | printf("%s: ", filename); 121 | bufferbase = 0; 122 | bufferpos = 0; 123 | bufferlen = 0; 124 | for(;;) { 125 | size_t readsize; 126 | if(bufferlen && ((buffersize - bufferpos) < (2 * stringlen - 1))) { 127 | memmove(buffer, buffer + bufferpos, bufferlen); 128 | bufferbase += bufferpos; 129 | bufferpos = 0; 130 | } 131 | readsize = buffersize - (bufferpos + bufferlen); 132 | if((readsize > 0) && (!feof(f))) { 133 | readsize = fread(buffer + (bufferpos + bufferlen), 1, readsize, f); 134 | bufferlen += readsize; 135 | } 136 | if(bufferlen < stringlen) break; 137 | if(matchtest(string, buffer + bufferpos, 1, 0)) { if(matchesfound < 1) printf("\n"); matchesfound++; report(bufferbase + bufferpos, buffer + bufferpos, stringlen, "normal"); } 138 | else if(matchtest(string, buffer + bufferpos, 1, 1)) { if(matchesfound < 1) printf("\n"); matchesfound++; report(bufferbase + bufferpos, buffer + bufferpos, stringlen, "double"); } 139 | if(bufferlen >= (2 * stringlen - 1)) { 140 | if(matchtest(string, buffer + bufferpos, 2, 0)) { if(matchesfound < 1) printf("\n"); matchesfound++; report(bufferbase + bufferpos, buffer + bufferpos, 2 * stringlen - 1, "wide"); } 141 | else if(matchtest(string, buffer + bufferpos, 2, 1)) { if(matchesfound < 1) printf("\n"); matchesfound++; report(bufferbase + bufferpos, buffer + bufferpos, 2 * stringlen - 1, "wide double"); } 142 | } 143 | bufferpos++; 144 | bufferlen--; 145 | } 146 | printf( 147 | "%lu match%s found\n", 148 | (unsigned long)matchesfound, 149 | (matchesfound == 1) ? "" : "es" 150 | ); 151 | 152 | goto done; 153 | 154 | error_f: 155 | printfileerror(f, filename); 156 | 157 | done: 158 | if(f != NULL) { fclose(f); } 159 | if(buffer != NULL) { free(buffer); } 160 | 161 | return matchesfound; 162 | } 163 | 164 | //////////////////////////////////////////////////////////////////////////////// 165 | 166 | int main(int argc, char **argv) { 167 | const uint8_t* string; 168 | int i; 169 | off_t total = 0; 170 | 171 | normalize_argv0(argv[0]); 172 | 173 | if(argc < 3) { 174 | banner(); 175 | printf( 176 | "Usage: %s string files\n" 177 | "\n" 178 | "Search string may include '.' characters as wildcards, but must include at\n" 179 | "least two non-wildcard characters.\n", 180 | argv[0] 181 | ); 182 | return 1; 183 | } 184 | string = (const uint8_t*)(argv[1]); 185 | { size_t nwc = 0; 186 | const uint8_t* s = string; 187 | for(;;) { 188 | uint8_t c = *s++; 189 | if(!c) { break; } 190 | if(c != '.') { nwc++; } 191 | } 192 | if(nwc < 2) { 193 | printf( 194 | "Search string must contain at least two non-wildcard characters\n" 195 | ); 196 | return 1; 197 | } 198 | } 199 | printf("Searching for \"%s\":\n", string); 200 | for(i = 2; i < argc; i++) { 201 | total += relsearch(string, argv[i]); 202 | } 203 | printf( 204 | "Total: %lu %s found\n", 205 | (unsigned long)total, 206 | (total == 1) ? "match" : "matches" 207 | ); 208 | return 0; 209 | } 210 | 211 | //////////////////////////////////////////////////////////////////////////////// 212 | -------------------------------------------------------------------------------- /rels.txt: -------------------------------------------------------------------------------- 1 | rels(1) 2 | ======= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | rels - Relative searcher 8 | 9 | SYNOPSIS 10 | -------- 11 | *rels* 'string' 'FILE'... 12 | 13 | DESCRIPTION 14 | ----------- 15 | Searches for a string in a group of files by comparing the relative 16 | difference between each letter. This allows finding strings in 17 | non-ASCII character sets. Also attempts double values (e.g. 00=A, 02=B, 18 | 04=C) and wide values (every other byte, as in UCS-2). A staple ROM 19 | hacking tool. 20 | 21 | Search strings may include ``.'' characters as wildcards, but must 22 | include at least two non-wildcard characters. 23 | -------------------------------------------------------------------------------- /screamf.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "screamf - .AMF to .S3M converter" 4 | #define COPYR "Copyright (C) 1996,2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | // S3M parapointer limit 27 | static const off_t S3MFILESIZELIMIT = (off_t)0xFFFFFLU; 28 | 29 | // Pattern size limit 30 | static const off_t S3MPATTERNSIZELIMIT = (off_t) 0xFFFFLU; 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | struct INSTRUMENT { 35 | char name [33]; 36 | char filename[14]; 37 | uint8_t sampled; 38 | uint32_t samplelength; 39 | uint32_t loopstart; 40 | uint32_t loopend; 41 | uint16_t c4spd; 42 | uint8_t defaultvol; 43 | uint8_t* sampledata; 44 | }; 45 | 46 | struct PLACE { 47 | int8_t note; 48 | int8_t octave; 49 | int8_t instrument; 50 | int8_t effect; 51 | int16_t volume; 52 | int16_t parameter; 53 | }; 54 | 55 | struct TRACK { 56 | struct PLACE place[64]; 57 | }; 58 | 59 | struct MODULE { 60 | char title[33]; 61 | uint8_t insnum; 62 | uint8_t ordnum; 63 | size_t tracknum; // AMF only 64 | uint8_t channels; 65 | int8_t panposition[32]; 66 | uint8_t tempo; 67 | uint8_t speed; 68 | }; 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | 72 | static void fput32lsb(uint32_t c, FILE* stream) { 73 | fputc(((c ) & 0xFF), stream); 74 | fputc(((c >> 8) & 0xFF), stream); 75 | fputc(((c >> 16) & 0xFF), stream); 76 | fputc(((c >> 24) & 0xFF), stream); 77 | } 78 | 79 | static void fput16lsb(uint16_t c, FILE* stream) { 80 | fputc(((c ) & 0xFF), stream); 81 | fputc(((c >> 8) & 0xFF), stream); 82 | } 83 | 84 | static uint32_t fget32lsb(FILE* stream) { 85 | uint32_t t; 86 | t = (((uint32_t)(fgetc(stream) & 0xFF)) << 0); 87 | t |= (((uint32_t)(fgetc(stream) & 0xFF)) << 8); 88 | t |= (((uint32_t)(fgetc(stream) & 0xFF)) << 16); 89 | t |= (((uint32_t)(fgetc(stream) & 0xFF)) << 24); 90 | return t; 91 | } 92 | 93 | static uint16_t fget16lsb(FILE* stream) { 94 | uint16_t t; 95 | t = (((uint16_t)(fgetc(stream) & 0xFF)) << 0); 96 | t |= (((uint16_t)(fgetc(stream) & 0xFF)) << 8); 97 | return t; 98 | } 99 | 100 | // 101 | // Align file offset to a paragraph (16 bytes) 102 | // 103 | static void alignpara(FILE* f) { 104 | while((ftello(f) & 0xF) != 0) { 105 | fputc(0, f); 106 | } 107 | } 108 | 109 | //////////////////////////////////////////////////////////////////////////////// 110 | // 111 | // Returns nonzero on error 112 | // 113 | static int amf_decodetrack( 114 | FILE* amffile, 115 | struct TRACK* track, 116 | uint8_t insnum 117 | ) { 118 | int16_t i,c,n,row,num,effect; 119 | struct PLACE* place = track->place; 120 | 121 | // 122 | // Initialize track to empty/unused 123 | // 124 | for(i = 0; i < 64; i++) { 125 | place[i].note = -1; 126 | place[i].octave = -1; 127 | place[i].instrument = -1; 128 | place[i].volume = -1; 129 | place[i].effect = -1; 130 | place[i].parameter = -1; 131 | } 132 | n = fgetc(amffile); 133 | num = fgetc(amffile); // lost? 134 | effect = fgetc(amffile); // lost? 135 | for(c = 0; c < n; c++) { 136 | row = fgetc(amffile); 137 | num = fgetc(amffile); 138 | effect = fgetc(amffile); 139 | 140 | if(row == 0xFF) { break; } 141 | if(row >= 64) { 142 | printf("row out of bounds"); 143 | goto errorat; 144 | } 145 | 146 | if(num == 0x80) { 147 | for(i = row; i < 64; i++) { 148 | if(effect >= insnum) { 149 | printf("instrument out of bounds"); 150 | goto errorat; 151 | } 152 | place[i].instrument = (int8_t)effect; 153 | } 154 | } else if(num > 0x80) { 155 | place[row].effect = num & 0x7F; 156 | place[row].parameter = (int8_t)effect; 157 | } else { 158 | place[row].note = num % 12; 159 | place[row].octave = num / 12; 160 | place[row].volume = effect; 161 | } 162 | } 163 | // 164 | // Postprocess the track 165 | // 166 | for(i = 0; i < 64; i++) { 167 | // 168 | // remove phantom instruments 169 | // 170 | if(place[i].note == -1) { 171 | place[i].instrument = -1; 172 | } 173 | // 174 | // Convert effects 175 | // 176 | switch(place[i].effect) { 177 | case -1: 178 | break; 179 | case 1: // Set Speed [A] 180 | place[i].effect='A'-64; 181 | break; 182 | case 2: // Volume Slide [D] 183 | place[i].effect='D'-64; 184 | // 185 | // Convert to S3M style 186 | // 187 | if(place[i].parameter < 0) { 188 | place[i].parameter=-(place[i].parameter); 189 | } else { 190 | place[i].parameter<<=4; 191 | } 192 | break; 193 | case 3: // Volume Change 194 | // (What's this doing in here?) 195 | place[i].volume = place[i].parameter; 196 | place[i].effect = -1; 197 | place[i].parameter = -1; 198 | break; 199 | case 4: // Portamento [E]+, [F]- 200 | if(place[i].parameter < 0) { 201 | place[i].effect='F'-64; 202 | place[i].parameter=-(place[i].parameter); 203 | } else { 204 | place[i].effect='E'-64; 205 | } 206 | break; 207 | case 6: // Tone Portamento [G] 208 | place[i].effect='G'-64; 209 | break; 210 | case 7: // Tremolo [R] 211 | place[i].effect='R'-64; 212 | break; 213 | case 8: // [J] 214 | place[i].effect='J'-64; 215 | break; 216 | case 9: // [H] 217 | place[i].effect='H'-64; 218 | break; 219 | case 10: // [L] (negative) 220 | place[i].effect='L'-64; 221 | place[i].parameter=-(place[i].parameter); 222 | break; 223 | case 11: // [K] (negative) 224 | place[i].effect='K'-64; 225 | place[i].parameter=-(place[i].parameter); 226 | break; 227 | case 12: // [C] 228 | place[i].effect='C'-64; 229 | break; 230 | case 13: // [B] 231 | place[i].effect='B'-64; 232 | break; 233 | case 15: // [Q] 234 | place[i].effect='Q'-64; 235 | break; 236 | case 16: // Set Sample Offset [O] 237 | place[i].effect='O'-64; 238 | break; 239 | case 17: // Find Volume Slide [DxF]+, [DFx]- 240 | place[i].effect='D'-64; 241 | // 242 | // convert to S3M style 243 | // 244 | if(place[i].parameter<(-14))place[i].parameter=(-14); 245 | if(place[i].parameter>( 14))place[i].parameter=( 14); 246 | if(place[i].parameter<0) { 247 | place[i].parameter=-(place[i].parameter); 248 | place[i].parameter|=0xF0; 249 | } else if(place[i].parameter>0) { 250 | place[i].parameter<<=4; 251 | place[i].parameter|=0x0F; 252 | } else { 253 | place[i].effect=-1; 254 | place[i].parameter=-1; 255 | } 256 | break; 257 | case 18: // Fine Portamento [EF]+, [FF]- 258 | if(place[i].parameter<0) { 259 | place[i].effect='F'-64; 260 | place[i].parameter=-(place[i].parameter); 261 | } else { 262 | place[i].effect='E'-64; 263 | } 264 | if(place[i].parameter>0xF)place[i].parameter=0xF; 265 | place[i].parameter|=0xF0; 266 | break; 267 | case 21: // Set Tempo [T] 268 | place[i].effect='T'-64; 269 | break; 270 | case 22: // Extra Fine Portamento [EE]+, [FE]- 271 | if(place[i].parameter<0) { 272 | place[i].effect='F'-64; 273 | place[i].parameter=-(place[i].parameter); 274 | } else { 275 | place[i].effect='E'-64; 276 | } 277 | if(place[i].parameter>0xF)place[i].parameter=0xF; 278 | place[i].parameter|=0xE0; 279 | break; 280 | case 23: // [X] 281 | place[i].effect='X'-64; 282 | break; 283 | default: // Remove the unknown effect 284 | printf( 285 | "Unknown effect %02X-%02X\n", 286 | place[i].effect, 287 | place[i].parameter 288 | ); 289 | place[i].effect=-1; 290 | break; 291 | } 292 | } 293 | return 0; 294 | 295 | errorat: 296 | printf(" @ 0x"); 297 | fprinthex(stdout, ftello(amffile), 1); 298 | printf("\n"); 299 | return 1; 300 | } 301 | 302 | static void amf_decodeinstrument( 303 | FILE* amffile, 304 | struct INSTRUMENT* instrument 305 | ) { 306 | size_t i; 307 | 308 | instrument->sampled = fgetc(amffile); 309 | 310 | for(i = 0; i < 32; i++) { 311 | instrument->name[i] = fgetc(amffile); 312 | } 313 | instrument->name[i] = 0; 314 | 315 | for(i = 0; i < 13; i++) { 316 | instrument->filename[i] = fgetc(amffile); 317 | } 318 | instrument->filename[i] = 0; 319 | 320 | fget32lsb(amffile); // TODO: unknown 321 | instrument->samplelength = fget32lsb(amffile); 322 | instrument->c4spd = fget16lsb(amffile); 323 | instrument->defaultvol = fgetc (amffile); 324 | 325 | instrument->loopstart = fget32lsb(amffile); 326 | instrument->loopend = fget32lsb(amffile); 327 | } 328 | 329 | static void s3m_writeinstrument( 330 | const struct INSTRUMENT* instrument, 331 | FILE* f 332 | ) { 333 | fputc(instrument->sampled, f); 334 | fwrite(instrument->filename, 1, 13, f); 335 | fput16lsb(0, f); // sample pointer - zero by default 336 | fput32lsb(instrument->samplelength, f); 337 | fput32lsb(instrument->loopstart, f); 338 | fput32lsb(instrument->loopend, f); 339 | fputc(instrument->defaultvol, f); 340 | fputc(0, f); 341 | fputc(0, f); 342 | if( 343 | instrument->loopstart != 0 || 344 | instrument->loopend != 0 345 | ) { 346 | fputc(1, f); 347 | } else { 348 | fputc(0, f); 349 | } 350 | fput32lsb(instrument->c4spd, f); 351 | // 352 | // Next 12 bytes are reserved 353 | // 354 | fput32lsb(0, f); 355 | fput32lsb(0, f); 356 | fput32lsb(0, f); 357 | fwrite(instrument->name, 1, 28, f); 358 | fputc('S', f); 359 | fputc('C', f); 360 | fputc('R', f); 361 | fputc('S', f); 362 | } 363 | 364 | //////////////////////////////////////////////////////////////////////////////// 365 | 366 | static uint8_t s3morderlist[256]; 367 | 368 | int main(int argc, char** argv) { 369 | int returncode = 0; 370 | 371 | FILE* amffile = NULL; 372 | FILE* s3mfile = NULL; 373 | 374 | const char* amffilename = ""; 375 | const char* s3mfilename = ""; 376 | 377 | uint16_t* ordertable = NULL; 378 | uint16_t* tracktable = NULL; 379 | struct INSTRUMENT* instrument = NULL; 380 | uint16_t* s3mpatterntable = NULL; 381 | uint8_t s3mpatterns; 382 | 383 | struct TRACK** amftrack = NULL; 384 | size_t amftracknum = 0; 385 | 386 | char amfheader[4]; 387 | struct MODULE module; 388 | off_t fpos_tracktable; 389 | off_t fpos_instrument; 390 | off_t fpos_samples; 391 | off_t s3minstrumentstart; 392 | off_t s3mpatternpointers; 393 | 394 | uint32_t i, j, k; 395 | 396 | normalize_argv0(argv[0]); 397 | 398 | if(argc != 3) { 399 | banner(); 400 | printf( 401 | "Usage: %s inputfile.amf outputfile.s3m\n", 402 | argv[0] 403 | ); 404 | goto error; 405 | } 406 | amffilename = argv[1]; 407 | amffile = fopen(amffilename, "rb"); 408 | if(!amffile) { goto error_amffile; } 409 | amfheader[0] = fgetc(amffile); 410 | amfheader[1] = fgetc(amffile); 411 | amfheader[2] = fgetc(amffile); 412 | amfheader[3] = 0; 413 | if(strcmp(amfheader, "AMF")) { 414 | printf("'%s' is not an AMF file\n",amffilename); 415 | goto conversionfailed; 416 | } 417 | if(fgetc(amffile) != 0xE) { 418 | printf("'%s' - unrecognized AMF version\n",amffilename); 419 | goto conversionfailed; 420 | } 421 | // 422 | // Ensure the S3M file doesn't already exist 423 | // 424 | s3mfilename = argv[2]; 425 | s3mfile = fopen(s3mfilename, "rb"); 426 | if(s3mfile != NULL) { 427 | printf("%s already exists; refusing to overwrite\n", s3mfilename); 428 | goto conversionfailed; 429 | } 430 | 431 | for(i = 0; i < 32; i++) { 432 | module.title[i] = fgetc(amffile); 433 | } 434 | module.title[i] = 0; 435 | 436 | module.insnum = fgetc (amffile); 437 | module.ordnum = fgetc (amffile); 438 | module.tracknum = fget16lsb(amffile); 439 | module.channels = fgetc (amffile); 440 | for(i = 0; i < 32; i++) { 441 | module.panposition[i] = fgetc(amffile); 442 | } 443 | module.tempo = fgetc (amffile); 444 | module.speed = fgetc (amffile); 445 | 446 | if(module.insnum == 0) { 447 | printf("Module apparently has zero instruments\n"); 448 | goto conversionfailed; 449 | } 450 | if(module.ordnum == 0) { 451 | printf("Module apparently has zero orders\n"); 452 | goto conversionfailed; 453 | } 454 | if(module.tracknum == 0) { 455 | printf("Module apparently has zero tracks\n"); 456 | goto conversionfailed; 457 | } 458 | if(module.channels == 0) { 459 | printf("Module apparently has zero channels\n"); 460 | goto conversionfailed; 461 | } 462 | if(module.channels > 32) { 463 | printf("Module reports too many channels (%u); max is 32\n", module.channels); 464 | goto conversionfailed; 465 | } 466 | 467 | // 468 | // Print out general information 469 | // 470 | printf( 471 | "Title..........%s\n" 472 | "Channels.......%lu\n" 473 | "Orders.........%lu\n" 474 | "Track entries..%lu\n" 475 | "Instruments....%lu\n" 476 | "Tempo..........%lu bpm\n" 477 | "Speed..........%lu\n\n", 478 | module.title, 479 | (unsigned long)module.channels, 480 | (unsigned long)module.ordnum, 481 | (unsigned long)module.tracknum, 482 | (unsigned long)module.insnum, 483 | (unsigned long)module.tempo, 484 | (unsigned long)module.speed 485 | ); 486 | 487 | // 488 | // Allocate memory for all kinds of things, except AMF tracks 489 | // 490 | ordertable = malloc(sizeof(*ordertable) * module.channels * module.ordnum); 491 | if(!ordertable) { printf("Out of memory\n"); goto conversionfailed; } 492 | 493 | if(module.tracknum > (((size_t)(-1)) / sizeof(*tracktable))) { 494 | printf("Too many tracks (%lu)\n", (unsigned long)module.tracknum); 495 | goto conversionfailed; 496 | } 497 | tracktable = malloc(sizeof(*tracktable) * module.tracknum); 498 | if(!tracktable) { printf("Out of memory\n"); goto conversionfailed; } 499 | 500 | instrument = malloc(sizeof(*instrument) * module.insnum); 501 | if(!instrument) { printf("Out of memory\n"); goto conversionfailed; } 502 | 503 | // 504 | // Determine the number of real tracks present 505 | // 506 | printf("Reading and decoding AMF...\n"); 507 | fpos_instrument = (off_t)0x4B + ((((off_t)module.channels + 1) * (off_t)module.ordnum) * 2); 508 | fpos_tracktable = (off_t)fpos_instrument + (off_t)65 * (off_t)module.insnum; 509 | fseeko(amffile, fpos_tracktable, SEEK_SET); 510 | for(i = 0; i < module.tracknum; i++) { 511 | tracktable[i] = fget16lsb(amffile); 512 | if(tracktable[i] > amftracknum) { 513 | amftracknum = tracktable[i]; 514 | } 515 | // make zero-based 516 | if(tracktable[i] > 0) { tracktable[i]--; } 517 | } 518 | 519 | // 520 | // Allocate space for real tracks 521 | // 522 | if(amftracknum > (((size_t)(-1)) / sizeof(*amftrack))) { 523 | printf("Too many amftracks (%lu)\n", (unsigned long)amftracknum); 524 | goto conversionfailed; 525 | } 526 | amftrack = malloc(sizeof(*amftrack) * amftracknum); 527 | if(!amftrack) { printf("Out of memory\n"); goto conversionfailed; } 528 | 529 | for(i = 0; i < amftracknum; i++) { amftrack[i] = NULL; } 530 | for(i = 0; i < amftracknum; i++) { 531 | amftrack[i] = malloc(sizeof(*amftrack[i])); 532 | if(!amftrack[i]) { printf("Out of memory\n"); goto conversionfailed; } 533 | } 534 | 535 | // 536 | // Decode the AMF tracks 537 | // 538 | for(i = 0; i < amftracknum; i++) { 539 | if(amf_decodetrack(amffile, amftrack[i], module.insnum)) { 540 | goto conversionfailed; 541 | } 542 | } 543 | // 544 | // Save this place, this is where the samples are stored 545 | // 546 | fpos_samples = ftello(amffile); 547 | // 548 | // Now decode the orderlist, replacing track tags with real track numbers 549 | // 550 | fseeko(amffile, 0x4B, SEEK_SET); 551 | for(i = 0; i < module.ordnum; i++) { 552 | fget16lsb(amffile); 553 | for(j = 0; j < module.channels; j++) { 554 | uint16_t x = fget16lsb(amffile); 555 | ordertable[i*module.channels+j] = tracktable[x ? x-1 : 0]; 556 | } 557 | } 558 | // 559 | // Now decode the instrument headers 560 | // 561 | for(i = 0; i < module.insnum; i++) { 562 | amf_decodeinstrument(amffile, instrument + i); 563 | } 564 | printf("Processing patterns..."); 565 | 566 | // 567 | // Rearrange the patterns in a more S3M-friendly way 568 | // 569 | s3mpatterntable = malloc(sizeof(*s3mpatterntable) * module.ordnum * module.channels); 570 | if(!s3mpatterntable) { printf("Out of memory\n"); goto conversionfailed; } 571 | 572 | // 573 | // Start numbering S3M patterns with 0 574 | // 575 | s3mpatterns = 0; 576 | // 577 | // Go through the entire AMF orderlist 578 | // 579 | for(i = 0; i < module.ordnum; i++) { 580 | // 581 | // Is there already a S3M pattern with this track configuration? 582 | // 583 | for(j = 0; j < s3mpatterns; j++) { 584 | if(!memcmp( 585 | s3mpatterntable + j * module.channels, 586 | ordertable + i * module.channels, 587 | sizeof(*s3mpatterntable) * module.channels 588 | )) { 589 | break; 590 | } 591 | } 592 | s3morderlist[i] = (uint8_t)j; 593 | if(j == s3mpatterns) { 594 | // We need to create a new pattern 595 | memmove( 596 | s3mpatterntable + j * module.channels, 597 | ordertable + i * module.channels, 598 | sizeof(*s3mpatterntable) * module.channels 599 | ); 600 | if(s3mpatterns >= 0xFF) { 601 | printf("Too many patterns!\n"); 602 | goto conversionfailed; 603 | } 604 | s3mpatterns++; 605 | } 606 | } 607 | printf(" (%u patterns used)\n", s3mpatterns); 608 | // 609 | // Now s3mpatterns holds the number of S3M patterns required. 610 | // (plus, we know how to set up each pattern now) 611 | // 612 | 613 | // 614 | // Try creating the S3M file 615 | // 616 | s3mfile = fopen(s3mfilename, "wb"); 617 | if(!s3mfile) { goto error_s3mfile; } 618 | // 619 | // Begin writing it 620 | // 621 | printf("Writing S3M header information...\n"); 622 | fwrite(module.title, 1, 28, s3mfile); 623 | fput32lsb(0x101A, s3mfile); 624 | // 625 | // OrdNum has to be even in the S3M file, so adjust it 626 | // 627 | fput16lsb((module.ordnum + 1) & (~1), s3mfile); 628 | 629 | fput16lsb(module.insnum, s3mfile); 630 | fput16lsb(s3mpatterns, s3mfile); 631 | fput16lsb(64, s3mfile); // flags 632 | fput16lsb(0x1301, s3mfile); // created with Scream Tracker 3.01 633 | fput16lsb(2, s3mfile); // unsigned samples 634 | fputc('S', s3mfile); 635 | fputc('C', s3mfile); 636 | fputc('R', s3mfile); 637 | fputc('M', s3mfile); 638 | fputc(0x40, s3mfile); // global volume 639 | fputc(module.speed, s3mfile); // initial speed 640 | fputc(module.tempo, s3mfile); // initial tempo 641 | fputc(0xB0, s3mfile); // master volume 642 | fputc(16, s3mfile); // ultraclick removal level=8 643 | fputc(0, s3mfile); // don't bother with pan positions 644 | // 645 | // next 10 bytes are reserved 646 | // 647 | fputc(0, s3mfile);fputc(0, s3mfile);fputc(0, s3mfile);fputc(0, s3mfile); 648 | fputc(0, s3mfile);fputc(0, s3mfile);fputc(0, s3mfile);fputc(0, s3mfile); 649 | fputc(0, s3mfile);fputc(0, s3mfile); 650 | // 651 | // Now set up the default LRLR... panning 652 | // 653 | for(i = 0; i < module.channels; i++) { 654 | fputc(((i & 1) << 3) | (i >> 1), s3mfile); 655 | } 656 | for(i = module.channels; i < 32; i++) { 657 | fputc(0xFF, s3mfile); 658 | } 659 | // 660 | // Write out the orderlist 661 | // 662 | for(i = 0; i < module.ordnum;i++) { 663 | fputc(s3morderlist[i], s3mfile); 664 | } 665 | // 666 | // Adjust for that even-ordnum thing 667 | // 668 | if(module.ordnum & 1) { 669 | fputc(0xFF, s3mfile); 670 | module.ordnum++; 671 | } 672 | // 673 | // Precompute and write out where all the instrument headers are going to be 674 | // 675 | s3minstrumentstart = ( 676 | ((0x60 + module.ordnum + 2 * module.insnum + 2 * s3mpatterns) + 0xF) & (~0xF) 677 | ); 678 | for(i = 0; i < module.insnum; i++) { 679 | fput16lsb((uint16_t)((s3minstrumentstart + 80 * i) >> 4), s3mfile); 680 | } 681 | // 682 | // Zero out the pattern parapointers for now 683 | // 684 | s3mpatternpointers = ftello(s3mfile); 685 | for(i = 0; i < s3mpatterns; i++) { 686 | fput16lsb(0, s3mfile); 687 | } 688 | // 689 | // Wait until the boundary is an even paragraph 690 | // 691 | alignpara(s3mfile); 692 | // 693 | // Write out all instrument headers (making up sample locations) 694 | // 695 | for(i = 0; i < module.insnum;i++) { 696 | s3m_writeinstrument(instrument + i, s3mfile); 697 | } 698 | 699 | // 700 | // Squeeze some extra bytes out of each track 701 | // 702 | for(i = 0; i < amftracknum; i++) { 703 | struct PLACE* place = amftrack[i]->place; 704 | for(j = 0; j < 64; j++) { 705 | if( 706 | place[j].note != -1 && 707 | place[j].instrument != -1 && 708 | place[j].volume == instrument[place[j].instrument].defaultvol 709 | ) { 710 | place[j].volume = -1; 711 | } 712 | } 713 | } 714 | 715 | // 716 | // Now encode and write patterns 717 | // 718 | printf("Writing patterns...\n"); 719 | for(i = 0; i < s3mpatterns; i++) { 720 | off_t pattern_start; 721 | off_t pattern_end; 722 | 723 | // 724 | // Align to the next paragraph offset 725 | // 726 | alignpara(s3mfile); 727 | 728 | // 729 | // Obtain the pattern pointer 730 | // 731 | pattern_start = ftello(s3mfile); 732 | if(pattern_start > S3MFILESIZELIMIT) { 733 | printf("S3M file is too large!\n"); 734 | goto conversionfailed; 735 | } 736 | 737 | // 738 | // Go back and write the pattern parapointer 739 | // 740 | fseeko(s3mfile, s3mpatternpointers + 2 * i, SEEK_SET); 741 | fput16lsb((uint16_t)(pattern_start >> 4), s3mfile); 742 | 743 | fseeko(s3mfile, pattern_start, SEEK_SET); 744 | 745 | // Dummy size - fill it in later 746 | fput16lsb(0, s3mfile); 747 | 748 | for(j = 0; j < 64; j++) { 749 | for(k = 0; k < module.channels; k++) { 750 | struct PLACE* thisplace = amftrack[ 751 | s3mpatterntable[module.channels * i + k] 752 | ]->place + j; 753 | 754 | uint8_t s3mbytewhat = 0; 755 | 756 | if(thisplace->note != -1) { s3mbytewhat |= 0x20; } 757 | if(thisplace->volume != -1) { s3mbytewhat |= 0x40; } 758 | if(thisplace->effect != -1) { s3mbytewhat |= 0x80; } 759 | 760 | if(s3mbytewhat != 0) { 761 | s3mbytewhat |= k; 762 | fputc(s3mbytewhat, s3mfile); 763 | if((s3mbytewhat & 0x20) != 0) { 764 | if(thisplace->octave > 0) { 765 | fputc(((thisplace->note)| 766 | (((thisplace->octave)-1)<<4)), s3mfile); 767 | fputc(thisplace->instrument+1, s3mfile); 768 | } else { // note cut 769 | fputc(254, s3mfile); 770 | fputc(0, s3mfile); 771 | } 772 | } 773 | if((s3mbytewhat & 0x40) != 0) { 774 | fputc(thisplace->volume , s3mfile); 775 | } 776 | if((s3mbytewhat & 0x80) != 0) { 777 | fputc(thisplace->effect , s3mfile); 778 | fputc(thisplace->parameter, s3mfile); 779 | } 780 | } 781 | } 782 | fputc(0, s3mfile); 783 | } 784 | 785 | // 786 | // Mark end of pattern 787 | // 788 | pattern_end = ftello(s3mfile); 789 | if((pattern_end - pattern_start) > S3MPATTERNSIZELIMIT) { 790 | printf("S3M pattern is too large!\n"); 791 | goto conversionfailed; 792 | } 793 | 794 | // 795 | // Go back and write the pattern size 796 | // 797 | fseeko(s3mfile, pattern_start, SEEK_SET); 798 | fput16lsb((uint16_t)(pattern_end - pattern_start), s3mfile); 799 | 800 | fseeko(s3mfile, pattern_end, SEEK_SET); 801 | } 802 | 803 | // 804 | // Now write out the samples 805 | // 806 | printf("Writing samples...\n"); 807 | fseeko(amffile, fpos_samples, SEEK_SET); 808 | for(i = 0; i < module.insnum; i++) { 809 | if(instrument[i].sampled == 1) { 810 | off_t sample_start; 811 | 812 | // 813 | // Align to the next paragraph offset 814 | // 815 | alignpara(s3mfile); 816 | 817 | // 818 | // Obtain the sample pointer 819 | // 820 | sample_start = ftello(s3mfile); 821 | if(sample_start > S3MFILESIZELIMIT) { 822 | printf("S3M file is too large!\n"); 823 | goto conversionfailed; 824 | } 825 | 826 | // 827 | // Go back and write the sample pointer in the instrument 828 | // 829 | fseeko(s3mfile, s3minstrumentstart + 80 * i + 14, SEEK_SET); 830 | fput16lsb((uint16_t)(sample_start >> 4), s3mfile); 831 | 832 | fseeko(s3mfile, sample_start, SEEK_SET); 833 | 834 | // 835 | // Copy the actual sample 836 | // 837 | for(j = 0; j < instrument[i].samplelength; j++) { 838 | fputc(fgetc(amffile), s3mfile); 839 | } 840 | } 841 | } 842 | printf("Done!\n"); 843 | // 844 | // Success (hopefully) 845 | // 846 | goto done; 847 | 848 | conversionfailed: 849 | printf("Conversion failed\n"); 850 | goto error; 851 | 852 | error_amffile: printfileerror(amffile, amffilename); goto error; 853 | error_s3mfile: printfileerror(s3mfile, s3mfilename); goto error; 854 | 855 | error: 856 | returncode = 1; 857 | 858 | done: 859 | // 860 | // Free up all allocated memory 861 | // 862 | if(ordertable != NULL) { free(ordertable ); } 863 | if(tracktable != NULL) { free(tracktable ); } 864 | if(instrument != NULL) { free(instrument ); } 865 | if(s3mpatterntable != NULL) { free(s3mpatterntable); } 866 | if(amftrack != NULL) { 867 | for(i = 0; i < amftracknum; i++) { 868 | if(amftrack[i] != NULL) { free(amftrack[i]); } 869 | } 870 | free(amftrack); 871 | } 872 | // 873 | // Close both files 874 | // 875 | if(amffile != NULL) { fclose(amffile); } 876 | if(s3mfile != NULL) { fclose(s3mfile); } 877 | 878 | return returncode; 879 | } 880 | 881 | //////////////////////////////////////////////////////////////////////////////// 882 | -------------------------------------------------------------------------------- /screamf.txt: -------------------------------------------------------------------------------- 1 | screamf(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | screamf - .AMF to .S3M converter 8 | 9 | SYNOPSIS 10 | -------- 11 | *screamf* 'amf_file' 's3m_file' 12 | 13 | DESCRIPTION 14 | ----------- 15 | Converts AMF music files to S3M. AMF was a format used exclusively by 16 | Otto Chrons' DOS Sound and Music Interface (DSMI) library and Dual 17 | Module Player. Various DOS games and demos in the 1990s used this 18 | format. AMF files were the result of running MOD2AMF on an existing 19 | S3M or other module; screamf does the opposite. It's round-trippable 20 | for the most part. 21 | -------------------------------------------------------------------------------- /uips.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "uips - Universal IPS patch create/apply utility" 4 | #define COPYR "Copyright (C) 1999,2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | static const off_t IPS_EOF = 0x00454F46L; 27 | static const off_t IPS_LIMIT = 0x01000000L; 28 | 29 | // 30 | // Wrapper for fopen that does various things 31 | // 32 | static FILE *my_fopen(const char *filename, const char *mode, off_t *size) { 33 | FILE *f = fopen(filename, mode); 34 | if(!f) { goto error; } 35 | if(size) { 36 | if(fseeko(f, 0, SEEK_END) == -1) { goto error; } 37 | *size = ftello(f); 38 | if(*size < 0) { goto error; } 39 | if(fseeko(f, 0, SEEK_SET) == -1) { goto error; } 40 | } 41 | return f; 42 | error: 43 | printfileerror(f, filename); 44 | if(f) { fclose(f); } 45 | return NULL; 46 | } 47 | 48 | // 49 | // Read a number from a file, MSB first 50 | // Returns -1 on end of file 51 | // 52 | static int32_t readvalue(FILE *f, size_t nbytes) { 53 | int32_t v = 0; 54 | while(nbytes--) { 55 | int c = fgetc(f); 56 | if(c == EOF) { 57 | return -1; 58 | } 59 | v = (v << 8) | (c & 0xFF); 60 | } 61 | return v; 62 | } 63 | 64 | // 65 | // Write a number to a file, MSB first 66 | // 67 | static void writevalue(int32_t value, FILE *f, size_t nbytes) { 68 | size_t i = nbytes << 3; 69 | while(nbytes--) { 70 | i -= 8; 71 | fputc(value >> i, f); 72 | } 73 | } 74 | 75 | // 76 | // Search for the next difference between the target file and a number of 77 | // source files 78 | // 79 | static off_t get_next_difference( 80 | off_t ofs, 81 | FILE **source_file, 82 | const off_t *source_size, 83 | size_t source_nfiles, 84 | FILE *target_file, 85 | off_t target_size 86 | ) { 87 | size_t i; 88 | if(ofs >= target_size) { 89 | return target_size; 90 | } 91 | fseeko(target_file, ofs, SEEK_SET); 92 | for(i = 0; i < source_nfiles; i++) { 93 | if(ofs >= source_size[i]) { 94 | return ofs; 95 | } 96 | } 97 | for(i = 0; i < source_nfiles; i++) { 98 | fseeko(source_file[i], ofs, SEEK_SET); 99 | } 100 | for(;;) { 101 | int tc = fgetc(target_file); 102 | if(tc == EOF) { 103 | return target_size; 104 | } 105 | for(i = 0; i < source_nfiles; i++) { 106 | if(fgetc(source_file[i]) != tc) { 107 | return ofs; 108 | } 109 | } 110 | ofs++; 111 | } 112 | } 113 | 114 | // 115 | // Search for the end of a difference block 116 | // 117 | static off_t get_difference_end( 118 | off_t ofs, 119 | off_t similar_limit, 120 | FILE **source_file, 121 | const off_t *source_size, 122 | size_t source_nfiles, 123 | FILE *target_file, 124 | off_t target_size 125 | ) { 126 | size_t i; 127 | off_t similar_rl = 0; 128 | if(ofs >= target_size) { 129 | return target_size; 130 | } 131 | fseeko(target_file, ofs, SEEK_SET); 132 | for(i = 0; i < source_nfiles; i++) { 133 | if(ofs >= source_size[i]) { 134 | return target_size; 135 | } 136 | } 137 | for(i = 0; i < source_nfiles; i++) { 138 | fseeko(source_file[i], ofs, SEEK_SET); 139 | } 140 | for(;;) { 141 | char is_different = 0; 142 | int tc = fgetc(target_file); 143 | if(tc == EOF) { 144 | return target_size - similar_rl; 145 | } 146 | for(i = 0; i < source_nfiles; i++) { 147 | int fc = fgetc(source_file[i]); 148 | if((fc == EOF) || (fc != tc)) { 149 | is_different = 1; 150 | } 151 | } 152 | ofs++; 153 | if(is_different) { 154 | similar_rl = 0; 155 | } else { 156 | similar_rl++; 157 | if(similar_rl == similar_limit) { 158 | break; 159 | } 160 | } 161 | } 162 | return ofs - similar_rl; 163 | } 164 | 165 | // 166 | // Encode a difference block into a patch file 167 | // 168 | static void encode_patch_block( 169 | FILE* patch_file, 170 | FILE* target_file, 171 | off_t ofs, 172 | off_t ofs_end 173 | ) { 174 | while(ofs < ofs_end) { 175 | off_t ofs_block_end; 176 | off_t rl; 177 | int c; 178 | // 179 | // Avoid accidental "EOF" marker 180 | // 181 | if(ofs == IPS_EOF) { 182 | ofs--; 183 | } 184 | // 185 | // Write the offset to the patch file 186 | // 187 | writevalue(ofs, patch_file, 3); 188 | fseeko(target_file, ofs, SEEK_SET); 189 | // 190 | // If there is a beginning run of at least 9 bytes, use it 191 | // 192 | c = fgetc(target_file); 193 | rl = 1; 194 | while( 195 | (fgetc(target_file) == c) && 196 | (rl < 0xFFFF) && 197 | ((ofs + rl) < ofs_end) 198 | ) { 199 | rl++; 200 | } 201 | // 202 | // Encode a run, if the run was long enough 203 | // 204 | if(rl >= 9) { 205 | writevalue( 0, patch_file, 2); 206 | writevalue(rl, patch_file, 2); 207 | writevalue( c, patch_file, 1); 208 | ofs += rl; 209 | continue; 210 | } 211 | // 212 | // Search for the end of the block. 213 | // The block ends if there's an internal run of at least 14, or an ending 214 | // run of at least 9, or the block length == 0xFFFF, or the block reaches 215 | // ofs_end. 216 | // 217 | fseeko(target_file, ofs, SEEK_SET); 218 | ofs_block_end = ofs; 219 | c = -1; 220 | while( 221 | (ofs_block_end < ofs_end) && 222 | ((ofs_block_end - ofs) < 0xFFFF) 223 | ) { 224 | int c2 = fgetc(target_file); 225 | ofs_block_end++; 226 | if(c == c2) { 227 | rl++; 228 | if(rl == 14) { 229 | ofs_block_end -= 14; 230 | break; 231 | } 232 | } else { 233 | rl = 1; 234 | c = c2; 235 | } 236 | } 237 | // 238 | // Look for a sufficiently long ending run 239 | // 240 | if((ofs_block_end == ofs_end) && (rl >= 9)) { 241 | ofs_block_end -= rl; 242 | if(ofs_block_end == IPS_EOF) { 243 | ofs_block_end++; 244 | } 245 | } 246 | // 247 | // Encode a regular patch block 248 | // 249 | writevalue(ofs_block_end - ofs, patch_file, 2); 250 | fseeko(target_file, ofs, SEEK_SET); 251 | while(ofs < ofs_block_end) { 252 | fputc(fgetc(target_file), patch_file); 253 | ofs++; 254 | } 255 | } 256 | } 257 | 258 | // 259 | // Create a patch given a list of source filenames and a target filename. 260 | // Returns 0 on success. 261 | // 262 | static int create_patch( 263 | const char *patch_filename, 264 | size_t source_nfiles, 265 | const char **source_filename, 266 | const char *target_filename 267 | ) { 268 | int returncode = 0; 269 | FILE *patch_file = NULL; 270 | FILE **source_file = NULL; 271 | off_t *source_size = NULL; 272 | FILE *target_file = NULL; 273 | off_t target_size; 274 | off_t ofs; 275 | size_t i; 276 | char will_truncate = 0; 277 | // 278 | // Allocate memory for list of source file streams and sizes 279 | // 280 | if( 281 | source_nfiles > (((size_t)(-1)) / sizeof(*source_file)) || 282 | source_nfiles > (((size_t)(-1)) / sizeof(*source_size)) || 283 | ((source_file = malloc(sizeof(*source_file) * source_nfiles)) == NULL) || 284 | ((source_size = malloc(sizeof(*source_size) * source_nfiles)) == NULL) 285 | ) { 286 | printf("Out of memory\n"); 287 | goto err; 288 | } 289 | for(i = 0; i < source_nfiles; i++) { source_file[i] = NULL; } 290 | // 291 | // Open target file 292 | // 293 | target_file = my_fopen(target_filename, "rb", &target_size); 294 | if(!target_file) { goto err; } 295 | // 296 | // Open source files 297 | // 298 | for(i = 0; i < source_nfiles; i++) { 299 | source_file[i] = my_fopen(source_filename[i], "rb", source_size + i); 300 | if(!source_file[i]) { goto err; } 301 | if(source_size[i] > target_size) { 302 | will_truncate = 1; 303 | } 304 | } 305 | // 306 | // Create patch file 307 | // 308 | patch_file = my_fopen(patch_filename, "wb", NULL); 309 | if(!patch_file) { goto err; } 310 | printf("Creating %s...\n", patch_filename); 311 | // 312 | // Write "PATCH" signature 313 | // 314 | if(fwrite("PATCH", 1, 5, patch_file) != 5) { goto err_patch_file; } 315 | // 316 | // Main patch creation loop 317 | // 318 | ofs = 0; 319 | for(;;) { 320 | off_t ofs_end; 321 | // 322 | // Search for next difference 323 | // 324 | ofs = get_next_difference( 325 | ofs, 326 | source_file, 327 | source_size, 328 | source_nfiles, 329 | target_file, 330 | target_size 331 | ); 332 | if(ofs == target_size) { 333 | break; 334 | } 335 | if(ofs >= IPS_LIMIT) { 336 | printf("Warning: Differences beyond 16MiB were ignored\n"); 337 | break; 338 | } 339 | // 340 | // Determine the length of the difference block 341 | // 342 | ofs_end = get_difference_end( 343 | ofs, 344 | 5, 345 | source_file, 346 | source_size, 347 | source_nfiles, 348 | target_file, 349 | target_size 350 | ); 351 | // 352 | // Encode the difference block into the patch file 353 | // 354 | encode_patch_block(patch_file, target_file, ofs, ofs_end); 355 | ofs = ofs_end; 356 | } 357 | // 358 | // Write EOF marker 359 | // 360 | writevalue(IPS_EOF, patch_file, 3); 361 | if(will_truncate) { 362 | if(target_size >= IPS_LIMIT) { 363 | printf("Warning: Can't truncate beyond 16MiB\n"); 364 | } else { 365 | writevalue(target_size, patch_file, 3); 366 | } 367 | } 368 | // 369 | // Finished 370 | // 371 | printf("Done\n"); 372 | goto no_err; 373 | 374 | err_patch_file: printfileerror(patch_file, patch_filename); goto err; 375 | 376 | err: 377 | returncode = 1; 378 | no_err: 379 | if(patch_file) { fclose(patch_file); } 380 | for(i = 0; i < source_nfiles; i++) { 381 | if(source_file[i]) { fclose(source_file[i]); } 382 | } 383 | if(target_file) { fclose(target_file); } 384 | if(source_file) { free(source_file); } 385 | if(source_size) { free(source_size); } 386 | return returncode; 387 | } 388 | 389 | // 390 | // Apply a patch to a given target. 391 | // Returns 0 on success. 392 | // 393 | static int apply_patch( 394 | const char* patch_filename, 395 | const char* target_filename 396 | ) { 397 | int returncode = 0; 398 | FILE *patch_file = NULL; 399 | FILE *target_file = NULL; 400 | off_t target_size; 401 | off_t ofs; 402 | // 403 | // Open patch file 404 | // 405 | patch_file = my_fopen(patch_filename, "rb", NULL); 406 | if(!patch_file) { goto err; } 407 | // 408 | // Verify first five characters 409 | // 410 | if( 411 | (fgetc(patch_file) != 'P') || 412 | (fgetc(patch_file) != 'A') || 413 | (fgetc(patch_file) != 'T') || 414 | (fgetc(patch_file) != 'C') || 415 | (fgetc(patch_file) != 'H') 416 | ) { 417 | printf("%s: Invalid patch file format\n", patch_filename); 418 | goto err; 419 | } 420 | // 421 | // Open target file 422 | // 423 | target_file = my_fopen(target_filename, "r+b", &target_size); 424 | if(!target_file) { goto err; } 425 | printf("Applying %s...\n", patch_filename); 426 | // 427 | // Main patch application loop 428 | // 429 | for(;;) { 430 | off_t ofs, len; 431 | off_t rlen = 0; 432 | int rchar = 0; 433 | // 434 | // Read the beginning of a patch record 435 | // 436 | ofs = readvalue(patch_file, 3); 437 | if(ofs == -1) { goto err_eof; } 438 | if(ofs == IPS_EOF) { 439 | break; 440 | } 441 | len = readvalue(patch_file, 2); 442 | if(len == -1) { goto err_eof; } 443 | if(!len) { 444 | rlen = readvalue(patch_file, 2); 445 | if(rlen == -1) { goto err_eof; } 446 | rchar = fgetc(patch_file); 447 | if(rchar == EOF) { goto err_eof; } 448 | } 449 | // 450 | // Seek to the appropriate position in the target file 451 | // 452 | if(ofs <= target_size) { 453 | fseeko(target_file, ofs, SEEK_SET); 454 | } else { 455 | fseeko(target_file, 0, SEEK_END); 456 | while(target_size < ofs) { 457 | fputc(0, target_file); 458 | target_size++; 459 | } 460 | } 461 | // 462 | // Apply patch block 463 | // 464 | if(len) { 465 | ofs += len; 466 | if(ofs > target_size) { 467 | target_size = ofs; 468 | } 469 | while(len--) { 470 | rchar = fgetc(patch_file); 471 | if(rchar == EOF) { goto err_eof; } 472 | fputc(rchar, target_file); 473 | } 474 | } else { 475 | ofs += rlen; 476 | if(ofs > target_size) { 477 | target_size = ofs; 478 | } 479 | while(rlen--) { 480 | fputc(rchar, target_file); 481 | } 482 | } 483 | } 484 | // 485 | // Perform truncation if necessary 486 | // 487 | fclose(target_file); 488 | target_file = NULL; 489 | ofs = readvalue(patch_file, 3); 490 | if(ofs != -1 && ofs < target_size) { 491 | if(truncate(target_filename, ofs) != 0) { 492 | printf("Warning: Truncate failed\n"); 493 | } 494 | } 495 | // 496 | // Finished 497 | // 498 | printf("Done\n"); 499 | goto no_err; 500 | 501 | err_eof: 502 | printf( 503 | "Error: %s: Unexpected end-of-file; patch incomplete\n", 504 | patch_filename 505 | ); 506 | 507 | err: 508 | returncode = 1; 509 | 510 | no_err: 511 | if(target_file) { fclose(target_file); } 512 | if( patch_file) { fclose( patch_file); } 513 | return returncode; 514 | } 515 | 516 | //////////////////////////////////////////////////////////////////////////////// 517 | 518 | int main( 519 | int argc, 520 | char **argv 521 | ) { 522 | int returncode = 1; 523 | char cmd; 524 | 525 | normalize_argv0(argv[0]); 526 | 527 | if(argc < 2) { 528 | banner(); 529 | goto usage; 530 | } 531 | cmd = argv[1][0]; 532 | if(cmd && argv[1][1]) { cmd = 0; } 533 | switch(cmd) { 534 | case 'c': 535 | case 'C': 536 | if(argc < 5) { 537 | goto usage; 538 | } 539 | returncode = create_patch( 540 | argv[2], 541 | argc - 4, 542 | (const char**)(argv + 3), 543 | argv[argc - 1] 544 | ); 545 | break; 546 | case 'a': 547 | case 'A': 548 | if(argc != 4) { 549 | goto usage; 550 | } 551 | returncode = apply_patch(argv[2], argv[3]); 552 | break; 553 | default: 554 | printf("Unknown command: %s\n", argv[1]); 555 | goto usage; 556 | } 557 | 558 | goto done; 559 | 560 | usage: 561 | printf( 562 | "Usage:\n" 563 | "To create an IPS patch:\n" 564 | " %s c patch_file source_file(s) target_file\n" 565 | "To apply an IPS patch:\n" 566 | " %s a patch_file target_file\n", 567 | argv[0], argv[0] 568 | ); 569 | returncode = 1; 570 | 571 | done: 572 | return returncode; 573 | } 574 | 575 | //////////////////////////////////////////////////////////////////////////////// 576 | -------------------------------------------------------------------------------- /uips.txt: -------------------------------------------------------------------------------- 1 | uips(1) 2 | ======= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | uips - Universal IPS patch create/apply utility 8 | 9 | SYNOPSIS 10 | -------- 11 | *uips* 'c' 'patch_file' 'source_file'... 'target_file' 12 | 13 | *uips* 'a' 'patch_file' 'target_file' 14 | 15 | DESCRIPTION 16 | ----------- 17 | uips is a utility for creating and applying IPS patches. IPS is an 18 | old patch format for binary files, limited to 16MiB in size, and not 19 | to be confused with the newer UPS format. Multiple source files and 20 | IPSv2 truncation are supported. 21 | 22 | A command invocation like ``uips c patch.ips file1 file2 file3 file4'' 23 | will create a patch file capable of transforming any of the files 24 | 'file1', 'file2', or 'file3' to 'file4'. 25 | -------------------------------------------------------------------------------- /vb2rip.txt: -------------------------------------------------------------------------------- 1 | vb2rip(1) 2 | ========= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | vb2rip - VB2 sound format ripping utility 8 | 9 | SYNOPSIS 10 | -------- 11 | *vb2rip* ['options'] 'FILE'... 12 | 13 | DESCRIPTION 14 | ----------- 15 | Rips music in .VB2 and similar formats found in PlayStation and 16 | PlayStation 2 games. 17 | 18 | Supported input formats: 19 | 20 | raw:: raw data (for experimentation) 21 | vb2:: Konami multi-song .BIN/.VB2 file (DDR, etc) 22 | 8:: 8 file (Suikoden) 23 | msa:: MSA file (Psyvariar, possibly other PS2 games) 24 | xa2:: XA2 file (Extreme-G 3, possibly other PS3 games) 25 | 26 | OPTIONS 27 | ------- 28 | *-fmt FORMAT*:: Specify the input file format 29 | *-o PATH*:: Specify the output file name, or specify the output 30 | directory 31 | *-mono*:: Treat input file as monoaural 32 | *-stereo*:: Treat input file as stereo 33 | *-rate N*:: Specify the sample rate 34 | *-interleave N*:: Specify the block interleave size 35 | *-skip N*:: Skip this many bytes after each block 36 | *-offset N*:: Start at the given offset in the input file 37 | *-endflag*:: Stop decoding when the sample end flag is reached 38 | *-noendflag*:: Ignore the sample end flag 39 | *-maxbytes N*:: Set the maximum number of input bytes to decode 40 | -------------------------------------------------------------------------------- /wordadd.c: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | #define TITLE "wordadd - Addition word puzzle solver" 4 | #define COPYR "Copyright (C) 2010 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 . 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | #include "common.h" 22 | #include "banner.h" 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Count trailing zeroes 27 | // 28 | static uint8_t ctztable[1024]; 29 | 30 | static void init_ctztable(void) { 31 | size_t i, j, k; 32 | ctztable[0] = 0; 33 | for(i = 1; i < 1024; i++) { 34 | for(k = 0, j = i; !(j & 1); k++, j >>= 1) { } 35 | ctztable[i] = k; 36 | } 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | enum { 42 | DIGIT = 0x0F, 43 | KNOWN = 0x10, 44 | NONZERO = 0x20 45 | }; 46 | 47 | static char** lines; 48 | static size_t n_lines; 49 | 50 | static uint8_t digitmap[256]; 51 | static uint16_t available = 0x3FF; 52 | 53 | static uint32_t solutions = 0; 54 | static char printresults = 0; 55 | 56 | static void init_puzzle(void) { 57 | size_t i; 58 | 59 | // 60 | // Reset digit maps 61 | // 62 | memset(digitmap, 0, 256); 63 | // 64 | // Digits not present: 0 65 | // 66 | digitmap[0] = KNOWN | 0; 67 | // 68 | // 0-9 are literal 69 | // 70 | for(i = 0; i < 10; i++) { 71 | digitmap['0' + i] = KNOWN | i; 72 | } 73 | // 74 | // First letter of each word must be nonzero 75 | // 76 | for(i = 0; i < n_lines; i++) { 77 | if(lines[i][0]) { 78 | digitmap[(uint8_t)lines[i][0]] |= NONZERO; 79 | } 80 | } 81 | } 82 | 83 | void print_puzzle(void) { 84 | size_t i; 85 | for(i = 0; i < n_lines; i++) { 86 | if(i == (n_lines - 1)) { 87 | printf(" = "); 88 | } else if(i > 0) { 89 | printf(" + "); 90 | } 91 | printf("%s", lines[i]); 92 | } 93 | printf("\n"); 94 | } 95 | 96 | void print_solution(void) { 97 | size_t i, j; 98 | for(i = 0; i < n_lines; i++) { 99 | if(i == (n_lines - 1)) { 100 | printf(" = "); 101 | } else if(i > 0) { 102 | printf(" + "); 103 | } 104 | for(j = 0; lines[i][j]; j++) { 105 | fputc('0' + (digitmap[(uint8_t)(lines[i][j])] & DIGIT), stdout); 106 | } 107 | } 108 | printf("\n"); 109 | } 110 | 111 | //////////////////////////////////////////////////////////////////////////////// 112 | 113 | void solve_puzzle( 114 | size_t line, 115 | size_t place, // 0 = rightmost 116 | int carry_in, 117 | char final 118 | ) { 119 | // 120 | // Obtain the current letter, or 0 if none 121 | // 122 | size_t len = strlen(lines[line]); 123 | uint8_t letter = 124 | (place < len) ? 125 | (letter = lines[line][(len - 1) - place]) : 0; 126 | 127 | if(letter) { final = 0; } 128 | 129 | 130 | if(line == (n_lines - 1)) { 131 | // 132 | // This is the sum line 133 | // 134 | 135 | if(final) { 136 | // 137 | // We have finished the addition 138 | // 139 | if(carry_in == 0) { 140 | // 141 | // Sum matches: Found a solution 142 | // 143 | if(printresults) { print_solution(); } 144 | solutions++; 145 | } 146 | } else { 147 | // 148 | // Get next carry digit 149 | // 150 | int carry_out = carry_in / 10; 151 | carry_in %= 10; 152 | 153 | if(digitmap[letter] & KNOWN) { 154 | // 155 | // Letter is mapped 156 | // 157 | if((digitmap[letter] & DIGIT) == carry_in) { 158 | // 159 | // Letter is mapped to the correct digit; move on 160 | // 161 | solve_puzzle(0, place + 1, carry_out, 1); 162 | } 163 | } else { 164 | // 165 | // Letter is not mapped yet. 166 | // We will need to map it to carry_in. 167 | // 168 | int mask = 1 << carry_in; 169 | if( 170 | (available & mask) && 171 | (carry_in || !(digitmap[letter] & NONZERO)) 172 | ) { 173 | // 174 | // Digit we need is available; map it and move on 175 | // 176 | digitmap[letter] |= KNOWN | carry_in; 177 | available ^= mask; 178 | 179 | solve_puzzle(0, place + 1, carry_out, 1); 180 | 181 | digitmap[letter] &= ~(KNOWN | DIGIT); 182 | available ^= mask; 183 | } 184 | } 185 | } 186 | } else { 187 | // 188 | // This is not the sum line 189 | // 190 | if(digitmap[letter] & KNOWN) { 191 | 192 | // 193 | // Letter is mapped; add it and move on 194 | // 195 | solve_puzzle(line + 1, place, carry_in + (digitmap[letter] & DIGIT), final); 196 | 197 | } else { 198 | // 199 | // Letter is not mapped 200 | // 201 | 202 | // 203 | // Try all available digits, and count the solutions 204 | // 205 | int t = available; 206 | if(digitmap[letter] & NONZERO) { t &= ~1; } 207 | for(; t; t &= t - 1) { 208 | int digit = ctztable[t]; 209 | int mask = 1 << digit; 210 | 211 | digitmap[letter] |= KNOWN | digit; 212 | available ^= mask; 213 | 214 | solve_puzzle(line + 1, place, carry_in + digit, final); 215 | 216 | digitmap[letter] &= ~(KNOWN | DIGIT); 217 | available ^= mask; 218 | } 219 | } 220 | } 221 | } 222 | 223 | static void solve_puzzle_all(void) { 224 | solve_puzzle(0, 0, 0, 1); 225 | } 226 | 227 | //////////////////////////////////////////////////////////////////////////////// 228 | 229 | int main(int argc, char** argv) { 230 | int i; 231 | // Bitmask of letters A-Z that were used 232 | uint32_t letters = 0; 233 | 234 | int lettercount; 235 | int avdigitcount; 236 | 237 | if(argc < 3) { 238 | banner(); 239 | printf( 240 | "Usage: %s words... sum\n" 241 | "Examples:\n" 242 | " %s BEEF BACON MEATS\n", 243 | argv[0], 244 | argv[0] 245 | ); 246 | return 1; 247 | } 248 | 249 | for(i = 1; i < argc; i++) { 250 | char* s = argv[i]; 251 | size_t j; 252 | for(j = 0; s[j]; j++) { 253 | int c = ((unsigned char)(s[j])); 254 | static const char* allletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 255 | static const char* alldigits = "0123456789"; 256 | if(isalpha(c)) { 257 | const char* t; 258 | s[j] = c = toupper(c); 259 | t = strchr(allletters, c); 260 | if(t) { 261 | letters |= ((uint32_t)1) << (t - allletters); 262 | continue; 263 | } 264 | } else if(isdigit(c)) { 265 | const char* t = strchr(alldigits, c); 266 | if(t) { 267 | // 268 | // Allow digits, but don't allow letters to use that digit 269 | // 270 | available &= ~(1 << (t - alldigits)); 271 | continue; 272 | } 273 | } 274 | printf("Not a word: \"%s\"\n", s); 275 | return 1; 276 | } 277 | } 278 | lines = argv + 1; 279 | n_lines = argc - 1; 280 | 281 | // 282 | // Count letters 283 | // 284 | lettercount = 0; 285 | for(i = 0; i < 26; i++) { 286 | lettercount += (letters >> i) & 1; 287 | } 288 | if(lettercount < 1) { 289 | printf("Not enough letters\n"); 290 | return 1; 291 | } 292 | if(lettercount > 10) { 293 | printf("Too many letters (%d)\n", lettercount); 294 | return 1; 295 | } 296 | 297 | // 298 | // Count available digits 299 | // 300 | avdigitcount = 0; 301 | for(i = 0; i < 10; i++) { 302 | avdigitcount += (available >> i) & 1; 303 | } 304 | if(lettercount > avdigitcount) { 305 | printf( 306 | "There are more letters (%d) than available digits (%d)\n", 307 | lettercount, 308 | avdigitcount 309 | ); 310 | return 1; 311 | } 312 | 313 | // 314 | // Solve 315 | // 316 | init_ctztable(); 317 | init_puzzle(); 318 | print_puzzle(); 319 | printresults = 1; 320 | solve_puzzle_all(); 321 | printf("Solutions: %lu\n", (unsigned long)solutions); 322 | 323 | return 0; 324 | } 325 | 326 | //////////////////////////////////////////////////////////////////////////////// 327 | -------------------------------------------------------------------------------- /wordadd.txt: -------------------------------------------------------------------------------- 1 | wordadd(1) 2 | ========== 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | wordadd - Addition word puzzle solver 8 | 9 | SYNOPSIS 10 | -------- 11 | *wordadd* 'words'... 'sum' 12 | 13 | DESCRIPTION 14 | ----------- 15 | Finds solutions to letter/digit substitution of the ``WORD + WORD = 16 | OTHER'' variety. Supports multiple adends and digits mixed in with 17 | letters. 18 | 19 | Example: wordadd BEEF BACON MEATS 20 | --------------------------------------------------------------------------------