1 /*
2 ucon64_misc.c - miscellaneous functions for uCON64
3
4 Copyright (c) 1999 - 2006 NoisyB
5 Copyright (c) 2001 - 2005, 2015 - 2021 dbjh
6 Copyright (c) 2001 Caz
7 Copyright (c) 2002 - 2003 Jan-Erik Karlsson (Amiga)
8
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #ifdef _MSC_VER
28 #pragma warning(push)
29 #pragma warning(disable: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
30 #endif
31 #include <ctype.h>
32 #ifdef _MSC_VER
33 #pragma warning(pop)
34 #endif
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #ifdef _MSC_VER
40 #pragma warning(push)
41 #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
42 #endif
43 #include <sys/stat.h>
44 #ifdef _MSC_VER
45 #pragma warning(pop)
46 #endif
47 #include "misc/archive.h"
48 #include "misc/bswap.h"
49 #include "misc/chksum.h"
50 #include "misc/file.h"
51 #include "misc/misc.h"
52 #include "misc/property.h"
53 #include "misc/string.h"
54 #include "misc/term.h"
55 #include "ucon64.h"
56 #include "ucon64_dat.h"
57 #include "ucon64_misc.h"
58
59
60 #ifdef USE_DISCMAGE
61 #ifdef DLOPEN
62 #include "misc/dlopen.h"
63
64
65 static void *libdm;
66 static uint32_t (*dm_get_version_ptr) (void) = NULL;
67 static const char *(*dm_get_version_s_ptr) (void) = NULL;
68 static void (*dm_set_gauge_ptr) (void (*) (int, int)) = NULL;
69 static void (*dm_nfo_ptr) (const dm_image_t *, int, int) = NULL;
70
71 static FILE *(*dm_fdopen_ptr) (dm_image_t *, int, const char *) = NULL;
72 static dm_image_t *(*dm_open_ptr) (const char *, uint32_t) = NULL;
73 static dm_image_t *(*dm_reopen_ptr) (const char *, uint32_t, dm_image_t *) = NULL;
74 static int (*dm_close_ptr) (dm_image_t *) = NULL;
75
76 static int (*dm_disc_read_ptr) (const dm_image_t *) = NULL;
77 static int (*dm_disc_write_ptr) (const dm_image_t *) = NULL;
78
79 static int (*dm_read_ptr) (char *, int, int, const dm_image_t *) = NULL;
80 static int (*dm_write_ptr) (const char *, int, int, const dm_image_t *) = NULL;
81
82 static dm_image_t *(*dm_toc_read_ptr) (dm_image_t *, const char *) = NULL;
83 static int (*dm_toc_write_ptr) (const dm_image_t *) = NULL;
84
85 static dm_image_t *(*dm_cue_read_ptr) (dm_image_t *, const char *) = NULL;
86 static int (*dm_cue_write_ptr) (const dm_image_t *) = NULL;
87
88 static int (*dm_rip_ptr) (const dm_image_t *, int, uint32_t) = NULL;
89 #endif // DLOPEN
90
91
92 static st_ucon64_obj_t discmage_obj[] =
93 {
94 {0, WF_SWITCH},
95 {0, WF_DEFAULT}
96 };
97
98 const st_getopt2_t discmage_usage[] =
99 {
100 {
101 NULL, 0, 0, 0,
102 NULL, "All disc-based consoles",
103 NULL
104 },
105 {
106 "disc", 0, 0, UCON64_DISC,
107 NULL, "force recognition",
108 &discmage_obj[0]
109 },
110 {
111 "rip", 1, 0, UCON64_RIP,
112 "N", "rip/dump track N from IMAGE",
113 &discmage_obj[1]
114 },
115 #if 0
116 {
117 "filerip", 1, 0, UCON64_FILERIP,
118 "N", "rip/dump files from a track N in IMAGE",
119 NULL
120 },
121 {
122 "cdmage", 1, 0, UCON64_CDMAGE,
123 "N", "like " OPTION_LONG_S "rip but writes always (padded) sectors with 2352 Bytes;\n"
124 "this is what CDmage would do",
125 &discmage_obj[1]
126 },
127 #endif
128 {
129 "bin2iso", 1, 0, UCON64_BIN2ISO,
130 "N", "convert track N to ISO (if possible) by resizing\n"
131 "sectors to 2048 Bytes",
132 &discmage_obj[1]
133 },
134 {
135 "isofix", 1, 0, UCON64_ISOFIX,
136 "N", "fix corrupted track N (if possible)\n"
137 "if PVD points to a bad DR offset it will add padding data\n"
138 "so actual DR gets located in right absolute address",
139 &discmage_obj[1]
140 },
141 {
142 "mkcue", 0, 0, UCON64_MKCUE,
143 NULL, "generate CUE sheet for IMAGE or existing TOC sheet",
144 &discmage_obj[1]
145 },
146 {
147 "mktoc", 0, 0, UCON64_MKTOC,
148 NULL, "generate TOC sheet for IMAGE or existing CUE sheet",
149 &discmage_obj[1]
150 },
151 {
152 // hidden option
153 "mksheet", 0, 0, UCON64_MKSHEET,
154 NULL, /* "same as " OPTION_LONG_S "mktoc and " OPTION_LONG_S "mkcue" */ NULL,
155 &discmage_obj[1]
156 },
157 {NULL, 0, 0, 0, NULL, NULL, NULL}
158 };
159 #endif // USE_DISCMAGE
160
161
162 const char *ucon64_msg[] =
163 {
164 "ERROR: Communication with backup unit failed\n" // PARPORT_ERROR
165 "TIP: Check cables and connection\n"
166 " Turn the backup unit off and on\n"
167 // " Split ROMs must be joined first\n" // handled with WF_NO_SPLIT
168 " Use " OPTION_LONG_S "port to specify a (different) parallel port address\n"
169 " Try different settings for the parallel port in the BIOS, UEFI or\n"
170 " setup software. \"ECP and EPP 1.9\" should give the best results\n"
171 " Read the backup unit's manual\n",
172
173 "WARNING: Could not auto detect the right ROM/IMAGE/console type\n" // CONSOLE_WARNING
174 "TIP: If this is a ROM or CD IMAGE you might try to force the recognition\n"
175 " For example, the force recognition switch for SNES is " OPTION_LONG_S "snes\n",
176
177 "Wrote output to %s\n", // WROTE
178 "ERROR: Cannot open \"%s\" for reading\n", // OPEN_READ_ERROR
179 "ERROR: Cannot open \"%s\" for writing\n", // OPEN_WRITE_ERROR
180 "ERROR: Cannot read from \"%s\"\n", // READ_ERROR
181 "ERROR: Cannot write to \"%s\"\n", // WRITE_ERROR
182 "ERROR: Not enough memory for buffer (%u bytes)\n", // BUFFER_ERROR
183 "ERROR: Not enough memory for ROM buffer (%u bytes)\n", // ROM_BUFFER_ERROR
184 "ERROR: Not enough memory for file buffer (%u bytes)\n", // FILE_BUFFER_ERROR
185 "DAT info: No ROM found with checksum 0x%08x\n", // DAT_NOT_FOUND
186 "WARNING: Support for DAT files is disabled, because \"ucon64_datdir\" (either\n" // DAT_NOT_ENABLED
187 " in the configuration file or the environment) points to an incorrect\n"
188 " directory. Read the FAQ for more information\n",
189 "Reading config file %s\n", // READ_CONFIG_FILE
190 "NOTE: %s not found or too old, support for discmage disabled\n", // NO_LIB
191 NULL
192 };
193
194 static st_ucon64_obj_t ucon64_option_obj[] =
195 {
196 {0, WF_SWITCH},
197 {0, WF_DEFAULT},
198 {0, WF_STOP},
199 {0, WF_NO_ROM},
200 {0, WF_DEFAULT | WF_STOP | WF_NO_ROM},
201 {0, WF_NO_ARCHIVE},
202 {0, WF_INIT},
203 {0, WF_INIT | WF_PROBE},
204 {0, WF_INIT | WF_PROBE | WF_NO_SPLIT},
205 {0, WF_INIT | WF_PROBE | WF_NO_CRC32}
206 };
207
208 const st_getopt2_t ucon64_options_usage[] =
209 {
210 {
211 NULL, 0, 0, 0,
212 NULL, "Options",
213 NULL
214 },
215 {
216 "o", 1, 0, UCON64_O,
217 "DIRECTORY", "specify output directory",
218 &ucon64_option_obj[0]
219 },
220 {
221 "r", 0, 0, UCON64_R,
222 NULL, "process subdirectories recursively",
223 &ucon64_option_obj[0]
224 },
225 {
226 "nbak", 0, 0, UCON64_NBAK,
227 NULL, "prevents backup files (*.BAK)",
228 &ucon64_option_obj[0]
229 },
230 #ifdef USE_ANSI_COLOR
231 {
232 "ncol", 0, 0, UCON64_NCOL,
233 NULL, "disable ANSI colors in output",
234 &ucon64_option_obj[0]
235 },
236 #endif
237 #if defined USE_PARALLEL || defined USE_LIBCD64 || defined USE_USB
238 {
239 "port", 1, 0, UCON64_PORT,
240 "PORT", "specify "
241 #ifdef USE_USB
242 "USB"
243 #endif
244 #if (defined USE_PARALLEL || defined USE_LIBCD64) && defined USE_USB
245 " or "
246 #endif
247 #if defined USE_PARALLEL || defined USE_LIBCD64
248 "parallel"
249 #endif
250 " PORT" OPTARG_S "{"
251 #ifdef USE_USB
252 "USB0,USB1,..."
253 #endif
254 #if (defined USE_PARALLEL || defined USE_LIBCD64) && defined USE_USB
255 " "
256 #endif
257 #if defined USE_PARALLEL || defined USE_LIBCD64
258 "3bc,378,278,..."
259 #endif
260 "}"
261 #if defined USE_PARALLEL || defined USE_LIBCD64
262 "\n"
263 "In order to connect a backup unit to a PC's parallel port\n"
264 "you need a standard bidirectional parallel cable"
265 #endif
266 ,
267 &ucon64_option_obj[0]
268 },
269 #endif // defined USE_PARALLEL || defined USE_LIBCD64 || defined USE_USB
270 #ifdef USE_PARALLEL
271 {
272 "xreset", 0, 0, UCON64_XRESET,
273 NULL, "reset parallel port",
274 &ucon64_option_obj[3] // it's NOT a stop option
275 },
276 #endif
277 {
278 "hdn", 1, 0, UCON64_HDN,
279 "N", "force ROM has backup unit/emulator header with size of N Bytes",
280 &ucon64_option_obj[0]
281 },
282 {
283 "hd", 0, 0, UCON64_HD,
284 NULL, "same as " OPTION_LONG_S "hdn" OPTARG_S "512\n"
285 "most backup units use a header with a size of 512 Bytes",
286 &ucon64_option_obj[0]
287 },
288 {
289 "nhd", 0, 0, UCON64_NHD,
290 NULL, "force ROM has no backup unit/emulator header",
291 &ucon64_option_obj[0]
292 },
293 {
294 "ns", 0, 0, UCON64_NS,
295 NULL, "force ROM is not split",
296 &ucon64_option_obj[0]
297 },
298 {
299 "e", 0, 0, UCON64_E,
300 NULL, "emulate/run ROM (check " PROPERTY_HOME_RC("ucon64") " for all Emulator settings)",
301 &ucon64_option_obj[1]
302 },
303 {
304 "crc", 0, 0, UCON64_CRC,
305 NULL, "show CRC32 value of ROM",
306 &ucon64_option_obj[9]
307 },
308 {
309 "sha1", 0, 0, UCON64_SHA1,
310 NULL, "show SHA1 value of ROM",
311 &ucon64_option_obj[9]
312 },
313 {
314 "md5", 0, 0, UCON64_MD5,
315 NULL, "show MD5 value of ROM",
316 &ucon64_option_obj[9]
317 },
318 {
319 "ls", 0, 0, UCON64_LS,
320 NULL, "generate ROM list for all recognized ROMs",
321 &ucon64_option_obj[7]
322 },
323 {
324 "lsv", 0, 0, UCON64_LSV,
325 NULL, "like " OPTION_LONG_S "ls but more verbose",
326 &ucon64_option_obj[7]
327 },
328 {
329 "hex", 2, 0, UCON64_HEX,
330 "O1[:O2]", "show ROM as hexdump\n"
331 "O1" OPTARG_S "optional offset (decimal/hexadecimal) of start\n"
332 "O2" OPTARG_S "optional offset (decimal/hexadecimal) of end",
333 NULL
334 },
335 {
336 "bit", 2, 0, UCON64_BIT,
337 "O1[:O2]", "show ROM as bitdump",
338 NULL
339 },
340 {
341 "code", 2, 0, UCON64_CODE,
342 "O1[:O2]", "show ROM as code",
343 NULL
344 },
345 {
346 "print", 2, 0, UCON64_PRINT,
347 "O1[:O2]", "show ROM in printable characters",
348 NULL
349 },
350 {
351 "find", 1, 0, UCON64_FIND,
352 "STRING", "find STRING in ROM (wildcard: '?')",
353 &ucon64_option_obj[6]
354 },
355 {
356 "findi", 1, 0, UCON64_FINDI,
357 "STR", "like " OPTION_LONG_S "find but ignores the case of alpha bytes",
358 &ucon64_option_obj[6]
359 },
360 {
361 "findr", 1, 0, UCON64_FINDR,
362 "STR", "like " OPTION_LONG_S "find but looks also for shifted/relative similarities\n"
363 "(no wildcard supported)",
364 &ucon64_option_obj[6]
365 },
366 {
367 "hfind", 1, 0, UCON64_HFIND,
368 "HEX", "find HEX codes in ROM; use quotation " OPTION_LONG_S "hfind" OPTARG_S "\"75 ? 4f 4e\"\n"
369 "(wildcard: '?')",
370 &ucon64_option_obj[6]
371 },
372 {
373 "hfindr", 1, 0, UCON64_HFINDR,
374 "HEX", "like " OPTION_LONG_S "hfind but looks also for shifted/relative similarities\n"
375 "(no wildcard supported)",
376 &ucon64_option_obj[6]
377 },
378 {
379 "dfind", 1, 0, UCON64_DFIND,
380 "DEC", "find DEC values in ROM; use quotation " OPTION_LONG_S "dfind" OPTARG_S "\"117 ? 79 78\"\n"
381 "(wildcard: '?')",
382 &ucon64_option_obj[6]
383 },
384 {
385 "dfindr", 1, 0, UCON64_DFINDR,
386 "DEC", "like " OPTION_LONG_S "dfind but looks also for shifted/relative similarities\n"
387 "(no wildcard supported)",
388 &ucon64_option_obj[6]
389 },
390 {
391 "c", 1, 0, UCON64_C,
392 "FILE", "compare FILE with ROM for differences",
393 NULL
394 },
395 {
396 "cs", 1, 0, UCON64_CS,
397 "FILE", "compare FILE with ROM for similarities",
398 NULL
399 },
400 {
401 "help", 2, 0, UCON64_HELP,
402 "WHAT", "display help and exit\n"
403 "WHAT" OPTARG_S "\"long\" show long help (default)\n"
404 "WHAT" OPTARG_S "\"pad\" show help for padding ROMs\n"
405 "WHAT" OPTARG_S "\"dat\" show help for DAT support\n"
406 "WHAT" OPTARG_S "\"patch\" show help for patching ROMs\n"
407 "WHAT" OPTARG_S "\"backup\" show help for backup units\n"
408 #ifdef USE_DISCMAGE
409 "WHAT" OPTARG_S "\"disc\" show help for DISC image support\n"
410 #endif
411 OPTION_LONG_S "help " OPTION_LONG_S "snes would show only SNES related help",
412 &ucon64_option_obj[2]
413 },
414 {
415 "version", 0, 0, UCON64_VER,
416 NULL, "output version information and exit",
417 &ucon64_option_obj[2]
418 },
419 {
420 "q", 0, 0, UCON64_Q,
421 NULL, "be quiet (don't show ROM info)",
422 &ucon64_option_obj[0]
423 },
424 #if 0
425 {
426 "qq", 0, 0, UCON64_QQ,
427 NULL, "be even more quiet",
428 &ucon64_option_obj[0]
429 },
430 #endif
431 {
432 "v", 0, 0, UCON64_V,
433 NULL, "be more verbose (show backup unit headers also)",
434 &ucon64_option_obj[0]
435 },
436 // hidden options
437 {
438 "crchd", 0, 0, UCON64_CRCHD, // backward compat.
439 NULL, NULL,
440 &ucon64_option_obj[9]
441 },
442 {
443 "file", 1, 0, UCON64_FILE, // obsolete?
444 NULL, NULL,
445 &ucon64_option_obj[0]
446 },
447 {
448 "frontend", 0, 0, UCON64_FRONTEND, // no usage?
449 NULL, NULL,
450 &ucon64_option_obj[0]
451 },
452 {
453 "?", 0, 0, UCON64_HELP, // same as --help
454 NULL, NULL,
455 &ucon64_option_obj[2]
456 },
457 {
458 "h", 0, 0, UCON64_HELP, // same as --help
459 NULL, NULL,
460 &ucon64_option_obj[2]
461 },
462 {
463 "id", 0, 0, UCON64_ID, // currently only used in snes.c
464 NULL, NULL,
465 &ucon64_option_obj[0]
466 },
467 {
468 "rom", 0, 0, UCON64_ROM, // obsolete?
469 NULL, NULL,
470 &ucon64_option_obj[0]
471 },
472 {
473 "rename", 0, 0, UCON64_RDAT, // is now "rdat"
474 NULL, NULL,
475 &ucon64_option_obj[8]
476 },
477 {
478 "force63", 0, 0, UCON64_RJOLIET, // is now "rjoilet"
479 NULL, NULL,
480 &ucon64_option_obj[5]
481 },
482 {
483 "rr83", 0, 0, UCON64_R83, // is now "r83"
484 NULL, NULL,
485 &ucon64_option_obj[5]
486 },
487 {
488 "83", 0, 0, UCON64_R83, // is now "r83"
489 NULL, NULL,
490 &ucon64_option_obj[5]
491 },
492 #if 0
493 {
494 "xcdrw", 0, 0, UCON64_XCDRW, // obsolete
495 NULL, NULL,
496 &ucon64_option_obj[4]
497 },
498 {
499 "cdmage", 1, 0, UCON64_CDMAGE, // obsolete
500 NULL, NULL,
501 &ucon64_option_obj[1]
502 },
503 #endif
504 {NULL, 0, 0, 0, NULL, NULL, NULL}
505 };
506
507 static st_ucon64_obj_t ucon64_padding_obj[] =
508 {
509 {0, WF_DEFAULT},
510 {0, WF_INIT | WF_NO_SPLIT}
511 };
512
513 const st_getopt2_t ucon64_padding_usage[] =
514 {
515 {
516 NULL, 0, 0, 0,
517 NULL, "Padding",
518 NULL
519 },
520 {
521 "ispad", 0, 0, UCON64_ISPAD,
522 NULL, "check if ROM is padded",
523 &ucon64_padding_obj[1]
524 },
525 {
526 "pad", 0, 0, UCON64_PAD,
527 NULL, "pad ROM to next Mb",
528 &ucon64_padding_obj[0]
529 },
530 {
531 "p", 0, 0, UCON64_P,
532 NULL, "same as " OPTION_LONG_S "pad",
533 &ucon64_padding_obj[0]
534 },
535 {
536 "padn", 1, 0, UCON64_PADN,
537 "N", "pad ROM to N Bytes (put Bytes with value 0x00 after end)",
538 &ucon64_padding_obj[0]
539 },
540 {
541 "strip", 1, 0, UCON64_STRIP,
542 "N", "strip N Bytes from end of ROM",
543 NULL
544 },
545 {
546 "stpn", 1, 0, UCON64_STPN,
547 "N", "strip N Bytes from start of ROM",
548 NULL
549 },
550 {
551 "stp", 0, 0, UCON64_STP,
552 NULL, "same as " OPTION_LONG_S "stpn" OPTARG_S "512\n"
553 "most backup units use a header with a size of 512 Bytes",
554 NULL
555 },
556 {
557 "insn", 1, 0, UCON64_INSN,
558 "N", "insert N Bytes (0x00) before ROM",
559 NULL
560 },
561 {
562 "ins", 0, 0, UCON64_INS,
563 NULL, "same as " OPTION_LONG_S "insn" OPTARG_S "512\n"
564 "most backup units use a header with a size of 512 Bytes",
565 NULL
566 },
567 {
568 "split", 1, 0, UCON64_SPLIT,
569 "N", "split ROM in parts of N Bytes (including possible header)\n"
570 "use " OPTION_S "s/" OPTION_LONG_S "ssize to split for a specific console or backup unit",
571 NULL
572 },
573 {NULL, 0, 0, 0, NULL, NULL, NULL}
574 };
575
576
577 #ifdef USE_DISCMAGE
578 int
ucon64_load_discmage(void)579 ucon64_load_discmage (void)
580 {
581 uint32_t version;
582 #ifdef DLOPEN
583 const char *p = "/usr/local/lib/libdiscmage.so";
584 if (p)
585 strcpy (ucon64.discmage_path, p);
586 else
587 *ucon64.discmage_path = '\0';
588
589 // if ucon64.discmage_path points to an existing file then load it
590 if (!access (ucon64.discmage_path, F_OK))
591 {
592 u_func_ptr_t sym;
593
594 libdm = open_module (ucon64.discmage_path);
595
596 sym.void_ptr = get_symbol (libdm, "dm_get_version");
597 dm_get_version_ptr = (uint32_t (*) (void)) sym.func_ptr;
598 version = dm_get_version_ptr ();
599 if (version < LIB_VERSION (UCON64_DM_VERSION_MAJOR,
600 UCON64_DM_VERSION_MINOR,
601 UCON64_DM_VERSION_STEP))
602 {
603 printf ("WARNING: Your libdiscmage is too old (%u.%u.%u)\n"
604 " You need at least version %u.%u.%u\n\n",
605 (unsigned int) version >> 16,
606 (unsigned int) ((version >> 8) & 0xff),
607 (unsigned int) (version & 0xff),
608 UCON64_DM_VERSION_MAJOR,
609 UCON64_DM_VERSION_MINOR,
610 UCON64_DM_VERSION_STEP);
611 return 0;
612 }
613 else
614 {
615 sym.void_ptr = get_symbol (libdm, "dm_get_version_s");
616 dm_get_version_s_ptr = (const char *(*) (void)) sym.func_ptr;
617 sym.void_ptr = get_symbol (libdm, "dm_set_gauge");
618 dm_set_gauge_ptr = (void (*) (void (*) (int, int))) sym.func_ptr;
619
620 sym.void_ptr = get_symbol (libdm, "dm_open");
621 dm_open_ptr = (dm_image_t *(*) (const char *, uint32_t)) sym.func_ptr;
622 sym.void_ptr = get_symbol (libdm, "dm_reopen");
623 dm_reopen_ptr = (dm_image_t *(*) (const char *, uint32_t, dm_image_t *)) sym.func_ptr;
624 sym.void_ptr = get_symbol (libdm, "dm_fdopen");
625 dm_fdopen_ptr = (FILE *(*) (dm_image_t *, int, const char *)) sym.func_ptr;
626 sym.void_ptr = get_symbol (libdm, "dm_close");
627 dm_close_ptr = (int (*) (dm_image_t *)) sym.func_ptr;
628 sym.void_ptr = get_symbol (libdm, "dm_nfo");
629 dm_nfo_ptr = (void (*) (const dm_image_t *, int, int)) sym.func_ptr;
630
631 sym.void_ptr = get_symbol (libdm, "dm_read");
632 dm_read_ptr = (int (*) (char *, int, int, const dm_image_t *)) sym.func_ptr;
633 sym.void_ptr = get_symbol (libdm, "dm_write");
634 dm_write_ptr = (int (*) (const char *, int, int, const dm_image_t *)) sym.func_ptr;
635
636 sym.void_ptr = get_symbol (libdm, "dm_disc_read");
637 dm_disc_read_ptr = (int (*) (const dm_image_t *)) sym.func_ptr;
638 sym.void_ptr = get_symbol (libdm, "dm_disc_write");
639 dm_disc_write_ptr = (int (*) (const dm_image_t *)) sym.func_ptr;
640
641 sym.void_ptr = get_symbol (libdm, "dm_toc_read");
642 dm_toc_read_ptr = (dm_image_t *(*) (dm_image_t *, const char *)) sym.func_ptr;
643 sym.void_ptr = get_symbol (libdm, "dm_toc_write");
644 dm_toc_write_ptr = (int (*) (const dm_image_t *)) sym.func_ptr;
645
646 sym.void_ptr = get_symbol (libdm, "dm_cue_read");
647 dm_cue_read_ptr = (dm_image_t *(*) (dm_image_t *, const char *)) sym.func_ptr;
648 sym.void_ptr = get_symbol (libdm, "dm_cue_write");
649 dm_cue_write_ptr = (int (*) (const dm_image_t *)) sym.func_ptr;
650
651 sym.void_ptr = get_symbol (libdm, "dm_rip");
652 dm_rip_ptr = (int (*) (const dm_image_t *, int, uint32_t)) sym.func_ptr;
653
654 return 1;
655 }
656 }
657 else
658 return 0;
659 #else // !defined DLOPEN
660 #ifdef DJGPP
661 {
662 /*
663 The following piece of code makes the DLL "search" behavior a bit like
664 the search behavior for Windows programs. A bit, because the import
665 library just opens the file with the name that is stored in
666 djimport_path. It won't search for the DXE in the Windows system
667 directory, nor will it search the directories of the PATH environment
668 variable.
669 */
670 extern char djimport_path[FILENAME_MAX];
671 char dir[FILENAME_MAX];
672 size_t n, l = strnlen (ucon64.argv[0], sizeof djimport_path - 1);
673
674 strncpy (djimport_path, ucon64.argv[0], l)[l] = '\0'; // use djimport_path as tmp buf
675 dirname2 (djimport_path, dir);
676 snprintf (djimport_path, FILENAME_MAX, "%s" DIR_SEPARATOR_S "%s", dir,
677 "discmage.dxe");
678 djimport_path[FILENAME_MAX - 1] = '\0';
679 // this is specific to DJGPP - not necessary, but prevents confusion
680 l = strlen (djimport_path);
681 for (n = 0; n < l; n++)
682 if (djimport_path[n] == '/')
683 djimport_path[n] = '\\';
684 }
685 #endif // DJGPP
686 version = dm_get_version ();
687 if (version < LIB_VERSION (UCON64_DM_VERSION_MAJOR,
688 UCON64_DM_VERSION_MINOR,
689 UCON64_DM_VERSION_STEP))
690 {
691 printf ("WARNING: Your libdiscmage is too old (%u.%u.%u)\n"
692 " You need at least version %u.%u.%u\n\n",
693 (unsigned int) version >> 16,
694 (unsigned int) ((version >> 8) & 0xff),
695 (unsigned int) (version & 0xff),
696 UCON64_DM_VERSION_MAJOR,
697 UCON64_DM_VERSION_MINOR,
698 UCON64_DM_VERSION_STEP);
699 return 0;
700 }
701 return 1; // discmage could be "loaded"
702 #endif // !defined DLOPEN
703 }
704
705
706 void
discmage_gauge(int pos,int size)707 discmage_gauge (int pos, int size)
708 {
709 static time_t init_time = 0;
710
711 if (!init_time || !pos /* || !size */)
712 init_time = time (NULL);
713
714 ucon64_gauge (init_time, pos, size);
715 }
716
717
718 #ifdef DLOPEN
719 uint32_t
dm_get_version(void)720 dm_get_version (void)
721 {
722 return dm_get_version_ptr ();
723 }
724
725
726 const char *
dm_get_version_s(void)727 dm_get_version_s (void)
728 {
729 return dm_get_version_s_ptr ();
730 }
731
732
733 void
dm_set_gauge(void (* a)(int,int))734 dm_set_gauge (void (*a) (int, int))
735 {
736 dm_set_gauge_ptr (a);
737 }
738
739
740 FILE *
dm_fdopen(dm_image_t * a,int b,const char * c)741 dm_fdopen (dm_image_t *a, int b, const char *c)
742 {
743 return dm_fdopen_ptr (a, b, c);
744 }
745
746
747 dm_image_t *
dm_open(const char * a,uint32_t b)748 dm_open (const char *a, uint32_t b)
749 {
750 return dm_open_ptr (a, b);
751 }
752
753
754 dm_image_t *
dm_reopen(const char * a,uint32_t b,dm_image_t * c)755 dm_reopen (const char *a, uint32_t b, dm_image_t *c)
756 {
757 return dm_reopen_ptr (a, b, c);
758 }
759
760
761 int
dm_close(dm_image_t * a)762 dm_close (dm_image_t *a)
763 {
764 return dm_close_ptr (a);
765 }
766
767
768 void
dm_nfo(const dm_image_t * a,int b,int c)769 dm_nfo (const dm_image_t *a, int b, int c)
770 {
771 dm_nfo_ptr (a, b, c);
772 }
773
774
775 int
dm_disc_read(const dm_image_t * a)776 dm_disc_read (const dm_image_t *a)
777 {
778 return dm_disc_read_ptr (a);
779 }
780
781
782 int
dm_disc_write(const dm_image_t * a)783 dm_disc_write (const dm_image_t *a)
784 {
785 return dm_disc_write_ptr (a);
786 }
787
788
789 int
dm_read(char * a,int b,int c,const dm_image_t * d)790 dm_read (char *a, int b, int c, const dm_image_t *d)
791 {
792 return dm_read_ptr (a, b, c, d);
793 }
794
795
796 int
dm_write(const char * a,int b,int c,const dm_image_t * d)797 dm_write (const char *a, int b, int c, const dm_image_t *d)
798 {
799 return dm_write_ptr (a, b, c, d);
800 }
801
802
803 dm_image_t *
dm_toc_read(dm_image_t * a,const char * b)804 dm_toc_read (dm_image_t *a, const char *b)
805 {
806 return dm_toc_read_ptr (a, b);
807 }
808
809
810 int
dm_toc_write(const dm_image_t * a)811 dm_toc_write (const dm_image_t *a)
812 {
813 return dm_toc_write_ptr (a);
814 }
815
816
817 dm_image_t *
dm_cue_read(dm_image_t * a,const char * b)818 dm_cue_read (dm_image_t *a, const char *b)
819 {
820 return dm_cue_read_ptr (a, b);
821 }
822
823
824 int
dm_cue_write(const dm_image_t * a)825 dm_cue_write (const dm_image_t *a)
826 {
827 return dm_cue_write_ptr (a);
828 }
829
830
831 int
dm_rip(const dm_image_t * a,int b,uint32_t c)832 dm_rip (const dm_image_t *a, int b, uint32_t c)
833 {
834 return dm_rip_ptr (a, b, c);
835 }
836 #endif // DLOPEN
837 #endif // USE_DISCMAGE
838
839
840 int
ucon64_file_handler(char * dest,char * src,unsigned int flags)841 ucon64_file_handler (char *dest, char *src, unsigned int flags)
842 /*
843 We have to handle the following cases (for example -swc and rom.swc exists):
844 1) ucon64 -swc rom.swc
845 a) with backup creation enabled
846 Create backup of rom.swc
847 postcondition: src == name of backup
848 b) with backup creation disabled
849 Create temporary backup of rom.swc by renaming rom.swc
850 postcondition: src == name of backup
851 2) ucon64 -swc rom.fig
852 a) with backup creation enabled
853 Create backup of rom.swc
854 postcondition: src == rom.fig
855 b) with backup creation disabled
856 Do nothing
857 postcondition: src == rom.fig
858
859 This function returns 1 if dest existed (in the directory specified with -o).
860 Otherwise it returns 0;
861 */
862 {
863 ucon64_output_fname (dest, flags); // call this function unconditionally
864
865 #if 0
866 // ucon64.temp_file will be reset in remove_temp_file()
867 ucon64.temp_file = NULL;
868 #endif
869 if (!access (dest, F_OK))
870 {
871 #ifdef _WIN32
872 struct _stati64 dest_info;
873 _stati64 (dest, &dest_info);
874 #else
875 struct stat dest_info;
876 stat (dest, &dest_info);
877 #endif
878 // *trying* to make dest writable here avoids having to change all code
879 // that might (try to) operate on a read-only file
880 chmod (dest, dest_info.st_mode | S_IWUSR);
881
882 if (src == NULL)
883 {
884 if (ucon64.backup)
885 printf ("Wrote backup to %s\n", mkbak (dest, BAK_DUPE));
886 return 1;
887 }
888
889 if (one_file (src, dest))
890 { // case 1
891 if (ucon64.backup)
892 { // case 1a
893 strcpy (src, mkbak (dest, BAK_DUPE));
894 printf ("Wrote backup to %s\n", src);
895 }
896 else
897 { // case 1b
898 strcpy (src, mkbak (dest, BAK_MOVE));
899 ucon64.temp_file = src;
900 }
901 }
902 else
903 { // case 2
904 if (ucon64.backup) // case 2a
905 printf ("Wrote backup to %s\n", mkbak (dest, BAK_DUPE));
906 }
907 return 1;
908 }
909 return 0;
910 }
911
912
913 void
remove_temp_file(void)914 remove_temp_file (void)
915 {
916 if (ucon64.temp_file)
917 {
918 printf ("Removing %s\n", ucon64.temp_file);
919 remove (ucon64.temp_file);
920 ucon64.temp_file = NULL;
921 }
922 }
923
924
925 char *
ucon64_output_fname(char * requested_fname,unsigned int flags)926 ucon64_output_fname (char *requested_fname, unsigned int flags)
927 {
928 char suffix[FILENAME_MAX];
929 const char *p;
930 size_t len;
931
932 // We have to make a copy, because get_suffix() returns a pointer to a
933 // location in the original string.
934 p = get_suffix (requested_fname);
935 len = strnlen (p, sizeof suffix - 1);
936 strncpy (suffix, p, len)[len] = '\0'; // in case suffix is >= sizeof suffix - 1 chars
937
938 // OF_FORCE_BASENAME is necessary for options like -gd3. Of course that
939 // code should handle archives and come up with unique filenames for
940 // archives with more than one file.
941 if (!ucon64.fname_arch[0] || (flags & OF_FORCE_BASENAME))
942 {
943 const char *requested_fname_base = basename2 (requested_fname);
944 char fname[FILENAME_MAX];
945
946 len = strnlen (requested_fname_base, FILENAME_MAX - 1);
947 strncpy (fname, requested_fname_base, len)[len] = '\0';
948 len += strnlen (ucon64.output_path, FILENAME_MAX - 1 - len);
949 snprintf (requested_fname, len + 1, "%s%s", ucon64.output_path, fname);
950 }
951 else // an archive (for now: zip file)
952 {
953 p = basename2 (ucon64.fname_arch);
954 len = strnlen (ucon64.output_path, FILENAME_MAX - 1);
955 len += strnlen (p, FILENAME_MAX - 1 - len);
956 snprintf (requested_fname, len + 1, "%s%s", ucon64.output_path, p);
957 }
958 requested_fname[len] = '\0';
959
960 /*
961 Keep the requested suffix, but only if it isn't ".zip" or ".gz". This
962 because we don't write to zip or gzip files. Otherwise the output
963 file would have the suffix ".zip" or ".gz" while it isn't a zip or gzip
964 file. uCON64 handles such files correctly, because it looks at the file
965 data itself, but many programs don't.
966 If the flag OF_FORCE_SUFFIX was used we keep the suffix, even if it's
967 ".zip" or ".gz". Now ucon64_output_fname() can be used when renaming/moving
968 files.
969 */
970 if (!(flags & OF_FORCE_SUFFIX) &&
971 !(stricmp (suffix, ".zip") && stricmp (suffix, ".gz")))
972 strcpy (suffix, ".tmp");
973 set_suffix (requested_fname, suffix);
974 return requested_fname;
975 }
976
977
978 #if 1
979 int64_t
ucon64_testpad(const char * filename)980 ucon64_testpad (const char *filename)
981 /*
982 Test if EOF is padded (repeated byte values)
983 This (new) version is not efficient for uncompressed files, but *much* more
984 efficient for compressed files. For example (a bad case), on a Celeron 850
985 just viewing info about a zipped dump of Mario Party (U) takes more than 3
986 minutes when the old version of ucon64_testpad() is used. A gzipped dump
987 can take more than 6 minutes. With this version it takes about 9 seconds for
988 the zipped dump and 12 seconds for the gzipped dump.
989 */
990 {
991 int64_t n = 0;
992 size_t blocksize;
993 unsigned char buffer[MAXBUFSIZE], c = 0;
994 FILE *file = fopen (filename, "rb");
995
996 if (!file)
997 return -1;
998
999 while ((blocksize = fread (buffer, 1, MAXBUFSIZE, file)) != 0)
1000 {
1001 int64_t start_n;
1002 size_t i;
1003
1004 if (buffer[blocksize - 1] != c)
1005 {
1006 c = buffer[blocksize - 1];
1007 n = 0;
1008 }
1009 start_n = n;
1010 for (i = blocksize - 1; i <= blocksize - 1; i--)
1011 {
1012 if (buffer[i] != c)
1013 {
1014 n -= start_n;
1015 break;
1016 }
1017 else
1018 n++;
1019 }
1020 }
1021 /*
1022 A file is either padded with 2 or more bytes or it isn't padded at all. It
1023 can't be detected that a file is padded with 1 byte (if the file size is
1024 disregarded, as is done here).
1025 */
1026 if (n == 1)
1027 n = 0;
1028
1029 fclose (file);
1030 return n;
1031 }
1032 #else
1033 int64_t
ucon64_testpad(const char * filename)1034 ucon64_testpad (const char *filename)
1035 // test if EOF is padded (repeating bytes)
1036 {
1037 int64_t pos = ucon64.fsize - 1;
1038 int buf_pos = pos % MAXBUFSIZE, c = ucon64_fgetc (filename, pos);
1039 unsigned char buf[MAXBUFSIZE];
1040 FILE *file = fopen (filename, "rb");
1041
1042 if (!file)
1043 return -1;
1044
1045 for (pos -= buf_pos; !fseeko2 (file, pos, SEEK_SET) && pos > -1;
1046 pos -= MAXBUFSIZE, buf_pos = MAXBUFSIZE)
1047 {
1048 fread (buf, 1, buf_pos, file);
1049
1050 for (; buf_pos > 0; buf_pos--)
1051 if (buf[buf_pos - 1] != c)
1052 {
1053 fclose (file);
1054 return ucon64.fsize - (pos + buf_pos) > 1 ?
1055 ucon64.fsize - (pos + buf_pos) : 0;
1056 }
1057 }
1058
1059 fclose (file);
1060 return ucon64.fsize; // the entire file is "padded"
1061 }
1062 #endif
1063
1064
1065 int
ucon64_gauge(time_t start_time,size_t pos,size_t size)1066 ucon64_gauge (time_t start_time, size_t pos, size_t size)
1067 {
1068 unsigned int bps, percentage;
1069 int col1, col2;
1070
1071 if (pos > size || !size)
1072 return -1;
1073
1074 percentage = misc_percent (pos, size);
1075
1076 if (ucon64.frontend)
1077 {
1078 printf ("%u\n", percentage);
1079 fflush (stdout);
1080
1081 return 0;
1082 }
1083
1084 printf ("\r%10u Bytes [", (unsigned) pos);
1085
1086 #ifdef USE_ANSI_COLOR
1087 if (ucon64.ansi_color)
1088 {
1089 col1 = 1;
1090 col2 = 2;
1091 }
1092 else
1093 #endif
1094 {
1095 col1 = -1;
1096 col2 = -1;
1097 }
1098 gauge (percentage, 22, '=', '-', col1, col2);
1099
1100 bps = bytes_per_second (start_time, pos);
1101 printf ("] %d%%, BPS=%d, ", percentage, bps);
1102
1103 if (pos == size)
1104 {
1105 unsigned int curr = (unsigned int) difftime (time (NULL), start_time);
1106 // "round up" to at least 1 sec (to be consistent with ETA)
1107 if (curr < 1)
1108 curr = 1;
1109 printf ("TOTAL=%02u:%02u", curr / 60, curr % 60);
1110 }
1111 else if (pos)
1112 {
1113 unsigned int left = (unsigned int) ((size - pos) / MAX (bps, 1));
1114 printf ("ETA=%02u:%02u ", left / 60, left % 60);
1115 }
1116 else // don't display a nonsense ETA
1117 fputs ("ETA=? ", stdout);
1118
1119 fflush (stdout);
1120
1121 return 0;
1122 }
1123
1124
1125 int
ucon64_testsplit(const char * filename,void (* testsplit_cb)(const char *,void *),void * cb_data)1126 ucon64_testsplit (const char *filename,
1127 void (*testsplit_cb) (const char *, void *), void *cb_data)
1128 // test if ROM is split into parts (for one of the supported backup units)
1129 // based on the name of files
1130 {
1131 int x, parts;
1132
1133 for (x = -1; x < 2; x += 2)
1134 {
1135 size_t l;
1136 char buf[FILENAME_MAX], *p;
1137
1138 parts = 0;
1139 strcpy (buf, filename);
1140 p = strrchr (basename2 (buf), '.');
1141 l = strlen (buf);
1142
1143 if (p == NULL) // filename doesn't contain a period
1144 p = buf + l - 1;
1145 else
1146 p += x; // if x == -1 change char before '.'
1147 // else if x == 1 change char after '.'
1148 if (buf > p || // filename starts with '.' (x == -1)
1149 (size_t) (p - buf) > l - 1) // filename ends with '.' (x == 1)
1150 continue;
1151
1152 while (!access (buf, F_OK))
1153 (*p)--; // "rewind" (find the first part)
1154 *p += 2;
1155 if (!access (buf, F_OK)) // test if at least 2 parts
1156 {
1157 (*p)--;
1158 while (!access (buf, F_OK)) // count split parts
1159 {
1160 if (testsplit_cb)
1161 testsplit_cb (buf, cb_data);
1162 (*p)++;
1163 parts++;
1164 }
1165 if (parts)
1166 break;
1167 }
1168 }
1169
1170 return parts;
1171 }
1172
1173
1174 static void
ucon64_set_property(st_property_t * prop,const char * org_configfile,const char * propname,const char * value_s,const char * comment_s)1175 ucon64_set_property (st_property_t *prop, const char *org_configfile,
1176 const char *propname, const char *value_s,
1177 const char *comment_s)
1178 {
1179 const char *p = NULL;
1180
1181 prop->name = propname;
1182 if (*org_configfile && propname)
1183 p = get_property (org_configfile, propname, PROPERTY_MODE_CFG_ONLY);
1184 prop->value_s = (p || value_s) ? strdup (p ? p : value_s) : NULL;
1185 prop->comment_s = comment_s;
1186 }
1187
1188
1189 int
ucon64_set_property_array(const char * org_configfile)1190 ucon64_set_property_array (const char *org_configfile)
1191 {
1192 st_property_t props[46];
1193 int i = 0, result;
1194
1195 ucon64_set_property (&props[i++], org_configfile, "backups", "1",
1196 "Create backups of files? (1=yes; 0=no)\n"
1197 "before processing a ROM uCON64 will make a backup of it");
1198 ucon64_set_property (&props[i++], org_configfile, "ansi_color", "1",
1199 "Use ANSI colors in output? (1=yes; 0=no)");
1200 #ifdef USE_PPDEV
1201 ucon64_set_property (&props[i++], org_configfile, "parport_dev", "/dev/parport0",
1202 "parallel port");
1203 #elif defined AMIGA
1204 ucon64_set_property (&props[i++], org_configfile, "parport_dev", "parallel.device",
1205 "parallel port");
1206 ucon64_set_property (&props[i++], org_configfile, "parport", "0", NULL);
1207 #else
1208 ucon64_set_property (&props[i++], org_configfile, "parport", "378",
1209 "(parallel) port");
1210 #ifdef USE_PARALLEL
1211 ucon64_set_property (&props[i++], org_configfile, "ecr_offset", "402",
1212 "offset of ECP Extended Control register relative to Data register (parport)");
1213 #endif
1214 #endif
1215 ucon64_set_property (&props[i++], org_configfile, "gd6_send_byte_delay", "0",
1216 "GDSF6/7 specific: delay in microseconds to simulate synchronizing (based on\n"
1217 "reading bit 1 of the parallel port Control register) before sending a byte.\n"
1218 "Also signifies that all synchronization involving reads from the Control\n"
1219 "register should be simulated\n"
1220 "(0=do not simulate, but read from the Control register)");
1221 ucon64_set_property (&props[i++], org_configfile, "n64_dat_v64", "1",
1222 "calculate CRC32 value of N64 ROM in Doctor V64 format for DAT files\n"
1223 "(1=Doctor V64; 0=Mr. Backup Z64)");
1224 ucon64_set_property (&props[i++], org_configfile, "discmage_path",
1225 #ifdef __MSDOS__
1226 PROPERTY_MODE_DIR ("ucon64") "discmage.dxe",
1227 #elif defined __CYGWIN__ || defined _WIN32
1228 PROPERTY_MODE_DIR ("ucon64") "discmage.dll",
1229 #elif defined __APPLE__ // Mac OS X actually
1230 PROPERTY_MODE_DIR ("ucon64") "discmage.dylib",
1231 #elif defined __unix__ || defined __BEOS__
1232 "/usr/local/lib/libdiscmage.so",
1233 #else
1234 "",
1235 #endif
1236 "complete path to the discmage library for DISC image support");
1237 ucon64_set_property (&props[i++], org_configfile, "ucon64_configdir",
1238 PROPERTY_MODE_DIR ("ucon64"),
1239 "directory with additional config files");
1240 ucon64_set_property (&props[i++], org_configfile, "ucon64_datdir",
1241 PROPERTY_MODE_DIR ("ucon64" DIR_SEPARATOR_S "dat"),
1242 "directory with DAT files");
1243 ucon64_set_property (&props[i++], org_configfile, "f2afirmware", "f2afirm.hex",
1244 "F2A support files\n"
1245 "path to F2A USB firmware");
1246 ucon64_set_property (&props[i++], org_configfile, "iclientu", "iclientu.bin",
1247 "path to GBA client binary (for USB code)");
1248 ucon64_set_property (&props[i++], org_configfile, "iclientp", "iclientp.bin",
1249 "path to GBA client binary (for parallel port code)");
1250 ucon64_set_property (&props[i++], org_configfile, "ilogo", "ilogo.bin",
1251 "path to iLinker logo file");
1252 ucon64_set_property (&props[i++], org_configfile, "gbaloader", "loader.bin",
1253 "path to GBA multi-game loader");
1254 ucon64_set_property (&props[i++], org_configfile, "gbaloader_sc", "sc_menu.bin",
1255 "path to GBA multi-game loader (Super Card)");
1256 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_3DO_S, "",
1257 "emulate_<console shortcut>=<emulator with options>\n\n"
1258 "You can also use CRC32 values for ROM specific emulation options:\n\n"
1259 "emulate_0x<crc32>=<emulator with options>\n"
1260 "emulate_<crc32>=<emulator with options>");
1261 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_ATA_S, "", NULL);
1262 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_CD32_S, "", NULL);
1263 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_CDI_S, "", NULL);
1264 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_COLECO_S, "", NULL);
1265 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_DC_S, "", NULL);
1266 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_GB_S,
1267 "vgb -sound -sync 50 -sgb -scale 2", NULL);
1268 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_GBA_S,
1269 "vgba -scale 2 -uperiod 6", NULL);
1270 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_GC_S, "", NULL);
1271 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_GEN_S, "dgen -f -S 2", NULL);
1272 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_INTELLI_S, "", NULL);
1273 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_JAG_S, "", NULL);
1274 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_LYNX_S, "", NULL);
1275 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_ARCADE_S, "", NULL);
1276 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_N64_S, "", NULL);
1277 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_NES_S,
1278 "tuxnes -E2 -rx11 -v -s/dev/dsp -R44100", NULL);
1279 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_NG_S, "", NULL);
1280 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_NGP_S, "", NULL);
1281 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_PCE_S, "", NULL);
1282 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_PS2_S, "", NULL);
1283 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_PSX_S, "pcsx", NULL);
1284 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_S16_S, "", NULL);
1285 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_SAT_S, "", NULL);
1286 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_SMS_S, "", NULL);
1287 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_GAMEGEAR_S, "", NULL);
1288 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_SNES_S,
1289 "snes9x -tr -sc -hires -dfr -r 7 -is -joymap1 2 3 5 0 4 7 6 1", NULL);
1290 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_SWAN_S, "", NULL);
1291 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_VBOY_S, "", NULL);
1292 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_VEC_S, "", NULL);
1293 ucon64_set_property (&props[i++], org_configfile, "emulate_" UCON64_XBOX_S, "", NULL);
1294 ucon64_set_property (&props[i++], org_configfile, NULL, NULL, NULL);
1295
1296 result = set_property_array (ucon64.configfile, props);
1297
1298 for (i -= 2; i >= 0; i--)
1299 free ((char *) props[i].value_s);
1300
1301 return result;
1302 }
1303
1304
1305 static inline char *
tofunc(char * s,size_t len,int (* func)(int))1306 tofunc (char *s, size_t len, int (*func) (int))
1307 {
1308 char *p = s;
1309
1310 for (; len > 0; p++, len--)
1311 *p = (char) func (*p);
1312
1313 return s;
1314 }
1315
1316
1317 int
ucon64_rename(int mode)1318 ucon64_rename (int mode)
1319 {
1320 char buf[FILENAME_MAX], buf2[FILENAME_MAX], suffix_buf[65];
1321 const char *suffix = NULL, *p, *p2;
1322 unsigned int crc;
1323 int good_name = 0, add_suffix = 1;
1324
1325 *buf2 = '\0';
1326
1327 switch (mode)
1328 {
1329 case UCON64_RROM:
1330 if (ucon64.nfo && ucon64.nfo->name[0])
1331 {
1332 suffix = get_suffix (ucon64.fname);
1333 strcpy (buf2, ucon64.nfo->name);
1334 strtriml (strtrimr (buf2));
1335 }
1336 break;
1337 case UCON64_RDAT: // GoodXXXX style rename
1338 if (ucon64.dat && ((st_ucon64_dat_t *) ucon64.dat)->fname[0])
1339 {
1340 suffix = get_suffix (ucon64.fname);
1341 strcpy (buf2, ((st_ucon64_dat_t *) ucon64.dat)->fname);
1342 }
1343 break;
1344 case UCON64_RJOLIET:
1345 /*
1346 We *have* to look at the structure of the filename, i.e., handle
1347 "base name" and suffix differently. This is necessary, because it's
1348 usual that filenames are identified by their suffix (especially on
1349 Windows).
1350 In order to be able to say that the base name and/or the suffix is too
1351 long, we have to specify a maximum length for both. We chose maximum
1352 lengths for the base name and the suffix of 48 and 16 characters
1353 respectively. These are arbitrary limits of course. The limits could
1354 just as well have been 60 and 4 characters. Note that the boundary will
1355 be adjusted if only one part is too long.
1356 If either the base name or the suffix is too long, we replace the last
1357 three characters of the base name with the three most significant
1358 digits of the CRC32 value of the full name.
1359 */
1360 {
1361 size_t len, len2;
1362
1363 p = get_suffix (ucon64.fname);
1364 len2 = strnlen (p, sizeof suffix_buf - 1);
1365 strncpy (suffix_buf, p, len2)[len2] = '\0';
1366 suffix = suffix_buf;
1367
1368 p = basename2 (ucon64.fname);
1369 strcpy (buf2, p);
1370 len = strlen (p); // it's safe to assume that len is < FILENAME_MAX
1371 crc = crc32 (0, (unsigned char *) buf2, (unsigned int) len);
1372 len -= len2;
1373 if (len + len2 <= 64) // Joliet maximum filename length is 64 chars
1374 {
1375 buf2[len] = '\0';
1376 break;
1377 }
1378 if (len2 <= 16) // len > 48
1379 len = 64 - len2 - 3;
1380 else // len2 > 16
1381 {
1382 if (len <= 48 - 3)
1383 len2 = 64 - len - 3;
1384 else // len > 48 - 3
1385 {
1386 len = 48 - 3;
1387 len2 = 16;
1388 }
1389 suffix_buf[len2] = '\0';
1390 }
1391 // NOTE: The implementation of snprintf() in glibc 2.3.5-10 (FC4)
1392 // terminates the string. So, a size argument of 4 results in 3
1393 // characters plus a string terminator.
1394 snprintf (buf2 + len, 4, "%0x", crc);
1395 buf2[len + 3] = '\0';
1396 }
1397 break;
1398 case UCON64_R83:
1399 /*
1400 The code for handling "FAT" filenames is similar to the code that
1401 handles Joliet filenames, except that the maximum lengths for base name
1402 and suffix are fixed (8 and 4 respectively).
1403 Note that FAT is quoted, as this code mainly limits the filename length.
1404 It doesn't guarantee that the filename is correct for FAT file systems.
1405 For example, a file with a name with a leading period (not a valid
1406 filename on a FAT file system) doesn't get special treatment.
1407 */
1408 {
1409 size_t len, len2;
1410
1411 p = get_suffix (ucon64.fname);
1412 len2 = strnlen (p, 4);
1413 strncpy (suffix_buf, p, len2)[len2] = '\0';
1414 suffix = suffix_buf;
1415
1416 p = basename2 (ucon64.fname);
1417 strcpy (buf2, p);
1418 len = strlen (p); // it's safe to assume that len is < FILENAME_MAX
1419 crc = crc32 (0, (unsigned char *) buf2, (unsigned int) len);
1420 len -= len2;
1421 if (len <= 8 && len2 <= 4) // FAT maximum filename length is 8 + 4 chars
1422 { // (we include the period with the suffix)
1423 buf2[len] = '\0';
1424 break;
1425 }
1426 if (len > 8 - 3)
1427 len = 8 - 3;
1428 snprintf (buf2 + len, 4, "%0x", crc);
1429 buf2[len + 3] = '\0';
1430 }
1431 break;
1432 case UCON64_RL:
1433 strcpy (buf2, basename2 (ucon64.fname));
1434 strlwr (buf2);
1435 break;
1436 case UCON64_RU:
1437 strcpy (buf2, basename2 (ucon64.fname));
1438 strupr (buf2);
1439 break;
1440 default:
1441 return 0; // invalid mode
1442 }
1443
1444 if (!buf2[0])
1445 return 0;
1446
1447 // replace chars the fs might not like
1448 tofunc (buf2, strlen (buf2), tofname);
1449 strcpy (buf, basename2 (ucon64.fname));
1450
1451 if (mode != UCON64_RL && mode != UCON64_RU)
1452 // Remove the suffix (ucon64.fname). Note that this isn't fool-proof.
1453 // However, this is the best solution, because several DAT files contain
1454 // "canonical" filenames with a suffix. That is a STUPID bug.
1455 *(char *) get_suffix (buf) = '\0';
1456
1457 if (!strcmp (buf, buf2))
1458 // Also process files with a correct name, so that -rename can be used to
1459 // "weed" out good dumps when -o is used (like GoodXXXX without inplace
1460 // command).
1461 good_name = 1;
1462 else if (mode != UCON64_RL && mode != UCON64_RU)
1463 {
1464 // Another test if the file already has a correct name. This is necessary
1465 // for files without a "normal" suffix (e.g. ".smc"). Take for example a
1466 // name like "Final Fantasy III (V1.1) (U) [!]".
1467 strcat (buf, suffix);
1468 if (!strcmp (buf, buf2))
1469 {
1470 good_name = 1;
1471 add_suffix = 0; // suffix is part of the correct name
1472 }
1473 else
1474 good_name = 0;
1475 }
1476
1477 if (add_suffix && mode != UCON64_RL && mode != UCON64_RU)
1478 {
1479 // DON'T use set_suffix()! Consider filenames (in the DAT file) like
1480 // "Final Fantasy III (V1.1) (U) [!]". The suffix is ".1) (U) [!]"...
1481 size_t len = strlen (buf2), len2 = strlen (suffix);
1482
1483 if (len + len2 >= sizeof buf2)
1484 len2 = sizeof buf2 - 1 - len;
1485 strncpy (buf2 + len, suffix, len2)[len2] = '\0';
1486 }
1487
1488 ucon64_output_fname (buf2, OF_FORCE_BASENAME | OF_FORCE_SUFFIX);
1489
1490 p = basename2 (ucon64.fname);
1491 p2 = basename2 (buf2);
1492
1493 if (one_file (ucon64.fname, buf2))
1494 {
1495 if (!strcmp (p, p2)) // skip only if the letter case
1496 { // also matches (Windows...)
1497 printf ("Skipping \"%s\"\n", p);
1498 return 0;
1499 }
1500 }
1501 else if (!access (buf2, F_OK))
1502 // A file with that name already exists? Ignore the letter case, because
1503 // Windows does so too.
1504 {
1505 printf ("Skipping \"%s\"\n", p);
1506 if (!good_name)
1507 printf (" Target filename is \"%s\"\n", p2);
1508 printf (" A file with the same name, but possibly different contents exists in the\n"
1509 " output directory (\"%s\")\n", ucon64.output_path[0] ?
1510 ucon64.output_path : "." DIR_SEPARATOR_S);
1511 return 0;
1512 }
1513
1514 if (!good_name)
1515 printf ("Renaming \"%s\" to \"%s\"\n", p, p2);
1516 else
1517 printf ("Moving \"%s\"\n", p);
1518
1519 {
1520 int result = 0;
1521
1522 #ifndef DEBUG
1523 if (rename2 (ucon64.fname, buf2) != 0) // rename_2_() must be used!
1524 {
1525 fprintf (stderr, "ERROR: Could not rename \"%s\"\n", ucon64.fname);
1526 result = -1;
1527 }
1528 #endif
1529 #ifdef USE_ZLIB
1530 unzip_current_file_nr = (~((ZPOS64_T) 0) >> 1) - 1; // dirty hack
1531 #endif
1532 return result;
1533 }
1534 }
1535
1536
1537 int
ucon64_e(void)1538 ucon64_e (void)
1539 {
1540 int result = 0;
1541 char buf[MAXBUFSIZE], name[MAXBUFSIZE];
1542 const char *value_p = NULL;
1543 #ifdef _MSC_VER
1544 #pragma warning(push)
1545 #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
1546 #endif
1547 typedef struct
1548 {
1549 int id;
1550 const char *s;
1551 } st_strings_t;
1552 #ifdef _MSC_VER
1553 #pragma warning(pop)
1554 #endif
1555 st_strings_t s[] =
1556 {
1557 {UCON64_3DO, "emulate_" UCON64_3DO_S},
1558 {UCON64_ATA, "emulate_" UCON64_ATA_S},
1559 {UCON64_CD32, "emulate_" UCON64_CD32_S},
1560 {UCON64_CDI, "emulate_" UCON64_CDI_S},
1561 {UCON64_COLECO, "emulate_" UCON64_COLECO_S},
1562 {UCON64_DC, "emulate_" UCON64_DC_S},
1563 {UCON64_GB, "emulate_" UCON64_GB_S},
1564 {UCON64_GBA, "emulate_" UCON64_GBA_S},
1565 {UCON64_GC, "emulate_" UCON64_GC_S},
1566 {UCON64_GEN, "emulate_" UCON64_GEN_S},
1567 {UCON64_GP32, "emulate_" UCON64_GP32_S},
1568 {UCON64_INTELLI, "emulate_" UCON64_INTELLI_S},
1569 {UCON64_JAG, "emulate_" UCON64_JAG_S},
1570 {UCON64_LYNX, "emulate_" UCON64_LYNX_S},
1571 {UCON64_ARCADE, "emulate_" UCON64_ARCADE_S},
1572 {UCON64_N64, "emulate_" UCON64_N64_S},
1573 {UCON64_NDS, "emulate_" UCON64_NDS_S},
1574 {UCON64_NES, "emulate_" UCON64_NES_S},
1575 {UCON64_NG, "emulate_" UCON64_NG_S},
1576 {UCON64_NGP, "emulate_" UCON64_NGP_S},
1577 {UCON64_PCE, "emulate_" UCON64_PCE_S},
1578 {UCON64_PS2, "emulate_" UCON64_PS2_S},
1579 {UCON64_PSX, "emulate_" UCON64_PSX_S},
1580 {UCON64_S16, "emulate_" UCON64_S16_S},
1581 {UCON64_SAT, "emulate_" UCON64_SAT_S},
1582 {UCON64_SMS, "emulate_" UCON64_SMS_S},
1583 {UCON64_GAMEGEAR, "emulate_" UCON64_GAMEGEAR_S},
1584 {UCON64_SNES, "emulate_" UCON64_SNES_S},
1585 {UCON64_SWAN, "emulate_" UCON64_SWAN_S},
1586 {UCON64_VBOY, "emulate_" UCON64_VBOY_S},
1587 {UCON64_VEC, "emulate_" UCON64_VEC_S},
1588 {UCON64_XBOX, "emulate_" UCON64_XBOX_S},
1589 {0, NULL}
1590 };
1591
1592 if (access (ucon64.configfile, F_OK) != 0)
1593 {
1594 fprintf (stderr, "ERROR: %s does not exist\n", ucon64.configfile);
1595 return -1;
1596 }
1597
1598 sprintf (name, "emulate_%08x", ucon64.crc32); // look for emulate_<crc32>
1599 value_p = get_property (ucon64.configfile, name, PROPERTY_MODE_TEXT);
1600
1601 if (value_p == NULL)
1602 {
1603 sprintf (name, "emulate_0x%08x", ucon64.crc32); // look for emulate_0x<crc32>
1604 value_p = get_property (ucon64.configfile, name, PROPERTY_MODE_TEXT);
1605 }
1606
1607 if (value_p == NULL)
1608 {
1609 int x = 0;
1610
1611 for (x = 0; s[x].s; x++)
1612 if (s[x].id == ucon64.console)
1613 {
1614 value_p = get_property (ucon64.configfile, s[x].s, PROPERTY_MODE_TEXT);
1615 break;
1616 }
1617 }
1618
1619 if (value_p == NULL)
1620 {
1621 fprintf (stderr, "ERROR: Could not find the correct settings (%s) in\n"
1622 " %s\n"
1623 "TIP: If the wrong console was detected you might try to force recognition\n"
1624 " For example, the force recognition switch for SNES is " OPTION_LONG_S "snes\n",
1625 name, ucon64.configfile);
1626 return -1;
1627 }
1628
1629 sprintf (buf, "%s \"%s\"", value_p, ucon64.fname);
1630
1631 puts (buf);
1632 fflush (stdout);
1633
1634 result = system (buf)
1635 #if !(defined __MSDOS__ || defined _WIN32)
1636 >> 8 // the exit code is encoded in bits 8-15
1637 #endif // (does not apply to DJGPP, MinGW & VC++)
1638 ;
1639
1640 #if 1
1641 // Snes9x (Linux) for example returns a non-zero value on a normal exit
1642 // (3)...
1643 // on WinDOS, system() immediately returns with exit code 0 when starting a
1644 // Windows executable (as if fork() was called) it also returns 0 when the
1645 // exe could not be started
1646 if (result != 127 && result != -1 && result != 0) // 127 && -1 are system() errors, rest are exit codes
1647 {
1648 fprintf (stderr, "ERROR: The emulator returned an error (?) code: %d\n"
1649 "TIP: If the wrong emulator was used you might try to force recognition\n"
1650 " For example, the force recognition switch for SNES is " OPTION_LONG_S "snes\n",
1651 result);
1652 }
1653 #endif
1654 return result;
1655 }
1656
1657
1658 #define PATTERN_BUFSIZE (64 * 1024)
1659 int
ucon64_pattern(const char * pattern_fname)1660 ucon64_pattern (const char *pattern_fname)
1661 {
1662 char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], buffer[PATTERN_BUFSIZE];
1663 FILE *srcfile, *destfile;
1664 int n_patterns, n;
1665 size_t bytesread = 0, overlap = 0, effective_overlap = 0;
1666 unsigned int n_found = 0;
1667 uint64_t totalbytesread = 0;
1668 st_cm_pattern_t *patterns = NULL;
1669
1670 realpath2 (pattern_fname, src_name);
1671 // first try the current directory, then the configuration directory
1672 if (access (src_name, F_OK | R_OK) == -1)
1673 {
1674 snprintf (src_name, FILENAME_MAX, "%s" DIR_SEPARATOR_S "%s",
1675 ucon64.configdir, basename2 (pattern_fname));
1676 src_name[FILENAME_MAX - 1] = '\0';
1677 }
1678 n_patterns = build_cm_patterns (&patterns, src_name);
1679 if (n_patterns == 0)
1680 {
1681 fprintf (stderr, "ERROR: No patterns found in %s\n", src_name);
1682 cleanup_cm_patterns (&patterns, n_patterns);
1683 return -1;
1684 }
1685 else if (n_patterns < 0)
1686 {
1687 char dir1[FILENAME_MAX], dir2[FILENAME_MAX];
1688
1689 dirname2 (pattern_fname, dir1);
1690 dirname2 (src_name, dir2);
1691 fprintf (stderr, "ERROR: Could not read %s, neither in %s nor in %s\n",
1692 basename2 (pattern_fname), dir1, dir2);
1693 // when build_cm_patterns() returns -1, cleanup_cm_patterns() should not be called
1694 return -1;
1695 }
1696
1697 printf ("Found %d pattern%s in %s\n", n_patterns, n_patterns != 1 ? "s" : "", src_name);
1698
1699 for (n = 0; n < n_patterns; n++)
1700 {
1701 if (patterns[n].search_size > overlap)
1702 {
1703 overlap = patterns[n].search_size;
1704 if (overlap > PATTERN_BUFSIZE || overlap > ucon64.fsize)
1705 {
1706 fprintf (stderr,
1707 "ERROR: Pattern %d is too large (for %s).\n"
1708 " Specify a shorter pattern or disable it -- skipping file\n",
1709 n + 1, ucon64.fname);
1710 cleanup_cm_patterns (&patterns, n_patterns);
1711 return -1;
1712 }
1713 }
1714
1715 if (patterns[n].offset <= -(int) patterns[n].search_size || patterns[n].offset > 0)
1716 printf ("WARNING: The offset of pattern %d falls outside the search pattern.\n"
1717 " This can cause matches to be ignored with the current implementation\n"
1718 " of " OPTION_LONG_S "pattern. Please consider enlarging the search pattern\n",
1719 n + 1);
1720 }
1721 overlap--;
1722
1723 printf ("Searching for patterns in %s...\n", ucon64.fname);
1724
1725 strcpy (src_name, ucon64.fname);
1726 strcpy (dest_name, ucon64.fname);
1727 ucon64_file_handler (dest_name, src_name, 0);
1728 if ((srcfile = fopen (src_name, "rb")) == NULL)
1729 {
1730 fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name);
1731 return -1;
1732 }
1733 if ((destfile = fopen (dest_name, "wb")) == NULL)
1734 {
1735 fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name);
1736 fclose (srcfile);
1737 return -1;
1738 }
1739
1740 while ((bytesread = fread (buffer + effective_overlap, 1,
1741 PATTERN_BUFSIZE - effective_overlap, srcfile)) != 0)
1742 {
1743 for (n = 0; n < n_patterns; n++)
1744 {
1745 size_t search_overlap = patterns[n].search_size - 1,
1746 buffer_size = bytesread + (totalbytesread > 0 ? search_overlap : 0);
1747 char *buffer_start = buffer + (totalbytesread > 0 ? overlap - search_overlap : 0);
1748
1749 if (ucon64.quiet < 0)
1750 printf ("Scanning offset 0x%08llx-0x%08llx (%u bytes) for pattern %d\n",
1751 (long long unsigned int) (totalbytesread - effective_overlap +
1752 (buffer_start - buffer)),
1753 (long long unsigned int) (totalbytesread - effective_overlap +
1754 (buffer_start - buffer) + buffer_size - 1),
1755 (unsigned int) buffer_size, n + 1);
1756 n_found += change_mem2 (buffer_start, buffer_size, patterns[n].search,
1757 patterns[n].search_size, patterns[n].wildcard,
1758 patterns[n].escape, patterns[n].replace,
1759 patterns[n].replace_size, patterns[n].offset,
1760 patterns[n].sets);
1761 }
1762 fwrite (buffer, 1, bytesread + effective_overlap - overlap, destfile);
1763
1764 totalbytesread += bytesread;
1765 if (totalbytesread < ucon64.fsize)
1766 memmove (buffer, buffer + effective_overlap - overlap + bytesread, overlap);
1767 else
1768 fwrite (buffer + effective_overlap - overlap + bytesread, 1, overlap, destfile);
1769 effective_overlap = overlap;
1770
1771 if (ucon64.quiet < 0)
1772 fputc ('\n', stdout);
1773 }
1774
1775 fclose (srcfile);
1776 fclose (destfile);
1777 cleanup_cm_patterns (&patterns, n_patterns);
1778
1779 printf ("Found %u pattern%s\n", n_found, n_found != 1 ? "s" : "");
1780 printf (ucon64_msg[WROTE], dest_name);
1781 remove_temp_file ();
1782 return n_found;
1783 }
1784 #undef PATTERN_BUFSIZE
1785
1786
1787 size_t
ucon64_bswap16_n(void * buffer,size_t n)1788 ucon64_bswap16_n (void *buffer, size_t n)
1789 // bswap_16() n bytes of buffer
1790 {
1791 uint16_t *w = (uint16_t *) buffer;
1792
1793 n >>= 1; // # words = # bytes / 2
1794 for (; (size_t) (w - (uint16_t *) buffer) < n; w++)
1795 *w = bswap_16 (*w);
1796
1797 return n << 1; // return # of bytes swapped
1798 }
1799
1800
1801 static inline size_t
ucon64_fbswap16_func(void * buffer,size_t n,void * object)1802 ucon64_fbswap16_func (void *buffer, size_t n, void *object)
1803 // bswap_16() n bytes of buffer
1804 {
1805 (void) object;
1806 return ucon64_bswap16_n (buffer, n);
1807 }
1808
1809
1810 static inline size_t
ucon64_fwswap32_func(void * buffer,size_t n,void * object)1811 ucon64_fwswap32_func (void *buffer, size_t n, void *object)
1812 // wswap_32() n/2 words of buffer
1813 {
1814 (void) object;
1815 uint32_t *d = (uint32_t *) buffer;
1816
1817 n >>= 2; // # double words = # bytes / 4
1818 for (; (size_t) (d - (uint32_t *) buffer) < n; d++)
1819 *d = wswap_32 (*d);
1820
1821 return n << 2; // return # of bytes swapped
1822 }
1823
1824
1825 void
ucon64_fbswap16(const char * fname,uint64_t start,uint64_t len)1826 ucon64_fbswap16 (const char *fname, uint64_t start, uint64_t len)
1827 {
1828 quick_io_func (ucon64_fbswap16_func, MAXBUFSIZE, NULL, start, len, fname, "r+b");
1829 }
1830
1831
1832 void
ucon64_fwswap32(const char * fname,uint64_t start,uint64_t len)1833 ucon64_fwswap32 (const char *fname, uint64_t start, uint64_t len)
1834 {
1835 quick_io_func (ucon64_fwswap32_func, MAXBUFSIZE, NULL, start, len, fname, "r+b");
1836 }
1837
1838
1839 #ifdef _MSC_VER
1840 #pragma warning(push)
1841 #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
1842 #endif
1843 typedef struct
1844 {
1845 FILE *output;
1846 uint64_t virtual_pos;
1847 unsigned int flags;
1848 } st_ucon64_dump_t;
1849 #ifdef _MSC_VER
1850 #pragma warning(pop)
1851 #endif
1852
1853
1854 static inline size_t
ucon64_dump_func(void * buffer,size_t n,void * object)1855 ucon64_dump_func (void *buffer, size_t n, void *object)
1856 {
1857 st_ucon64_dump_t *o = (st_ucon64_dump_t *) object;
1858
1859 dumper (o->output, buffer, n, o->virtual_pos, o->flags);
1860 o->virtual_pos += n;
1861
1862 return n;
1863 }
1864
1865
1866 void
ucon64_dump(FILE * output,const char * filename,uint64_t start,uint64_t len,unsigned int flags)1867 ucon64_dump (FILE *output, const char *filename, uint64_t start, uint64_t len,
1868 unsigned int flags)
1869 {
1870 st_ucon64_dump_t o;
1871 o.output = output;
1872 o.virtual_pos = start;
1873 o.flags = flags;
1874
1875 quick_io_func (ucon64_dump_func, MAXBUFSIZE, &o, start, len, filename, "rb");
1876 }
1877
1878
1879 #ifdef _MSC_VER
1880 #pragma warning(push)
1881 #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
1882 #endif
1883 typedef struct
1884 {
1885 const char *search;
1886 size_t searchlen;
1887 unsigned int flags;
1888 uint64_t pos;
1889 int64_t found;
1890 // the members below are necessary for ucon64_replace()
1891 const char *replace;
1892 size_t replacelen;
1893 FILE *file;
1894 char *buf;
1895 size_t bufsize;
1896 } st_ucon64_find_t;
1897 #ifdef _MSC_VER
1898 #pragma warning(pop)
1899 #endif
1900
1901
1902 static inline size_t
ucon64_find_func(void * buffer,size_t n,void * object)1903 ucon64_find_func (void *buffer, size_t n, void *object)
1904 {
1905 st_ucon64_find_t *o = (st_ucon64_find_t *) object;
1906 char *ptr0 = (char *) buffer, *ptr1 = (char *) buffer;
1907 size_t m;
1908 static char match[MAXBUFSIZE - 1];
1909 static size_t matchlen;
1910
1911 // reset matchlen if this is the first call for a new file
1912 if (o->found == -2)
1913 {
1914 o->found = -1; // -1 is default (return) value
1915 matchlen = 0;
1916 }
1917
1918 if (o->searchlen > n + matchlen)
1919 {
1920 o->pos += n;
1921 return n;
1922 }
1923
1924 // check if we can match the search string across the buffer boundary
1925 for (m = 0; matchlen > 0; matchlen--)
1926 {
1927 char compare[MAXBUFSIZE + 0x0f];
1928 size_t len = MIN (n, ((o->searchlen + 0x0f) & ~0x0f) - matchlen);
1929
1930 memcpy (compare, match + m++, matchlen);
1931 memcpy (compare + matchlen, ptr1, len);
1932 if (memcmp2 (compare, o->search, o->searchlen, o->flags) == 0)
1933 {
1934 o->found = o->pos - matchlen;
1935 if (!(o->flags & UCON64_FIND_QUIET))
1936 {
1937 fputc ('\n', stdout);
1938 dumper (stdout, compare, matchlen + len, o->found, DUMPER_HEX);
1939 }
1940
1941 if (o->flags & UCON64_FIND_REPLACE)
1942 {
1943 memcpy (o->buf + o->bufsize - matchlen, o->replace,
1944 MIN (matchlen, o->replacelen));
1945 if (o->replacelen > matchlen)
1946 memcpy (ptr1, o->replace + matchlen, MIN (n, o->replacelen - matchlen));
1947
1948 if (!(o->flags & UCON64_FIND_QUIET))
1949 {
1950 memcpy (compare, o->buf + o->bufsize - matchlen, matchlen);
1951 if (o->replacelen > matchlen)
1952 len = MIN (n, ((o->replacelen + 0x0f) & ~0x0f) - matchlen);
1953 else
1954 len = MIN (n, ((matchlen + 0x0f) & ~0x0f) - matchlen);
1955 memcpy (compare + matchlen, ptr1, len);
1956 dumper (stdout, compare, matchlen + len, o->found, DUMPER_HEX);
1957 }
1958 }
1959 }
1960 }
1961
1962 while ((size_t) (ptr1 - ptr0) < n)
1963 {
1964 ptr1 = (char *) memmem2 (ptr1, n - (ptr1 - ptr0), o->search, o->searchlen,
1965 o->flags);
1966 if (ptr1)
1967 {
1968 o->found = o->pos + (size_t) (ptr1 - ptr0);
1969 if (!(o->flags & UCON64_FIND_QUIET))
1970 {
1971 fputc ('\n', stdout);
1972 dumper (stdout, ptr1,
1973 MIN (n - (ptr1 - ptr0), (o->searchlen + 0x0f) & ~0x0f),
1974 o->found, DUMPER_HEX);
1975 }
1976
1977 if (o->flags & UCON64_FIND_REPLACE)
1978 {
1979 memcpy (ptr1, o->replace, MIN (n - (ptr1 - ptr0), o->replacelen));
1980
1981 if (!(o->flags & UCON64_FIND_QUIET))
1982 dumper (stdout, ptr1,
1983 MIN (n - (ptr1 - ptr0), (o->replacelen + 0x0f) & ~0x0f),
1984 o->found, DUMPER_HEX);
1985 }
1986 ptr1++;
1987 }
1988 else
1989 {
1990 // try to find a partial match at the end of buffer
1991 size_t len = MIN (n, o->searchlen);
1992
1993 ptr1 = ptr0 + n - len;
1994 for (m = 1; m < len; m++)
1995 if (memcmp2 (ptr1 + m, o->search, len - m, o->flags) == 0)
1996 {
1997 memcpy (match, ptr1 + m, len - m);
1998 matchlen = len - m;
1999 break;
2000 }
2001 /*
2002 A relative search is undefined for a search string with a length of
2003 1, so we have to handle that case by copying a possibly matching
2004 byte to the array match.
2005 */
2006 if (!matchlen && o->flags & MEMMEM2_REL)
2007 {
2008 match[0] = ptr0[n - 1];
2009 matchlen = 1;
2010 }
2011 break;
2012 }
2013 }
2014
2015 if (o->flags & UCON64_FIND_REPLACE)
2016 {
2017 if (o->bufsize) // first perform a delayed write
2018 {
2019 fwrite (o->buf, 1, o->bufsize, o->file);
2020 o->bufsize = 0;
2021 }
2022
2023 if (!matchlen) // no partial match => commit buffer to disk
2024 fwrite (buffer, 1, n, o->file);
2025 else // partial match => delay write until next iteration
2026 {
2027 char *oldbuf = o->buf;
2028 if ((o->buf = (char *) realloc (oldbuf, n)) == NULL)
2029 {
2030 fprintf (stderr, ucon64_msg[BUFFER_ERROR], n);
2031 free (oldbuf);
2032 exit (1);
2033 }
2034 o->bufsize = n;
2035 memcpy (o->buf, buffer, n);
2036 }
2037 }
2038
2039 o->pos += n;
2040 return n;
2041 }
2042
2043
2044 static int64_t
ucon64_find_helper(const char * filename,uint64_t start,uint64_t len,st_ucon64_find_t * o)2045 ucon64_find_helper (const char *filename, uint64_t start, uint64_t len,
2046 st_ucon64_find_t *o)
2047 {
2048 if (!(o->flags & UCON64_FIND_QUIET))
2049 {
2050 char *display_search, *dest;
2051 const char *src = o->search;
2052
2053 fputs (filename, stdout);
2054 if (ucon64.fname_arch[0])
2055 printf (" (%s)\n", ucon64.fname_arch);
2056 else
2057 fputc ('\n', stdout);
2058
2059 if ((display_search = (char *) malloc (o->searchlen * 4 + 1)) == NULL)
2060 {
2061 fprintf (stderr, ucon64_msg[BUFFER_ERROR], o->searchlen * 4 + 1);
2062 exit (1);
2063 }
2064 dest = display_search;
2065 while ((size_t) (src - o->search) < o->searchlen)
2066 {
2067 if (isprint ((int) *src))
2068 *dest++ = *src;
2069 else
2070 dest += sprintf (dest, "\\x%02x", (unsigned char) *src);
2071 src++;
2072 }
2073 *dest = '\0'; // terminate string
2074
2075 if (o->flags & MEMCMP2_CASE)
2076 printf ("Case insensitive searching: \"%s\"\n", display_search);
2077 else if (o->flags & MEMCMP2_REL)
2078 {
2079 size_t n;
2080
2081 printf ("Relative searching: \"%s\"\n\n", display_search);
2082 for (n = 0; n + 1 < o->searchlen; n++)
2083 {
2084 char format[80];
2085
2086 sprintf (format, "%s - %s = %%d\n",
2087 isprint ((int) o->search[n]) ? "'%c'" : "%u",
2088 isprint ((int) o->search[n + 1]) ? "'%c'" : "%u");
2089 printf (format, o->search[n] & 0xff, o->search[n + 1] & 0xff,
2090 (unsigned char) o->search[n] - (unsigned char) o->search[n + 1]);
2091 }
2092 }
2093 else
2094 printf ("Searching: \"%s\"\n", display_search);
2095
2096 free (display_search);
2097 }
2098
2099 quick_io_func (ucon64_find_func, MAXBUFSIZE, o, start, len, filename, "rb");
2100
2101 return o->found; // return last occurrence or -1
2102 }
2103
2104
2105 int64_t
ucon64_find(const char * filename,uint64_t start,uint64_t len,const char * search,size_t searchlen,unsigned int flags)2106 ucon64_find (const char *filename, uint64_t start, uint64_t len,
2107 const char *search, size_t searchlen, unsigned int flags)
2108 {
2109 // o.found == -2 signifies a new find operation (usually for a new file)
2110 st_ucon64_find_t o = { NULL, 0, 0, 0, -2, NULL, 0, NULL, NULL, 0 };
2111 o.search = search;
2112 o.searchlen = searchlen;
2113 o.flags = flags;
2114 o.pos = start;
2115
2116 if (searchlen == 0)
2117 {
2118 fputs ("ERROR: No search string specified\n", stderr);
2119 exit (1);
2120 }
2121 else if (flags & MEMCMP2_REL && searchlen < 2)
2122 {
2123 fputs ("ERROR: Search string must be longer than 1 character for a relative search\n",
2124 stderr);
2125 exit (1);
2126 }
2127 else if (searchlen > MAXBUFSIZE)
2128 {
2129 fprintf (stderr, "ERROR: Search string must be <= %d characters\n",
2130 MAXBUFSIZE);
2131 exit (1); // see ucon64_find_func() for why
2132 }
2133
2134 return ucon64_find_helper (filename, start, len, &o);
2135 }
2136
2137
2138 int64_t
ucon64_replace(const char * filename,uint64_t start,uint64_t len,const char * search,size_t searchlen,const char * replace,size_t replacelen,unsigned int flags)2139 ucon64_replace (const char *filename, uint64_t start, uint64_t len,
2140 const char *search, size_t searchlen, const char *replace,
2141 size_t replacelen, unsigned int flags)
2142 {
2143 int64_t result;
2144 char src_name[FILENAME_MAX], dest_name[FILENAME_MAX];
2145 // o.found == -2 signifies a new find operation (usually for a new file)
2146 st_ucon64_find_t o = { NULL, 0, 0, 0, -2, NULL, 0, NULL, NULL, 0 };
2147 o.search = search;
2148 o.searchlen = searchlen;
2149 o.flags = flags | UCON64_FIND_REPLACE;
2150 o.pos = start;
2151 o.replace = replace;
2152 o.replacelen = replacelen;
2153
2154 if (searchlen == 0)
2155 {
2156 fputs ("ERROR: No search string specified\n", stderr);
2157 exit (1);
2158 }
2159 else if (flags & MEMCMP2_REL && searchlen < 2)
2160 {
2161 fputs ("ERROR: Search string must be longer than 1 character for a relative search\n",
2162 stderr);
2163 exit (1);
2164 }
2165 else if (searchlen > MAXBUFSIZE)
2166 {
2167 fprintf (stderr, "ERROR: Search string must be <= %d characters\n",
2168 MAXBUFSIZE);
2169 exit (1); // see ucon64_find_func() for why
2170 }
2171 if (replacelen == 0)
2172 {
2173 fputs ("ERROR: No replacement string specified\n", stderr);
2174 exit (1);
2175 }
2176 else if (replacelen > MAXBUFSIZE)
2177 {
2178 fprintf (stderr, "ERROR: Replacement string must be <= %d characters\n",
2179 MAXBUFSIZE);
2180 exit (1); // see ucon64_find_func() for why
2181 }
2182 if (replacelen > searchlen)
2183 puts ("WARNING: The replacement string is longer than the search string.\n"
2184 " This can cause replacements to be cut off with the current\n"
2185 " implementation. Please consider enlarging the search string");
2186
2187 strcpy (src_name, filename);
2188 strcpy (dest_name, filename);
2189 ucon64_file_handler (dest_name, src_name, 0);
2190
2191 if ((o.file = fopen (dest_name, "wb")) == NULL)
2192 {
2193 fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name);
2194 exit (1);
2195 }
2196
2197 result = ucon64_find_helper (src_name, start, len, &o);
2198
2199 if (o.bufsize)
2200 fwrite (o.buf, 1, o.bufsize, o.file);
2201 free (o.buf);
2202 fclose (o.file);
2203
2204 fputc ('\n', stdout);
2205 printf (ucon64_msg[WROTE], dest_name);
2206 remove_temp_file ();
2207 return result;
2208 }
2209
2210
2211 typedef struct
2212 {
2213 s_sha1_ctx_t *sha1_ctx;
2214 s_md5_ctx_t *md5_ctx;
2215 // uint16_t *crc16;
2216 unsigned int *crc32;
2217 } st_ucon64_chksum_t;
2218
2219
2220 static inline size_t
ucon64_chksum_func(void * buffer,size_t n,void * object)2221 ucon64_chksum_func (void *buffer, size_t n, void *object)
2222 {
2223 st_ucon64_chksum_t *o = (st_ucon64_chksum_t *) object;
2224
2225 if (o->sha1_ctx)
2226 sha1 (o->sha1_ctx, (const unsigned char *) buffer, (unsigned int) n);
2227
2228 if (o->md5_ctx)
2229 md5_update (o->md5_ctx, (unsigned char *) buffer, (unsigned int) n);
2230
2231 // if (o->crc16)
2232 // *(o->crc16) = crc16 (*(o->crc16), (const unsigned char *) buffer, (unsigned int) n);
2233
2234 if (o->crc32)
2235 *(o->crc32) = crc32 (*(o->crc32), (const unsigned char *) buffer, (unsigned int) n);
2236
2237 return n;
2238 }
2239
2240
2241 void
ucon64_chksum(char * sha1_s,char * md5_s,unsigned int * crc32_i,const char * filename,uint64_t file_size,uint64_t start)2242 ucon64_chksum (char *sha1_s, char *md5_s, unsigned int *crc32_i, // uint16_t *crc16_i,
2243 const char *filename, uint64_t file_size, uint64_t start)
2244 {
2245 int i = 0;
2246 s_sha1_ctx_t sha1_ctx;
2247 s_md5_ctx_t md5_ctx;
2248 st_ucon64_chksum_t o;
2249
2250 memset (&o, 0, sizeof (st_ucon64_chksum_t));
2251
2252 if (sha1_s)
2253 sha1_begin (o.sha1_ctx = &sha1_ctx);
2254
2255 if (md5_s)
2256 md5_init (o.md5_ctx = &md5_ctx, 0);
2257
2258 // if (crc16_i)
2259 // o.crc16 = crc16_i;
2260
2261 if (crc32_i)
2262 o.crc32 = crc32_i;
2263
2264 quick_io_func (ucon64_chksum_func, MAXBUFSIZE, &o, start, file_size - start,
2265 filename, "rb");
2266
2267 if (sha1_s)
2268 {
2269 unsigned char buf[MAXBUFSIZE];
2270
2271 sha1_end (buf, &sha1_ctx);
2272 for (i = 0; i < 20; i++, sha1_s = strchr (sha1_s, 0))
2273 sprintf (sha1_s, "%02x", buf[i]);
2274 }
2275
2276 if (md5_s)
2277 {
2278 md5_final (&md5_ctx);
2279 for (i = 0; i < 16; i++, md5_s = strchr (md5_s, 0))
2280 sprintf (md5_s, "%02x", md5_ctx.digest[i]);
2281 }
2282 }
2283
2284
2285 #if 0
2286 #define FILEFILE_LARGE_BUF (1024 * 1024)
2287
2288
2289 #ifdef _MSC_VER
2290 #pragma warning(push)
2291 #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
2292 #endif
2293 typedef struct
2294 {
2295 FILE *output;
2296 uint64_t pos0;
2297 uint64_t pos;
2298 int similar;
2299 unsigned char *buffer;
2300 const char *fname0;
2301 const char *fname;
2302 const char *fname_arch;
2303 uint64_t fsize;
2304 uint64_t found;
2305 } st_ucon64_filefile_t;
2306 #ifdef _MSC_VER
2307 #pragma warning(pop)
2308 #endif
2309
2310
2311 static inline size_t
2312 ucon64_filefile_func (void *buffer, size_t n, void *object)
2313 {
2314 st_ucon64_filefile_t *o = (st_ucon64_filefile_t *) object;
2315 size_t i, j, len = (size_t) MIN (FILEFILE_LARGE_BUF, o->fsize - o->pos);
2316 unsigned char *b = (unsigned char *) buffer;
2317
2318 ucon64_fread (o->buffer, o->pos, len, o->fname);
2319
2320 for (i = 0; i < n; i++)
2321 if (o->similar == TRUE ? // find start
2322 *(b + i) == *(o->buffer + i) :
2323 *(b + i) != *(o->buffer + i))
2324 {
2325 for (j = 0; i + j < n; j++)
2326 if (o->similar == TRUE ? // find end (len)
2327 *(b + i + j) != *(o->buffer + i + j) :
2328 *(b + i + j) == *(o->buffer + i + j))
2329 break;
2330
2331 fprintf (o->output, "%s:\n", o->fname0);
2332 dumper (o->output, b + i, j, o->pos0 + i, DUMPER_HEX);
2333
2334 fprintf (o->output, "%s", o->fname);
2335 if (o->fname_arch)
2336 fprintf (o->output, " (%s)", o->fname_arch);
2337 fputs (":\n", o->output);
2338 dumper (o->output, o->buffer + i, j, o->pos + i, DUMPER_HEX);
2339
2340 fputc ('\n', o->output);
2341
2342 i += j;
2343 o->found += j;
2344 }
2345
2346 return n;
2347 }
2348
2349
2350 void
2351 ucon64_filefile (const char *filename1, uint64_t start1, uint64_t start2, int similar)
2352 {
2353 uint64_t fsize1;
2354 st_ucon64_filefile_t o;
2355
2356 printf ("Comparing %s with %s", filename1, ucon64.fname);
2357 if (ucon64.fname_arch[0])
2358 printf (" (%s)", ucon64.fname_arch);
2359 fputc ('\n', stdout);
2360
2361 if (one_file (filename1, ucon64.fname))
2362 {
2363 printf ("%s and %s refer to the same file\n\n", filename1, ucon64.fname);
2364 return;
2365 }
2366
2367 fsize1 = fsizeof (filename1);
2368 if (fsize1 <= start1 || ucon64.fsize <= start2)
2369 return;
2370
2371 if ((o.buffer = (unsigned char *) malloc (FILEFILE_LARGE_BUF)) == NULL)
2372 {
2373 fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], FILEFILE_LARGE_BUF);
2374 return;
2375 }
2376
2377 o.fname0 = filename1;
2378 o.pos0 = start1;
2379
2380 o.fname = ucon64.fname;
2381 o.fname_arch = ucon64.fname_arch[0] ? ucon64.fname_arch : NULL;
2382 o.pos = start2;
2383 o.fsize = ucon64.fsize;
2384
2385 o.output = stdout;
2386 o.similar = similar;
2387 o.found = 0;
2388
2389 quick_io_func (ucon64_filefile_func, FILEFILE_LARGE_BUF, &o, start1, fsize1,
2390 filename1, "rb");
2391
2392 free (o.buffer);
2393
2394 printf ("Found %llu %s\n",
2395 (long long unsigned int) o.found, similar ?
2396 (o.found == 1 ? "similarity" : "similarities") :
2397 (o.found == 1 ? "difference" : "differences"));
2398 }
2399 #else
2400 #define FILEFILE_LARGE_BUF
2401 // When verifying if the code produces the same output when FILEFILE_LARGE_BUF
2402 // is defined as when it's not, be sure to use the same buffer size.
2403 void
ucon64_filefile(const char * filename1,uint64_t start1,uint64_t start2,int similar)2404 ucon64_filefile (const char *filename1, uint64_t start1, uint64_t start2, int similar)
2405 {
2406 int readok = 1;
2407 uint64_t fsize1, bytesread1, bytesread2, bytesleft1, bytesleft2, n_bytes = 0;
2408 #ifdef FILEFILE_LARGE_BUF
2409 unsigned int bufsize = 1024 * 1024;
2410 unsigned char *buf1, *buf2;
2411 #else
2412 unsigned int bufsize = MAXBUFSIZE;
2413 unsigned char buf1[MAXBUFSIZE], buf2[MAXBUFSIZE];
2414 #endif
2415 FILE *file1, *file2;
2416
2417 printf ("Comparing %s with %s", filename1, ucon64.fname);
2418 if (ucon64.fname_arch[0])
2419 printf (" (%s)", ucon64.fname_arch);
2420 fputc ('\n', stdout);
2421
2422 if (one_file (filename1, ucon64.fname))
2423 {
2424 printf ("%s and %s refer to the same file\n\n", filename1, ucon64.fname);
2425 return;
2426 }
2427
2428 fsize1 = fsizeof (filename1); // fsizeof() returns size in bytes
2429 if (fsize1 <= start1 || ucon64.fsize <= start2)
2430 return;
2431
2432 #ifdef FILEFILE_LARGE_BUF
2433 if ((buf1 = (unsigned char *) malloc (bufsize)) == NULL)
2434 {
2435 fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], bufsize);
2436 return;
2437 }
2438 if ((buf2 = (unsigned char *) malloc (bufsize)) == NULL)
2439 {
2440 free (buf1);
2441 fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], bufsize);
2442 return;
2443 }
2444 #endif
2445
2446 if ((file1 = fopen (filename1, "rb")) == NULL)
2447 {
2448 fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename1);
2449 #ifdef FILEFILE_LARGE_BUF
2450 free (buf1);
2451 free (buf2);
2452 #endif
2453 return ;
2454 }
2455 if ((file2 = fopen (ucon64.fname, "rb")) == NULL)
2456 {
2457 fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.fname);
2458 fclose (file1);
2459 #ifdef FILEFILE_LARGE_BUF
2460 free (buf1);
2461 free (buf2);
2462 #endif
2463 return;
2464 }
2465
2466 fseeko2 (file1, start1, SEEK_SET);
2467 fseeko2 (file2, start2, SEEK_SET);
2468 bytesleft1 = fsize1;
2469 bytesread1 = 0;
2470 bytesleft2 = ucon64.fsize;
2471 bytesread2 = 0;
2472
2473 while (bytesleft1 > 0 && bytesread1 < ucon64.fsize && readok)
2474 {
2475 size_t chunksize1 = fread (buf1, 1, bufsize, file1);
2476 if (chunksize1 == 0)
2477 readok = 0;
2478 else
2479 {
2480 bytesread1 += chunksize1;
2481 bytesleft1 -= chunksize1;
2482 }
2483
2484 while (bytesleft2 > 0 && bytesread2 < bytesread1 && readok)
2485 {
2486 size_t chunksize2 = fread (buf2, 1, chunksize1, file2);
2487 if (chunksize2 == 0)
2488 readok = 0;
2489 else
2490 {
2491 size_t base = 0;
2492 while (base < chunksize2)
2493 {
2494 if (similar == TRUE ?
2495 buf1[base] == buf2[base] :
2496 buf1[base] != buf2[base])
2497 {
2498 size_t len;
2499
2500 for (len = 0; base + len < chunksize2; len++)
2501 if (similar == TRUE ?
2502 buf1[base + len] != buf2[base + len] :
2503 buf1[base + len] == buf2[base + len])
2504 break;
2505
2506 printf ("%s:\n", filename1);
2507 dumper (stdout, &buf1[base], len, start1 + base + bytesread2,
2508 DUMPER_HEX);
2509
2510 printf ("%s", ucon64.fname);
2511 if (ucon64.fname_arch[0])
2512 printf (" (%s)", ucon64.fname_arch);
2513 puts (":");
2514 dumper (stdout, &buf2[base], len, start2 + base + bytesread2,
2515 DUMPER_HEX);
2516
2517 fputc ('\n', stdout);
2518
2519 base += len;
2520 n_bytes += len;
2521 }
2522 else
2523 base++;
2524 }
2525
2526 bytesread2 += chunksize2;
2527 bytesleft2 -= chunksize2;
2528 }
2529 }
2530 }
2531
2532 fclose (file1);
2533 fclose (file2);
2534 #ifdef FILEFILE_LARGE_BUF
2535 free (buf1);
2536 free (buf2);
2537 #endif
2538
2539 printf ("Found %llu %s\n",
2540 (long long unsigned int) n_bytes, similar ?
2541 (n_bytes == 1 ? "similarity" : "similarities") :
2542 (n_bytes == 1 ? "difference" : "differences"));
2543 }
2544 #endif
2545
2546
2547 int
ucon64_split(uint64_t part_size)2548 ucon64_split (uint64_t part_size)
2549 {
2550 uint64_t size = ucon64.fsize, nparts, surplus;
2551 unsigned int n;
2552 char dest_name[FILENAME_MAX], suffix[5] = ".001";
2553
2554 if (part_size >= size)
2555 {
2556 fprintf (stderr,
2557 "ERROR: A part size was specified that is larger than or equal to the file size\n"
2558 " (%llu >= %llu)\n",
2559 (long long unsigned int) part_size, (long long unsigned int) size);
2560 return -1;
2561 }
2562 else if (part_size == 0)
2563 {
2564 fputs ("ERROR: Part size must be larger than 0 bytes\n", stderr);
2565 return -1;
2566 }
2567
2568 nparts = size / part_size;
2569 surplus = size % part_size;
2570 if (nparts + (surplus ? 1 : 0) > 999)
2571 {
2572 fprintf (stderr,
2573 "ERROR: A part size was specified that would result in more than 999\n"
2574 " (%llu) files. Specify a size of %llu bytes or larger\n",
2575 (long long unsigned int) nparts + (surplus ? 1 : 0),
2576 (long long unsigned int) (size + 998U) / 999);
2577 return -1;
2578 }
2579
2580 strcpy (dest_name, ucon64.fname);
2581 set_suffix (dest_name, suffix);
2582 ucon64_output_fname (dest_name, 0);
2583
2584 for (n = 0; n < nparts; n++)
2585 {
2586 // don't write backups of parts, because one name is used
2587 fcopy (ucon64.fname, n * part_size, part_size, dest_name, "wb");
2588 printf (ucon64_msg[WROTE], dest_name);
2589
2590 snprintf (suffix, sizeof suffix, ".%03u", n + 2);
2591 suffix[sizeof suffix - 1] = '\0';
2592 set_suffix (dest_name, suffix);
2593 }
2594
2595 if (surplus)
2596 {
2597 // don't write backups of parts, because one name is used
2598 fcopy (ucon64.fname, n * part_size, surplus, dest_name, "wb");
2599 printf (ucon64_msg[WROTE], dest_name);
2600 }
2601
2602 return 0;
2603 }
2604