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