├── .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 |
--------------------------------------------------------------------------------