ucon64.c 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985
  1. /*
  2. uCON64 - a tool to modify video game ROMs and to transfer ROMs to the
  3. different backup units/emulators that exist. It is based on the old uCON but
  4. with completely new source. It aims to support all cartridge consoles and
  5. handhelds like N64, JAG, SNES, NG, GENESIS, GB, LYNX, PCE, SMS, GG, NES and
  6. their backup units
  7. Copyright (c) 1999 - 2005 NoisyB
  8. Copyright (c) 2001 - 2005, 2015 dbjh
  9. This program is free software; you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation; either version 2 of the License, or
  12. (at your option) any later version.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. GNU General Public License for more details.
  17. You should have received a copy of the GNU General Public License
  18. along with this program; if not, write to the Free Software
  19. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21. /*
  22. First I want to thank SiGMA SEVEN! who was my mentor and taught me how to
  23. write programs in C.
  24. */
  25. #ifdef HAVE_CONFIG_H
  26. #include "config.h"
  27. #endif
  28. #include <ctype.h>
  29. #include <stdlib.h>
  30. #ifdef HAVE_UNISTD_H
  31. #include <unistd.h>
  32. #endif
  33. #ifdef _MSC_VER
  34. #pragma warning(push)
  35. #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
  36. #endif
  37. #include <sys/stat.h>
  38. #ifdef _MSC_VER
  39. #pragma warning(pop)
  40. #endif
  41. #ifdef DEBUG
  42. #ifdef __GNUC__
  43. #warning DEBUG active
  44. #else
  45. #pragma message ("DEBUG active")
  46. #endif
  47. #endif
  48. #include "misc/archive.h"
  49. #include "misc/file.h"
  50. #include "misc/getopt2.h" // st_getopt2_t
  51. #include "misc/misc.h"
  52. #include "misc/parallel.h"
  53. #include "misc/property.h"
  54. #include "misc/term.h"
  55. #include "ucon64.h"
  56. #include "ucon64_dat.h"
  57. #include "ucon64_misc.h"
  58. #include "ucon64_opts.h"
  59. #include "console/atari.h"
  60. #include "console/coleco.h"
  61. #include "console/console.h"
  62. #include "console/dc.h"
  63. #include "console/gb.h"
  64. #include "console/gba.h"
  65. #include "console/genesis.h"
  66. #include "console/jaguar.h"
  67. #include "console/lynx.h"
  68. #include "console/n64.h"
  69. #include "console/nds.h"
  70. #include "console/neogeo.h"
  71. #include "console/nes.h"
  72. #include "console/ngp.h"
  73. #include "console/pce.h"
  74. #include "console/psx.h"
  75. #include "console/sms.h"
  76. #include "console/snes.h"
  77. #include "console/swan.h"
  78. #include "console/vboy.h"
  79. #include "backup/backup.h"
  80. #include "backup/cd64.h"
  81. #include "backup/cmc.h"
  82. #include "backup/dex.h"
  83. #include "backup/doctor64.h"
  84. #include "backup/doctor64jr.h"
  85. #include "backup/f2a.h"
  86. #include "backup/fal.h"
  87. #include "backup/gbx.h"
  88. #include "backup/gd.h"
  89. #include "backup/lynxit.h"
  90. #include "backup/mccl.h"
  91. #include "backup/mcd.h"
  92. #include "backup/md-pro.h"
  93. #include "backup/msg.h"
  94. #include "backup/pce-pro.h"
  95. #include "backup/pl.h"
  96. #include "backup/quickdev16.h"
  97. #include "backup/sflash.h"
  98. #include "backup/smc.h"
  99. #include "backup/smd.h"
  100. #include "backup/smsgg-pro.h"
  101. #include "backup/swc.h"
  102. #include "patch/aps.h"
  103. #include "patch/bsl.h"
  104. #include "patch/gg.h"
  105. #include "patch/ips.h"
  106. #include "patch/patch.h"
  107. #include "patch/ppf.h"
  108. #ifdef _MSC_VER
  109. // Visual C++ doesn't allow inline in C source code
  110. #define inline __inline
  111. #endif
  112. static void ucon64_exit (void);
  113. static int ucon64_execute_options (void);
  114. static void ucon64_rom_nfo (const st_ucon64_nfo_t *nfo);
  115. static st_ucon64_nfo_t *ucon64_probe (st_ucon64_nfo_t *nfo);
  116. static int ucon64_rom_handling (void);
  117. static int ucon64_process_rom (const char *fname);
  118. st_ucon64_t ucon64; // containes ptr to image, dat and nfo
  119. #ifdef AMIGA
  120. unsigned long __stacksize = 102400; // doesn't work on PPC? is StormC specific?
  121. //unsigned long __stack = 102400; // for SAS/C, DICE, GCC etc.?
  122. char vers[] = "$VER: uCON64 "UCON64_VERSION_S" "CURRENT_OS_S" ("__DATE__") ("__TIME__")";
  123. #endif
  124. typedef struct
  125. {
  126. int val; // (st_getopt2_t->val)
  127. char *optarg; // option argument
  128. int console; // the console (st_getopt2_t->object)
  129. int flags; // workflow flags (st_getopt2_t->object)
  130. } st_args_t;
  131. static st_args_t arg[UCON64_MAX_ARGS];
  132. static st_getopt2_t options[UCON64_MAX_ARGS];
  133. static const st_getopt2_t lf[] =
  134. {
  135. {NULL, 0, 0, 0, NULL, "", NULL},
  136. {NULL, 0, 0, 0, NULL, NULL, NULL}
  137. },
  138. *option[] =
  139. {
  140. ucon64_options_usage,
  141. lf,
  142. ucon64_padding_usage,
  143. lf,
  144. ucon64_dat_usage,
  145. lf,
  146. patch_usage,
  147. bsl_usage,
  148. ips_usage,
  149. aps_usage,
  150. ppf_usage,
  151. gg_usage,
  152. lf,
  153. #ifdef USE_DISCMAGE
  154. discmage_usage,
  155. lf,
  156. #endif
  157. dc_usage,
  158. lf,
  159. psx_usage,
  160. #ifdef USE_PARALLEL
  161. dex_usage,
  162. #endif
  163. lf,
  164. gba_usage,
  165. #if defined USE_PARALLEL || defined USE_USB
  166. // f2a_usage has to come before fal_usage in case only USE_USB is defined
  167. // (no support for parallel port)
  168. f2a_usage,
  169. #ifdef USE_PARALLEL
  170. fal_usage,
  171. #endif
  172. #endif // USE_PARALLEL || USE_USB
  173. #if 0
  174. // this backup unit uses CF cards for transfer
  175. sc_usage,
  176. #endif
  177. lf,
  178. nds_usage,
  179. lf,
  180. n64_usage,
  181. #ifdef USE_PARALLEL
  182. doctor64_usage,
  183. doctor64jr_usage,
  184. #ifdef USE_LIBCD64
  185. cd64_usage,
  186. #endif
  187. dex_usage,
  188. #endif // USE_PARALLEL
  189. lf,
  190. snes_usage,
  191. #ifdef USE_PARALLEL
  192. swc_usage,
  193. gd_usage,
  194. fig_usage,
  195. sflash_usage,
  196. // mgd_usage,
  197. #endif
  198. #ifdef USE_USB
  199. quickdev16_usage,
  200. #endif
  201. lf,
  202. neogeo_usage,
  203. lf,
  204. genesis_usage,
  205. #ifdef USE_PARALLEL
  206. smd_usage,
  207. mdpro_usage,
  208. mcd_usage,
  209. cmc_usage,
  210. // mgd_usage,
  211. #endif
  212. lf,
  213. gb_usage,
  214. #ifdef USE_PARALLEL
  215. gbx_usage,
  216. mccl_usage,
  217. #endif
  218. lf,
  219. lynx_usage,
  220. #ifdef USE_PARALLEL
  221. lynxit_usage,
  222. #endif
  223. lf,
  224. pce_usage,
  225. #ifdef USE_PARALLEL
  226. msg_usage,
  227. pcepro_usage,
  228. // mgd_usage,
  229. #endif
  230. lf,
  231. nes_usage,
  232. #ifdef USE_PARALLEL
  233. smc_usage,
  234. #endif
  235. lf,
  236. sms_usage,
  237. #ifdef USE_PARALLEL
  238. smsggpro_usage,
  239. #endif
  240. lf,
  241. swan_usage,
  242. lf,
  243. jaguar_usage,
  244. lf,
  245. ngp_usage,
  246. #ifdef USE_PARALLEL
  247. pl_usage,
  248. #endif
  249. lf,
  250. atari_usage,
  251. #if 0
  252. // these backup units use audio to transfer ROMs
  253. cc2_usage,
  254. spsc_usage,
  255. #endif
  256. lf,
  257. coleco_usage,
  258. lf,
  259. vboy_usage,
  260. NULL
  261. };
  262. static st_ucon64_nfo_t *
  263. ucon64_clear_nfo (st_ucon64_nfo_t *nfo)
  264. {
  265. if (nfo)
  266. memset (nfo, 0, sizeof (st_ucon64_nfo_t));
  267. ucon64.nfo = NULL;
  268. ucon64.crc32 = ucon64.fcrc32 = 0; // yes, this belongs here
  269. nfo->data_size = UCON64_UNKNOWN;
  270. return nfo;
  271. }
  272. //#define TEST
  273. #ifdef TEST
  274. typedef struct
  275. {
  276. int val;
  277. const char *cmdline;
  278. uint32_t crc32; // crc32 of cmdline's output
  279. } st_ucon64_test_t;
  280. void
  281. ucon64_test (void)
  282. {
  283. // default prepare and cleanup macros
  284. #define TEST_START ""
  285. #define TEST_END ""
  286. #define TEST_BREAK {0, NULL, 0},
  287. #define TEST_BUG 1
  288. #define TEST_TODO 2
  289. st_ucon64_test_t test[] =
  290. {
  291. {UCON64_1991, "ucon64 -1991 /tmp/test/test.smd;"
  292. "ucon64 -gen test.smd;"
  293. "rm test.smd", 0xadc940f4},
  294. {UCON64_A, "ucon64 -a", TEST_TODO},
  295. {UCON64_ATA, "ucon64 -ata /tmp/test/test.64k", 0x4b3a37d0},
  296. {UCON64_B, "ucon64 -b", TEST_TODO},
  297. {UCON64_B0, "ucon64 -b0 64 /tmp/test/test.lnx;"
  298. "ucon64 test.lnx;"
  299. "rm test.lnx", 0xde32e069},
  300. {UCON64_B1, "ucon64 -b1 64 /tmp/test/test.lnx;"
  301. "ucon64 test.lnx;"
  302. "rm test.lnx", 0x6b648320},
  303. {UCON64_BAT, "ucon64 -bat /tmp/test/test.nes", TEST_BUG},
  304. {UCON64_BIN, "ucon64 -bin", TEST_TODO},
  305. {UCON64_BIOS, "ucon64 -bios", TEST_TODO},
  306. {UCON64_BOT, "ucon64 -bot=test.bot /tmp/test/test.v64;"
  307. "ucon64 -crc test.bot;"
  308. "rm test.bot", 1},
  309. {UCON64_BS, "ucon64 -bs /tmp/test/test.smc", 0x18910ac6},
  310. {UCON64_C, "ucon64 -c /tmp/test/test.txt /tmp/test/12345678.abc", 0x2284888d},
  311. #if 0
  312. {UCON64_CC2, "ucon64 -cc2 /tmp/test/test.16k;"
  313. "ucon64 -crc test.wav;" // crc should be 0xc5cdd20f
  314. "rm test.wav", 3},
  315. #endif
  316. {UCON64_CHK, "ucon64 -chk /tmp/test/test.smc;"
  317. "ucon64 test.smc;"
  318. "rm test.smc", 0x3fa1e89a},
  319. {UCON64_CMNT, "ucon64 -cmnt", TEST_TODO},
  320. {UCON64_CODE, "ucon64 -code /tmp/test/test.txt", TEST_BUG},
  321. {UCON64_COL, "ucon64 -col 0xff00", 0xd4f45031},
  322. {UCON64_COLECO, "ucon64 -coleco /tmp/test/test.1mb", 0x2fb8741c},
  323. {UCON64_CRC, "ucon64 -crc /tmp/test/test.2kb", 0xd17fda4a},
  324. {UCON64_CRP, "ucon64 -crp", TEST_TODO},
  325. {UCON64_CS, "ucon64 -cs /tmp/test/test.txt /tmp/test/12345678.abc", 0xd6e61833},
  326. {UCON64_CTRL, "ucon64 -ctrl", TEST_TODO},
  327. {UCON64_CTRL2, "ucon64 -ctrl2", TEST_TODO},
  328. {UCON64_DB, "ucon64 -db", TEST_TODO},
  329. {UCON64_DBS, "ucon64 -dbs", TEST_TODO},
  330. {UCON64_DBUH, "ucon64 -dbuh", TEST_TODO},
  331. {UCON64_DBV, "ucon64 -dbv", TEST_TODO},
  332. {UCON64_DC, "ucon64 -dc /tmp/test/test.1mb", 0x14c9d369},
  333. {UCON64_DFIND, "ucon64 -dfind \"97 98 99 100\" /tmp/test/test.txt", 0xd7aed3fd},
  334. {UCON64_DFINDR, "ucon64 -dfindr \"1 2 3 4\" /tmp/test/test.txt", 0x88a6f737},
  335. {UCON64_DINT, "ucon64 -dint /tmp/test/test.txt;"
  336. "ucon64 -crc test.txt;"
  337. "rm test.txt", 0xe60a7df5},
  338. {UCON64_DMIRR, "ucon64 -dmirr", TEST_TODO},
  339. {UCON64_DNSRT, "ucon64 -dnsrt", TEST_TODO},
  340. {UCON64_DUAL, "ucon64 -dual", TEST_TODO},
  341. {UCON64_DUMPINFO, "ucon64 -dumpinfo", TEST_TODO},
  342. {UCON64_E, "ucon64 -e", TEST_TODO},
  343. {UCON64_EROM, "ucon64 -erom", TEST_TODO},
  344. {UCON64_F, "ucon64 -f", TEST_TODO},
  345. {UCON64_FDS, "ucon64 -fds", TEST_TODO},
  346. {UCON64_FDSL, "ucon64 -fdsl", TEST_TODO},
  347. {UCON64_FFE, "ucon64 -ffe", TEST_TODO},
  348. {UCON64_FIG, "ucon64 -fig", TEST_TODO},
  349. {UCON64_FIGS, "ucon64 -figs", TEST_TODO},
  350. {UCON64_FIND, "ucon64 -find \"abcd\" /tmp/test/test.txt", 0xd7aed3fd},
  351. {UCON64_FINDI, "ucon64 -findi \"ABcD\" /tmp/test/test.txt", 0x10e913cd},
  352. {UCON64_FINDR, "ucon64 -findr \"1234\" /tmp/test/test.txt", 0x1b1284d8},
  353. {UCON64_GB, "ucon64 -gb /tmp/test/test.1mb", 0xf050caa1},
  354. {UCON64_GBA, "ucon64 -gba /tmp/test/test.1mb", 0x5253861d},
  355. {UCON64_GBX, "ucon64 -gbx", TEST_TODO},
  356. {UCON64_GD3, "ucon64 -gd3", TEST_TODO},
  357. {UCON64_GD3S, "ucon64 -gd3s", TEST_TODO},
  358. {UCON64_GEN, "ucon64 -gen /tmp/test/test.1mb", 0xaa1f503c},
  359. {UCON64_GG, "ucon64 -gg", TEST_TODO},
  360. {UCON64_GGD, "ucon64 -ggd", TEST_TODO},
  361. {UCON64_GGE, "ucon64 -gge", TEST_TODO},
  362. {UCON64_HD, "ucon64 -snes -hd /tmp/test/test.1mb", 0x9ea45865},
  363. {UCON64_HDN, "ucon64 -snes -hdn=1024 /tmp/test/test.1mb", 0x37812a26},
  364. {UCON64_HEX, "ucon64 -hex /tmp/test/test.txt", 0x90d0b764},
  365. {UCON64_HFIND, "ucon64 -hfind \"? 68 ?? 6a\" /tmp/test/test.txt", 0xdfa06028},
  366. {UCON64_HFINDR, "ucon64 -hfindr \"01 02 03 04\" /tmp/test/test.txt", 0x88a6f737},
  367. {UCON64_HI, "ucon64 -snes -hi /tmp/test/test.1mb", 0x086266b1},
  368. {UCON64_I, "ucon64 -i", TEST_TODO},
  369. {UCON64_IDNUM, "ucon64 -idnum", TEST_TODO},
  370. {UCON64_IDPPF, "ucon64 -idppf", TEST_TODO},
  371. {UCON64_INES, "ucon64 -ines", TEST_TODO},
  372. {UCON64_INESHD, "ucon64 -ineshd", TEST_TODO},
  373. {UCON64_INS, "ucon64 -ins /tmp/test/test.txt;"
  374. "ucon64 -crc test.txt 2>&1;"
  375. "rm test.txt", 0xa87abae3},
  376. {UCON64_INSN, "ucon64 -insn=512 /tmp/test/test.txt;"
  377. "ucon64 -crc test.txt 2>&1;"
  378. "rm test.txt", 0xa87abae3},
  379. {UCON64_INT, "ucon64 -int /tmp/test/test.z64", 0x1d23f41c},
  380. {UCON64_INT2, "ucon64 -int2 /tmp/test/test.v64", 1},
  381. {UCON64_ISPAD, "ucon64 -ispad /tmp/test/test.2kb", 0xa5e38fbd},
  382. {UCON64_J, "ucon64 -j", TEST_TODO},
  383. {UCON64_JAG, "ucon64 -jag /tmp/test/test.1mb", 0x2d1f3594},
  384. {UCON64_K, "ucon64 -k", TEST_TODO},
  385. {UCON64_L, "ucon64 -l", TEST_TODO},
  386. {UCON64_LNX, "ucon64 -lnx", TEST_TODO},
  387. {UCON64_LOGO, "ucon64 -logo /tmp/test/test.gba", TEST_TODO},
  388. {UCON64_LS, "ucon64 -ls /tmp/test/*", TEST_BUG},
  389. {UCON64_LSD, "ucon64 -lsd /tmp/test/*", TEST_BUG},
  390. {UCON64_LSRAM, "ucon64 -lsram", TEST_TODO},
  391. {UCON64_LSV, "ucon64 -lsv", TEST_TODO},
  392. {UCON64_LYNX, "ucon64 -lynx /tmp/test/test.1mb", 0x78e02858},
  393. {UCON64_LYX, "ucon64 -lyx", TEST_TODO},
  394. {UCON64_MAPR, "ucon64 -mapr", TEST_TODO},
  395. {UCON64_MD5, "ucon64 -md5", TEST_TODO},
  396. {UCON64_MGD, "ucon64 -mgd", TEST_TODO},
  397. {UCON64_MGDGG, "ucon64 -mgdgg", TEST_TODO},
  398. {UCON64_MIRR, "ucon64 -mirr", TEST_TODO},
  399. {UCON64_MKA, "ucon64 -mka", TEST_TODO},
  400. {UCON64_MKDAT, "ucon64 -mkdat", TEST_TODO},
  401. {UCON64_MKI, "ucon64 -mki=/tmp/test/test.txt /tmp/test/test2.txt;"
  402. "ucon64 -crc test2.ips;"
  403. "rm test2.ips", 0xe2b26d35},
  404. {UCON64_MKIP, "ucon64 -mkip", TEST_TODO},
  405. {UCON64_MKPPF, "ucon64 -mkppf", TEST_TODO},
  406. {UCON64_MSG, "ucon64 -msg", TEST_TODO},
  407. {UCON64_MULTI, "ucon64 -multi", TEST_TODO},
  408. {UCON64_N, "ucon64 -n", TEST_TODO},
  409. {UCON64_N2, "ucon64 -n2", TEST_TODO},
  410. {UCON64_N2GB, "ucon64 -n2gb", TEST_TODO},
  411. {UCON64_N64, "ucon64 -n64 /tmp/test/test.1mb", 0x5eedaf08},
  412. {UCON64_NA, "ucon64 -na", TEST_TODO},
  413. {UCON64_NBAT, "ucon64 -nbat", TEST_TODO},
  414. {UCON64_NBS, "ucon64 -nbs", TEST_TODO},
  415. {UCON64_NCOL, "ucon64 -ncol -snes /tmp/test/test.1mb", 0xf3091231},
  416. {UCON64_NDS, "ucon64 -nds", TEST_TODO},
  417. {UCON64_NES, "ucon64 -nes", TEST_TODO},
  418. {UCON64_NG, "ucon64 -ng", TEST_TODO},
  419. {UCON64_NGP, "ucon64 -ngp", TEST_TODO},
  420. {UCON64_NHD, "ucon64 -nhd /tmp/test/test.smc", 0x1a5cc5d4},
  421. {UCON64_NHI, "ucon64 -snes -nhi /tmp/test/test.1mb", 0xf3091231},
  422. {UCON64_NINT, "ucon64 -nint", TEST_TODO},
  423. {UCON64_NPPF, "ucon64 -nppf", TEST_TODO},
  424. {UCON64_NROT, "ucon64 -nrot", TEST_TODO},
  425. {UCON64_NS, "ucon64 -ns", TEST_TODO},
  426. {UCON64_NTSC, "ucon64 -ntsc", TEST_TODO},
  427. {UCON64_NVRAM, "ucon64 -nvram", TEST_TODO},
  428. {UCON64_P, "ucon64 -p", TEST_TODO},
  429. {UCON64_PAD, "ucon64 -pad", TEST_TODO},
  430. {UCON64_PADN, "ucon64 -padn", TEST_TODO},
  431. {UCON64_PAL, "ucon64 -pal", TEST_TODO},
  432. {UCON64_PARSE, "ucon64 -parse", TEST_TODO},
  433. {UCON64_PASOFAMI, "ucon64 -pasofami", TEST_TODO},
  434. {UCON64_PATCH, "ucon64 -patch", TEST_TODO},
  435. {UCON64_PATTERN, "ucon64 -pattern", TEST_TODO},
  436. {UCON64_PCE, "ucon64 -pce", TEST_TODO},
  437. {UCON64_POKE, "ucon64 -poke", TEST_TODO},
  438. {UCON64_PPF, "ucon64 -ppf", TEST_TODO},
  439. {UCON64_PRINT, "ucon64 -print /tmp/test/test.txt", 0x5c4acd52},
  440. {UCON64_PSX, "ucon64 -psx /tmp/test/test.1mb", 0x79b34e40},
  441. {UCON64_R83, "cp /tmp/test/1234567890.abcd .;"
  442. "ucon64 -r83 1234567890.abcd;"
  443. "rm 12345eeb.abc", 0x1f791880},
  444. {UCON64_RDAT, "ucon64 -rdat", TEST_TODO},
  445. {UCON64_REGION, "ucon64 -region", TEST_TODO},
  446. {UCON64_RJOLIET, "cp /tmp/test/1234567890123456789012345678901234567890123456789012345678901234567890.abcd .;"
  447. "ucon64 -rjoliet 1234567890123456789012345678901234567890123456789012345678901234567890.abcd;"
  448. "rm 123456789012345678901234567890123456789012345678901234566f5.abcd", 0xd5cfab05},
  449. {UCON64_RL, "cp /tmp/test/12345678.ABC .;"
  450. "ucon64 -rl 12345678.ABC;"
  451. "rm 12345678.abc", 0x48934d06},
  452. {UCON64_ROTL, "ucon64 -rotl", TEST_TODO},
  453. {UCON64_ROTR, "ucon64 -rotr", TEST_TODO},
  454. {UCON64_RROM, "cp /tmp/test/test.smc .;"
  455. "ucon64 -rrom test.smc;"
  456. "rm \"Mode 7 interactive de.smc\"", 0xaaa714b6},
  457. {UCON64_RU, "cp /tmp/test/12345678.abc .;"
  458. "ucon64 -ru 12345678.abc;"
  459. "rm 12345678.ABC", 0x0c9f305f},
  460. {UCON64_S, "ucon64 -s", TEST_TODO},
  461. {UCON64_SAM, "ucon64 -sam", TEST_TODO},
  462. {UCON64_SC, "ucon64 -sc", TEST_TODO},
  463. {UCON64_SCAN, "ucon64 -scan", TEST_TODO},
  464. {UCON64_SCR, "ucon64 -scr", TEST_TODO},
  465. {UCON64_SGB, "ucon64 -sgb", TEST_TODO},
  466. {UCON64_SHA1, "ucon64 -sha1 /tmp/test/test.txt", 0x65608105},
  467. {UCON64_SMC, "ucon64 -smc", TEST_TODO},
  468. {UCON64_SMD, "ucon64 -smd", TEST_TODO},
  469. {UCON64_SMDS, "ucon64 -smds", TEST_TODO},
  470. {UCON64_SMS, "ucon64 -sms /tmp/test/test.1mb", 0x73996f1d},
  471. {UCON64_SNES, "ucon64 -snes /tmp/test/test.1mb", 0xf3091231},
  472. {UCON64_SRAM, "ucon64 -sram", TEST_TODO},
  473. {UCON64_SSC, "ucon64 -ssc", TEST_TODO},
  474. {UCON64_SSIZE, "ucon64 -ssize", TEST_TODO},
  475. {UCON64_STP, "ucon64 -stp /tmp/test/test.64k;"
  476. "ucon64 -crc test.64k;"
  477. "rm test.64k", 0xe1a4cd85},
  478. {UCON64_STPN, "ucon64 -stpn=512 /tmp/test/test.64k;"
  479. "ucon64 -crc test.64k;"
  480. "rm test.64k", 0xe1a4cd85},
  481. {UCON64_STRIP, "ucon64 -strip=512 /tmp/test/test.64k;"
  482. "ucon64 -crc test.64k;"
  483. "rm test.64k", 0xe1a4cd85},
  484. {UCON64_SWAN, "ucon64 -swan /tmp/test/test.1mb", 0x8e07f287},
  485. {UCON64_SWAP, "ucon64 -swap /tmp/test/test.txt;"
  486. "ucon64 -crc test.txt;"
  487. "rm test.txt", 0xe60a7df5},
  488. {UCON64_SWAP2, "ucon64 -swap2 /tmp/test/test.txt;"
  489. "ucon64 -crc test.txt;"
  490. "rm test.txt", 0xd5f4368d},
  491. {UCON64_SWC, "ucon64 -swc /tmp/test/test.1mb;"
  492. "ucon64 test.swc;"
  493. "rm test.swc", 0xa9c3730f},
  494. {UCON64_SWCS, "ucon64 -swcs", TEST_TODO},
  495. {UCON64_UFO, "ucon64 -ufo", TEST_TODO},
  496. {UCON64_UFOS, "ucon64 -ufos", TEST_TODO},
  497. {UCON64_UNIF, "ucon64 -unif /tmp/test/test.nes", TEST_BUG},
  498. {UCON64_UNSCR, "ucon64 -unscr", TEST_TODO},
  499. {UCON64_USMS, "ucon64 -usms", TEST_TODO},
  500. {UCON64_V64, "ucon64 -v64 /tmp/test/test.z64;"
  501. "ucon64 test.v64;"
  502. "rm test.v64", 1},
  503. {UCON64_VBOY, "ucon64 -vboy /tmp/test/test.1mb", 0x87661ae8},
  504. {UCON64_VRAM, "ucon64 -vram", TEST_TODO},
  505. {UCON64_Z64, "ucon64 -z64 /tmp/test/test.v64;"
  506. "ucon64 test.z64;"
  507. "rm test.z64", 1},
  508. TEST_BREAK
  509. {UCON64_VER, "ucon64 -version", 0}, // NO TEST: changes always
  510. {UCON64_V, "ucon64 -v", 0}, // NO TEST: verbose switch
  511. {UCON64_Q, "ucon64 -q", 0}, // NO TEST: quiet switch
  512. {UCON64_HELP, "ucon64 -help", 0}, // NO TEST: usage changes always
  513. {UCON64_R, "ucon64 -r", 0}, // NO TEST: recursion
  514. {UCON64_O, "ucon64 -o", 0}, // NO TEST: output
  515. {UCON64_NBAK, "ucon64 -nbak", 0}, // NO TEST: no backup
  516. {UCON64_ROM, "ucon64 -rom", 0}, // NO TEST: hidden option or deprecated
  517. {UCON64_3DO, "ucon64 -3do", 0}, // NO TEST: hidden option or deprecated
  518. {UCON64_CRCHD, "ucon64 -crchd /tmp/test/test.2kb", 0x707bbaf1}, // NO TEST: hidden option or deprecated
  519. {UCON64_FILE, "ucon64 -file", 0}, // NO TEST: hidden option or deprecated
  520. {UCON64_FRONTEND, "ucon64 -frontend", 0}, // NO TEST: hidden option or deprecated
  521. {UCON64_GC, "ucon64 -gc", 0}, // NO TEST: hidden option or deprecated
  522. {UCON64_GP32, "ucon64 -gp32", 0}, // NO TEST: hidden option or deprecated
  523. {UCON64_ID, "ucon64 -id", 0}, // NO TEST: hidden option or deprecated
  524. {UCON64_INTELLI, "ucon64 -intelli", 0}, // NO TEST: hidden option or deprecated
  525. {UCON64_PS2, "ucon64 -ps2", 0}, // NO TEST: hidden option or deprecated
  526. {UCON64_S16, "ucon64 -s16", 0}, // NO TEST: hidden option or deprecated
  527. {UCON64_SAT, "ucon64 -sat", 0}, // NO TEST: hidden option or deprecated
  528. {UCON64_VEC, "ucon64 -vec", 0}, // NO TEST: hidden option or deprecated
  529. {UCON64_XBOX, "ucon64 -xbox", 0}, // NO TEST: hidden option or deprecated
  530. {UCON64_BIN2ISO, "ucon64 -bin2iso", 0}, // NO TEST: discmage
  531. {UCON64_DISC, "ucon64 -disc", 0}, // NO TEST: discmage
  532. {UCON64_ISOFIX, "ucon64 -isofix", 0}, // NO TEST: discmage
  533. {UCON64_MKCUE, "ucon64 -mkcue", 0}, // NO TEST: discmage
  534. {UCON64_MKSHEET, "ucon64 -mksheet", 0}, // NO TEST: discmage
  535. {UCON64_MKTOC, "ucon64 -mktoc", 0}, // NO TEST: discmage
  536. {UCON64_RIP, "ucon64 -rip", 0}, // NO TEST: discmage
  537. {UCON64_PORT, "ucon64 -port", 0}, // NO TEST: transfer code
  538. {UCON64_XCMC, "ucon64 -xcmc", 0}, // NO TEST: transfer code
  539. {UCON64_XCMCM, "ucon64 -xcmcm", 0}, // NO TEST: transfer code
  540. {UCON64_XCMCT, "ucon64 -xcmct", 0}, // NO TEST: transfer code
  541. {UCON64_XDEX, "ucon64 -xdex", 0}, // NO TEST: transfer code
  542. {UCON64_XDJR, "ucon64 -xdjr", 0}, // NO TEST: transfer code
  543. {UCON64_XF2A, "ucon64 -xf2a", 0}, // NO TEST: transfer code
  544. {UCON64_XF2AB, "ucon64 -xf2ab", 0}, // NO TEST: transfer code
  545. {UCON64_XF2AC, "ucon64 -xf2ac", 0}, // NO TEST: transfer code
  546. {UCON64_XF2AMULTI, "ucon64 -xf2amulti", 0}, // NO TEST: transfer code
  547. {UCON64_XF2AS, "ucon64 -xf2as", 0}, // NO TEST: transfer code
  548. {UCON64_XFAL, "ucon64 -xfal", 0}, // NO TEST: transfer code
  549. {UCON64_XFALB, "ucon64 -xfalb", 0}, // NO TEST: transfer code
  550. {UCON64_XFALC, "ucon64 -xfalc", 0}, // NO TEST: transfer code
  551. {UCON64_XFALM, "ucon64 -xfalm", 0}, // NO TEST: transfer code
  552. {UCON64_XFALMULTI, "ucon64 -xfalmulti", 0}, // NO TEST: transfer code
  553. {UCON64_XFALS, "ucon64 -xfals", 0}, // NO TEST: transfer code
  554. {UCON64_XFIG, "ucon64 -xfig", 0}, // NO TEST: transfer code
  555. {UCON64_XFIGC, "ucon64 -xfigc", 0}, // NO TEST: transfer code
  556. {UCON64_XFIGS, "ucon64 -xfigs", 0}, // NO TEST: transfer code
  557. {UCON64_XGBX, "ucon64 -xgbx", 0}, // NO TEST: transfer code
  558. {UCON64_XGBXB, "ucon64 -xgbxb", 0}, // NO TEST: transfer code
  559. {UCON64_XGBXM, "ucon64 -xgbxm", 0}, // NO TEST: transfer code
  560. {UCON64_XGBXS, "ucon64 -xgbxs", 0}, // NO TEST: transfer code
  561. {UCON64_XGD3, "ucon64 -xgd3", 0}, // NO TEST: transfer code
  562. {UCON64_XGD3R, "ucon64 -xgd3r", 0}, // NO TEST: transfer code
  563. {UCON64_XGD3S, "ucon64 -xgd3s", 0}, // NO TEST: transfer code
  564. {UCON64_XGD6, "ucon64 -xgd6", 0}, // NO TEST: transfer code
  565. {UCON64_XGD6R, "ucon64 -xgd6r", 0}, // NO TEST: transfer code
  566. {UCON64_XGD6S, "ucon64 -xgd6s", 0}, // NO TEST: transfer code
  567. {UCON64_XGG, "ucon64 -xgg", 0}, // NO TEST: transfer code
  568. {UCON64_XGGB, "ucon64 -xggb", 0}, // NO TEST: transfer code
  569. {UCON64_XGGS, "ucon64 -xggs", 0}, // NO TEST: transfer code
  570. {UCON64_XLIT, "ucon64 -xlit", 0}, // NO TEST: transfer code
  571. {UCON64_XMCCL, "ucon64 -xmccl", 0}, // NO TEST: transfer code
  572. {UCON64_XMCD, "ucon64 -xmcd", 0}, // NO TEST: transfer code
  573. {UCON64_XMD, "ucon64 -xmd", 0}, // NO TEST: transfer code
  574. {UCON64_XMDB, "ucon64 -xmdb", 0}, // NO TEST: transfer code
  575. {UCON64_XMDS, "ucon64 -xmds", 0}, // NO TEST: transfer code
  576. {UCON64_XMSG, "ucon64 -xmsg", 0}, // NO TEST: transfer code
  577. {UCON64_XPCE, "ucon64 -xpce", 0}, // NO TEST: transfer code
  578. {UCON64_XPL, "ucon64 -xpl", 0}, // NO TEST: transfer code
  579. {UCON64_XPLI, "ucon64 -xpli", 0}, // NO TEST: transfer code
  580. {UCON64_XPLM, "ucon64 -xplm", 0}, // NO TEST: transfer code
  581. {UCON64_XQD16, "ucon64 -xqd16", 0}, // NO TEST: transfer code
  582. {UCON64_XRESET, "ucon64 -xreset", 0}, // NO TEST: transfer code
  583. {UCON64_XSF, "ucon64 -xsf", 0}, // NO TEST: transfer code
  584. {UCON64_XSFS, "ucon64 -xsfs", 0}, // NO TEST: transfer code
  585. {UCON64_XSMC, "ucon64 -xsmc", 0}, // NO TEST: transfer code
  586. {UCON64_XSMCR, "ucon64 -xsmcr", 0}, // NO TEST: transfer code
  587. {UCON64_XSMD, "ucon64 -xsmd", 0}, // NO TEST: transfer code
  588. {UCON64_XSMDS, "ucon64 -xsmds", 0}, // NO TEST: transfer code
  589. {UCON64_XSWC, "ucon64 -xswc", 0}, // NO TEST: transfer code
  590. {UCON64_XSWC_IO, "ucon64 -xswc-io", 0}, // NO TEST: transfer code
  591. {UCON64_XSWC2, "ucon64 -xswc2", 0}, // NO TEST: transfer code
  592. {UCON64_XSWCC, "ucon64 -xswcc", 0}, // NO TEST: transfer code
  593. {UCON64_XSWCR, "ucon64 -xswcr", 0}, // NO TEST: transfer code
  594. {UCON64_XSWCS, "ucon64 -xswcs", 0}, // NO TEST: transfer code
  595. {UCON64_XV64, "ucon64 -xv64", 0}, // NO TEST: transfer code
  596. {0, NULL, 0}
  597. };
  598. int x = 0;
  599. unsigned int crc = 0;
  600. char buf[MAXBUFSIZE], fname[FILENAME_MAX];
  601. #ifdef DEBUG
  602. //#if 1
  603. // this is why no external script is used for testing
  604. {
  605. int c = 0, y = 0;
  606. // convert (st_getopt2_t **) to (st_getopt2_t *)
  607. memset (&options, 0, sizeof (st_getopt2_t) * UCON64_MAX_ARGS);
  608. for (c = x = 0; option[x]; x++)
  609. for (y = 0; option[x][y].name || option[x][y].help; y++)
  610. if (c < UCON64_MAX_ARGS)
  611. {
  612. memcpy (&options[c], &option[x][y], sizeof (st_getopt2_t));
  613. c++;
  614. }
  615. // do we test ALL options?
  616. for (x = 0; options[x].name || options[x].help; x++)
  617. if (options[x].val)
  618. {
  619. int found = 0;
  620. for (y = 0; test[y].val; y++)
  621. if (options[x].val == test[y].val)
  622. {
  623. found = 1;
  624. break;
  625. }
  626. if (!found)
  627. printf ("option: %4d \"%s\" will NOT be tested\n", options[x].val, options[x].name);
  628. }
  629. }
  630. #endif
  631. for (x = 0; test[x].val; x++)
  632. {
  633. FILE *in = NULL, *out = NULL;
  634. const char *state = NULL;
  635. // NO testing?
  636. if (!test[x].cmdline || !test[x].crc32)
  637. continue;
  638. crc = 0;
  639. if (test[x].crc32 != TEST_BUG && test[x].crc32 != TEST_TODO)
  640. {
  641. sprintf (buf, "%s", test[x].cmdline);
  642. if (!(in = popen (buf, "r")))
  643. {
  644. fprintf (stderr, "ERROR: cmdline \"%s\"\n", test[x].cmdline);
  645. continue;
  646. }
  647. sprintf (fname, "%d-output.txt", test[x].val);
  648. out = fopen (fname, "w");
  649. while ((fgets (buf, MAXBUFSIZE, in)))
  650. {
  651. crc = crc32 (crc, (const void *) &buf, strlen (buf));
  652. fputs (buf, out);
  653. }
  654. }
  655. sprintf (buf, "option: %4d crc: 0x%08x calc: 0x%08x status: ",
  656. test[x].val,
  657. test[x].crc32,
  658. crc);
  659. if (test[x].crc32 == TEST_BUG)
  660. state = "BUG!";
  661. else if (test[x].crc32 == TEST_TODO)
  662. state = "TODO ";
  663. else if (test[x].crc32 == crc)
  664. state = "OK ";
  665. else
  666. state = "BUG?";
  667. sprintf (strchr (buf, 0), "%5s (%s)\n", state, test[x].cmdline);
  668. printf (buf);
  669. fflush (stdout);
  670. if (test[x].crc32 != TEST_BUG && test[x].crc32 != TEST_TODO)
  671. {
  672. fputs ("^^^ ", out);
  673. fputs (buf, out);
  674. fclose (in);
  675. fclose (out);
  676. }
  677. }
  678. exit (0);
  679. }
  680. #endif
  681. #ifdef DEBUG
  682. void
  683. ucon64_runtime_debug_output (st_getopt2_t *p)
  684. {
  685. printf ("{\"%s\", %d, 0, %d, \"%s\", \"%s\", %d}, // console: %d workflow: %d\n",
  686. p->name,
  687. p->has_arg,
  688. p->val,
  689. p->arg_name,
  690. p->help ? "usage" : p->help, // i (nb) mean it
  691. // p->help,
  692. 0,
  693. p->object ? ((st_ucon64_obj_t *) p->object)->console : 0,
  694. p->object ? ((st_ucon64_obj_t *) p->object)->flags : 0);
  695. }
  696. static void
  697. ucon64_runtime_debug (void)
  698. {
  699. int x = 0, y = 0, c = 0;
  700. (void) x;
  701. (void) y;
  702. (void) c;
  703. #if 0
  704. // how many options (incl. dupes) do we have?
  705. for (x = y = 0; options[x].name || options[x].help; x++)
  706. if (options[x].name)
  707. y++;
  708. printf ("DEBUG: Total options (with dupes): %d\n", y);
  709. printf ("DEBUG: UCON64_MAX_ARGS == %d, %s\n", UCON64_MAX_ARGS,
  710. (y < UCON64_MAX_ARGS ? "good" : "\nERROR: too small; must be larger than options"));
  711. #endif
  712. #if 1
  713. // list all options as a single st_getopt2_t array
  714. for (x = 0; options[x].name || options[x].help; x++)
  715. if (options[x].name)
  716. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  717. #endif
  718. #if 0
  719. // how many consoles does uCON64 support?
  720. for (x = y = 0; options[x].name || options[x].help; x++)
  721. if (options[x].name && options[x].object)
  722. if (options[x].val == ((st_ucon64_obj_t *) options[x].object)->console)
  723. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  724. #endif
  725. #if 0
  726. // find options without an object (allowed)
  727. for (x = 0; options[x].name || options[x].help; x++)
  728. if (options[x].name && !options[x].object)
  729. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  730. #endif
  731. #if 0
  732. // find options without a console (allowed)
  733. for (x = 0; options[x].name || options[x].help; x++)
  734. if (options[x].name && !((st_ucon64_obj_t *) options[x].object)->console)
  735. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  736. #endif
  737. #if 0
  738. // find options without a workflow (allowed)
  739. for (x = 0; options[x].name || options[x].help; x++)
  740. if (options[x].name && !((st_ucon64_obj_t *) options[x].object)->flags)
  741. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  742. #endif
  743. #if 0
  744. // find options without a val (NOT allowed)
  745. for (x = 0; options[x].name || options[x].help; x++)
  746. if (options[x].name && !options[x].val)
  747. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  748. #endif
  749. #if 0
  750. // find options with has_arg but without arg_name AND/OR usage
  751. // hidden options without arg_name AND usage are allowed
  752. for (x = 0; options[x].name || options[x].help; x++)
  753. if (options[x].name &&
  754. ((!options[x].has_arg && options[x].arg_name) ||
  755. (options[x].has_arg && !options[x].arg_name) ||
  756. !options[x].help))
  757. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  758. #endif
  759. #if 0
  760. // find dupe (NOT a problem) options that have different values for val,
  761. // flag, and/or object (NOT allowed)
  762. // getopt1() will always use the 1st option in the array
  763. // (st_getopt2_t *)->arg_name and (st_getopt2_t *)->help can be as
  764. // different as you like
  765. for (x = 0; options[x].name || options[x].help; x++)
  766. if (options[x].name)
  767. for (y = 0; options[y].name || options[y].help; y++)
  768. if (options[y].name && x != y) // IS option
  769. if (!strcmp (options[y].name, options[x].name))
  770. if (options[y].has_arg != options[x].has_arg || // (NOT allowed)
  771. options[y].flag != options[x].flag || // (NOT allowed)
  772. options[y].val != options[x].val || // (NOT allowed)
  773. // options[y].arg_name != options[x].arg_name || // (allowed)
  774. // options[y].help != options[x].help || // (allowed)
  775. ((st_ucon64_obj_t *) options[y].object)->console != ((st_ucon64_obj_t *) options[x].object)->console // (NOT allowed)
  776. ((st_ucon64_obj_t *) options[x].object)->flags != ((st_ucon64_obj_t *) options[x].object)->flags) // (NOT allowed)
  777. {
  778. fputs ("ERROR: different dupe options found\n ", stdout);
  779. ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]);
  780. fputs (" ", stdout);
  781. ucon64_runtime_debug_output ((st_getopt2_t *) &options[y]);
  782. fputs ("\n\n", stdout);
  783. }
  784. #endif
  785. puts ("DEBUG: Sanity check finished");
  786. fflush (stdout);
  787. }
  788. #endif // DEBUG
  789. static void
  790. ucon64_exit (void)
  791. {
  792. #ifdef USE_DISCMAGE
  793. if (ucon64.discmage_enabled && ucon64.image)
  794. dm_close ((dm_image_t *) ucon64.image);
  795. #endif
  796. handle_registered_funcs ();
  797. fflush (stdout);
  798. }
  799. int
  800. main (int argc, char **argv)
  801. {
  802. int result = 0;
  803. int x = 0, y = 0, c = 0;
  804. const char *p = NULL;
  805. struct stat fstate;
  806. struct option long_options[UCON64_MAX_ARGS];
  807. #ifdef TEST
  808. if (argc == 1)
  809. ucon64_test ();
  810. #else
  811. printf ("uCON64 " UCON64_VERSION_S " " CURRENT_OS_S " 1999-2016\n"
  812. "Uses code from various people. See 'developers.html' for more!\n"
  813. "This may be freely redistributed under the terms of the GNU Public License\n\n");
  814. #endif
  815. if (atexit (ucon64_exit) == -1)
  816. {
  817. fputs ("ERROR: Could not register function with atexit()\n", stderr);
  818. exit (1);
  819. }
  820. // flush st_ucon64_t
  821. memset (&ucon64, 0, sizeof (st_ucon64_t));
  822. // these members of ucon64 (except fname and fname_arch) don't change per file
  823. ucon64.argc = argc;
  824. ucon64.argv = argv; // must be set prior to calling
  825. // ucon64_load_discmage() (for DOS)
  826. ucon64.fname =
  827. ucon64.file =
  828. ucon64.mapr =
  829. ucon64.comment = "";
  830. ucon64.fname_arch[0] = 0;
  831. ucon64.recursive =
  832. ucon64.parport_needed =
  833. ucon64.io_mode = 0;
  834. ucon64.battery =
  835. ucon64.bs_dump =
  836. ucon64.backup_header_len =
  837. ucon64.org_console =
  838. ucon64.controller =
  839. ucon64.controller2 =
  840. ucon64.do_not_calc_crc =
  841. ucon64.id =
  842. ucon64.interleaved =
  843. ucon64.mirror =
  844. ucon64.part_size =
  845. ucon64.region =
  846. ucon64.snes_header_base =
  847. ucon64.snes_hirom =
  848. ucon64.tv_standard =
  849. ucon64.use_dump_info =
  850. ucon64.vram = UCON64_UNKNOWN;
  851. ucon64.flags = WF_DEFAULT;
  852. // ucon64.console does change per file
  853. ucon64.console = ucon64.org_console;
  854. // convert (st_getopt2_t **) to (st_getopt2_t *)
  855. memset (&options, 0, sizeof (st_getopt2_t) * UCON64_MAX_ARGS);
  856. for (c = x = 0; option[x]; x++)
  857. for (y = 0; option[x][y].name || option[x][y].help; y++)
  858. if (c < UCON64_MAX_ARGS)
  859. {
  860. memcpy (&options[c], &option[x][y], sizeof (st_getopt2_t));
  861. c++;
  862. }
  863. #ifdef DEBUG
  864. ucon64_runtime_debug (); // check (st_getopt2_t *) options consistency
  865. #endif
  866. // configfile handling
  867. #ifdef __unix__
  868. // We need to modify the umask, because the configfile is made while we are
  869. // still running in root mode. Maybe 0 is even better (in case root did
  870. // `chmod +s').
  871. umask (002);
  872. #endif
  873. realpath2 (PROPERTY_HOME_RC ("ucon64"), ucon64.configfile);
  874. result = property_check (ucon64.configfile, UCON64_CONFIG_VERSION, 1);
  875. if (result == 1) // update needed
  876. result = ucon64_set_property_array ();
  877. if (result == -1) // property_check() or update failed
  878. return -1;
  879. #ifdef USE_ANSI_COLOR
  880. // ANSI colors?
  881. ucon64.ansi_color = get_property_int (ucon64.configfile, "ansi_color");
  882. // the conditional call to ansi_init() has to be done *after* the check for
  883. // the switch --ncol
  884. #endif
  885. // parallel port?
  886. #if defined USE_PPDEV || defined AMIGA
  887. p = get_property (ucon64.configfile, "parport_dev", PROPERTY_MODE_FILENAME);
  888. x = sizeof (ucon64.parport_dev);
  889. #ifdef USE_PPDEV
  890. strncpy (ucon64.parport_dev, p ? p : "/dev/parport0", x)[x - 1] = 0;
  891. #elif defined AMIGA
  892. strncpy (ucon64.parport_dev, p ? p : "parallel.device", x)[x - 1] = 0;
  893. #endif
  894. #endif
  895. p = get_property (ucon64.configfile, "parport", PROPERTY_MODE_TEXT);
  896. if (p)
  897. sscanf (p, "%hx", &ucon64.parport);
  898. else
  899. // use -1 (UCON64_UNKNOWN) to force probing if the config file doesn't contain
  900. // a parport line
  901. ucon64.parport = (uint16_t) -1;
  902. // make backups?
  903. ucon64.backup = get_property_int (ucon64.configfile, "backups");
  904. // $HOME/.ucon64/ ?
  905. p = get_property (ucon64.configfile, "ucon64_configdir", PROPERTY_MODE_FILENAME);
  906. if (p)
  907. {
  908. x = sizeof (ucon64.configdir);
  909. strncpy (ucon64.configdir, p, x)[x - 1] = 0;
  910. }
  911. else
  912. *ucon64.configdir = 0;
  913. // DAT file handling
  914. ucon64.dat_enabled = 0;
  915. p = get_property (ucon64.configfile, "ucon64_datdir", PROPERTY_MODE_FILENAME);
  916. if (p)
  917. {
  918. x = sizeof (ucon64.datdir);
  919. strncpy (ucon64.datdir, p, x)[x - 1] = 0;
  920. }
  921. else
  922. *ucon64.datdir = 0;
  923. // we use ucon64.datdir as path to the dats
  924. if (!access (ucon64.datdir,
  925. // !W_OK doesn't mean that files can't be written to dir for Win32 exe's
  926. #if !defined __CYGWIN__ && !defined _WIN32
  927. W_OK |
  928. #endif
  929. R_OK | X_OK) &&
  930. !stat (ucon64.datdir, &fstate) && S_ISDIR (fstate.st_mode))
  931. ucon64.dat_enabled = 1;
  932. if (!ucon64.dat_enabled && !access (ucon64.configdir,
  933. #if !defined __CYGWIN__ && !defined _WIN32
  934. W_OK |
  935. #endif
  936. R_OK | X_OK) &&
  937. !stat (ucon64.configdir, &fstate) && S_ISDIR (fstate.st_mode))
  938. {
  939. // fprintf (stderr, "Please move your DAT files from %s to %s\n\n",
  940. // ucon64.configdir, ucon64.datdir);
  941. strcpy (ucon64.datdir, ucon64.configdir); // use .ucon64/ instead of .ucon64/dat/
  942. ucon64.dat_enabled = 1;
  943. }
  944. #ifdef USE_DISCMAGE
  945. // load libdiscmage (should be done before handling the switches (--ver), but
  946. // ucon64_usage() has a dependency as well)
  947. ucon64.discmage_enabled = ucon64_load_discmage ();
  948. #endif
  949. if (argc < 2)
  950. {
  951. ucon64_usage (argc, argv, USAGE_VIEW_SHORT);
  952. return 0;
  953. }
  954. // turn st_getopt2_t into struct option
  955. getopt2_long_only (long_options, options, UCON64_MAX_ARGS);
  956. // getopt() is utilized to make uCON64 handle/parse cmdlines in a sane
  957. // and expected way
  958. x = optind = 0;
  959. memset (&arg, 0, sizeof (st_args_t) * UCON64_MAX_ARGS);
  960. while ((c = getopt_long_only (argc, argv, "", long_options, NULL)) != -1)
  961. {
  962. if (c == '?') // getopt() returns 0x3f ('?') when an unknown option was given
  963. {
  964. fprintf (stderr,
  965. "Try '%s " OPTION_LONG_S "help' for more information.\n",
  966. argv[0]);
  967. exit (1);
  968. }
  969. if (x < UCON64_MAX_ARGS)
  970. {
  971. const st_ucon64_obj_t *o =
  972. (st_ucon64_obj_t *) getopt2_get_index_by_val (options, c)->object;
  973. arg[x].console = UCON64_UNKNOWN; // default
  974. if (o)
  975. {
  976. arg[x].flags = o->flags;
  977. if (o->console)
  978. arg[x].console = o->console;
  979. }
  980. arg[x].val = c;
  981. arg[x++].optarg = optarg ? optarg : NULL;
  982. }
  983. else
  984. // this shouldn't happen
  985. exit (1);
  986. }
  987. #ifdef DEBUG
  988. for (x = 0; arg[x].val; x++)
  989. printf ("%d %s %d %d\n\n",
  990. arg[x].val,
  991. arg[x].optarg ? arg[x].optarg : "(null)",
  992. arg[x].flags,
  993. arg[x].console);
  994. #endif
  995. // switches
  996. for (x = 0; arg[x].val; x++)
  997. {
  998. if (arg[x].console != UCON64_UNKNOWN)
  999. ucon64.org_console = ucon64.console = arg[x].console;
  1000. if (arg[x].flags)
  1001. ucon64.flags = arg[x].flags;
  1002. if (arg[x].val)
  1003. ucon64.option = arg[x].val;
  1004. ucon64.optarg = arg[x].optarg;
  1005. // if (ucon64.flags & WF_SWITCH)
  1006. ucon64_switches (&ucon64);
  1007. }
  1008. #ifdef USE_ANSI_COLOR
  1009. if (ucon64.ansi_color)
  1010. ucon64.ansi_color = ansi_init ();
  1011. #endif
  1012. /*
  1013. Call ucon64_dat_indexer() after handling the switches and after calling
  1014. ansi_init() so that the progress bar is displayed correctly (colour/no
  1015. colour).
  1016. */
  1017. if (ucon64.dat_enabled)
  1018. ucon64_dat_indexer (); // update cache (index) files if necessary
  1019. #ifdef USE_PARALLEL
  1020. /*
  1021. The copier options need root privileges for parport_open(). We can't use
  1022. ucon64.flags & WF_PAR to detect whether a (parallel port) copier option has
  1023. been specified, because another switch might've been specified after -port.
  1024. */
  1025. if (ucon64.parport_needed == 1)
  1026. {
  1027. ucon64.parport = parport_open (ucon64.parport);
  1028. if (register_func (parport_close) == -1)
  1029. {
  1030. fputs ("ERROR: Could not register function with register_func()\n", stderr);
  1031. exit (1);
  1032. }
  1033. }
  1034. #endif
  1035. #if defined __unix__ && !defined __MSDOS__
  1036. /*
  1037. We can drop privileges after we have set up parallel port access. We cannot
  1038. drop privileges if the user wants to communicate with the USB version of the
  1039. F2A.
  1040. SECURITY WARNING: We stay in root mode if the user specified an F2A option!
  1041. We could of course drop privileges which requires the user to run uCON64 as
  1042. root (not setuid root), but we want to be user friendly. Besides, doing
  1043. things as root is bad anyway (from a security viewpoint).
  1044. */
  1045. if (ucon64.parport_needed != 2
  1046. #ifdef USE_USB
  1047. && !ucon64.usbport
  1048. #endif
  1049. )
  1050. drop_privileges ();
  1051. #endif // __unix__ && !__MSDOS__
  1052. if (optind == argc) // no file was specified (e.g. --db)
  1053. ucon64_execute_options();
  1054. else
  1055. {
  1056. int flags = GETOPT2_FILE_FILES_ONLY;
  1057. if (ucon64.recursive)
  1058. flags |= GETOPT2_FILE_RECURSIVE;
  1059. else
  1060. {
  1061. /*
  1062. Check if one of the parameters is a directory and if so, set the
  1063. flag GETOPT2_FILE_RECURSIVE_ONCE. This flag makes uCON64 behave
  1064. like version 2.0.0, i.e., specifying a directory is equivalent to
  1065. specifying all files in that directory. In commands:
  1066. ucon64 file dir1 dir2
  1067. is equivalent to:
  1068. ucon64 file dir1\* dir2\*
  1069. Once the flag is set it is not necessary to check the remaining
  1070. parameters.
  1071. TODO: Find a solution for the fact that the stat() implementation
  1072. of MinGW and VC++ don't accept a path with an ending slash.
  1073. */
  1074. int i = optind;
  1075. for (; i < argc; i++)
  1076. if (!stat (argv[i], &fstate) && S_ISDIR (fstate.st_mode))
  1077. {
  1078. flags |= GETOPT2_FILE_RECURSIVE_ONCE;
  1079. break;
  1080. }
  1081. }
  1082. getopt2_file (argc, argv, ucon64_process_rom, flags);
  1083. }
  1084. return 0;
  1085. }
  1086. static int
  1087. ucon64_process_rom (const char *fname)
  1088. {
  1089. #ifdef USE_ZLIB
  1090. int n_entries;
  1091. #endif
  1092. // Try to get file status information only if the file exists. We have to
  1093. // accept non-existing files for the dump options.
  1094. if (access (fname, F_OK) == 0)
  1095. {
  1096. struct stat fstate;
  1097. if (stat (fname, &fstate) == -1)
  1098. return 0;
  1099. if (!S_ISREG (fstate.st_mode))
  1100. return 0;
  1101. }
  1102. #ifdef USE_ZLIB
  1103. n_entries = unzip_get_number_entries (fname);
  1104. if (n_entries != -1) // it's a zip file
  1105. {
  1106. for (unzip_current_file_nr = 0; unzip_current_file_nr < n_entries;
  1107. unzip_current_file_nr++)
  1108. {
  1109. ucon64_fname_arch (fname);
  1110. /*
  1111. There seems to be no other way to detect directories in ZIP files
  1112. than by looking at the file name. Paths in ZIP files should contain
  1113. forward slashes. ucon64_fname_arch() changes forward slashes into
  1114. backslashes (DIR_SEPARATORs) when uCON64 is compiled with Visual
  1115. C++ or MinGW so that basename2() always produces a correct base
  1116. name. So, if the entry in the ZIP file is a directory
  1117. ucon64.fname_arch will be an empty string.
  1118. */
  1119. if (ucon64.fname_arch[0] == 0)
  1120. continue;
  1121. ucon64.fname = fname;
  1122. ucon64_execute_options();
  1123. if (ucon64.flags & WF_STOP)
  1124. break;
  1125. }
  1126. unzip_current_file_nr = 0;
  1127. ucon64.fname_arch[0] = 0;
  1128. if (ucon64.flags & WF_STOP)
  1129. return 1;
  1130. }
  1131. else
  1132. #endif
  1133. {
  1134. ucon64.fname = fname;
  1135. ucon64_execute_options();
  1136. if (ucon64.flags & WF_STOP)
  1137. return 1;
  1138. }
  1139. return 0;
  1140. }
  1141. static int
  1142. ucon64_execute_options (void)
  1143. /*
  1144. Execute all options for a single file.
  1145. Please, if you experience problems then try your luck with the flags in
  1146. ucon64_misc.c/ucon64_wf[] before changing things here or in
  1147. ucon64_rom_handling().
  1148. */
  1149. {
  1150. int c = 0, result = 0, x = 0, opts = 0;
  1151. // these members of ucon64 can change per file
  1152. ucon64.dat = NULL;
  1153. #ifdef USE_DISCMAGE
  1154. ucon64.image = NULL;
  1155. #endif
  1156. ucon64.nfo = NULL;
  1157. ucon64.split = UCON64_UNKNOWN;
  1158. ucon64.file_size =
  1159. ucon64.crc32 =
  1160. ucon64.fcrc32 = 0;
  1161. ucon64.console = ucon64.org_console;
  1162. for (x = 0; arg[x].val; x++)
  1163. if (!(arg[x].flags & WF_SWITCH))
  1164. {
  1165. if (ucon64.console == UCON64_UNKNOWN)
  1166. ucon64.console = arg[x].console;
  1167. ucon64.flags = arg[x].flags;
  1168. ucon64.option = arg[x].val;
  1169. ucon64.optarg = arg[x].optarg;
  1170. opts++;
  1171. // WF_NO_SPLIT, WF_INIT, WF_PROBE, CRC32, DATabase and WF_NFO
  1172. result = ucon64_rom_handling ();
  1173. if (result == -1) // no rom, but WF_NO_ROM
  1174. return -1;
  1175. if (ucon64_options (&ucon64) == -1)
  1176. {
  1177. const st_getopt2_t *p = getopt2_get_index_by_val (options, c);
  1178. const char *opt = p ? p->name : NULL;
  1179. fprintf (stderr, "ERROR: %s%s encountered a problem\n",
  1180. opt ? (!opt[1] ? OPTION_S : OPTION_LONG_S) : "",
  1181. opt ? opt : "uCON64");
  1182. // if (p)
  1183. // getopt2_usage (p);
  1184. fputs (" Is the option you used available for the current console system?\n"
  1185. " Please report bugs to ucon64-main@lists.sf.net or http://ucon64.sf.net\n\n",
  1186. stderr);
  1187. return -1;
  1188. }
  1189. /*
  1190. "stop" options:
  1191. - -multi (and -xfalmulti) takes more than one file as argument, but
  1192. should be executed only once.
  1193. - stop after sending one ROM to a copier ("multizip")
  1194. - stop after applying a patch so that the patch file won't be
  1195. interpreted as ROM
  1196. */
  1197. if (ucon64.flags & WF_STOP)
  1198. break;
  1199. }
  1200. if (!opts) // no options => just display ROM info
  1201. {
  1202. ucon64.flags = WF_DEFAULT;
  1203. // WF_NO_SPLIT WF_INIT, WF_PROBE, CRC32, DATabase and WF_NFO
  1204. if (ucon64_rom_handling () == -1)
  1205. return -1; // no rom, but WF_NO_ROM
  1206. }
  1207. fflush (stdout);
  1208. return 0;
  1209. }
  1210. static int
  1211. ucon64_rom_handling (void)
  1212. {
  1213. int no_rom = 0;
  1214. static st_ucon64_nfo_t nfo;
  1215. struct stat fstate;
  1216. ucon64_clear_nfo (&nfo);
  1217. // a ROM (file)?
  1218. if (!ucon64.fname)
  1219. no_rom = 1;
  1220. else if (!ucon64.fname[0])
  1221. no_rom = 1;
  1222. else if (access (ucon64.fname, F_OK | R_OK) == -1 && (!(ucon64.flags & WF_NO_ROM)))
  1223. {
  1224. fprintf (stderr, "ERROR: Could not open %s\n", ucon64.fname);
  1225. no_rom = 1;
  1226. }
  1227. else if (stat (ucon64.fname, &fstate) == -1)
  1228. no_rom = 1;
  1229. else if (S_ISREG (fstate.st_mode) != TRUE)
  1230. no_rom = 1;
  1231. #if 0
  1232. // printing the no_rom error message for files of 0 bytes only confuses people
  1233. else if (!fstate.st_size)
  1234. no_rom = 1;
  1235. #endif
  1236. if (no_rom)
  1237. {
  1238. if (!(ucon64.flags & WF_NO_ROM))
  1239. {
  1240. fputs ("ERROR: This option requires a file argument (ROM/image/SRAM file/directory)\n",
  1241. stderr);
  1242. return -1;
  1243. }
  1244. return 0;
  1245. }
  1246. // The next statement is important and should be executed as soon as
  1247. // possible (and sensible) in this function
  1248. if ((ucon64.file_size = fsizeof (ucon64.fname)) < 0)
  1249. {
  1250. fprintf (stderr, "ERROR: Could not determine size of %s\n", ucon64.fname);
  1251. return -1;
  1252. }
  1253. // We have to do this here, because we don't know the file size until now
  1254. if (ucon64.backup_header_len > ucon64.file_size)
  1255. {
  1256. fprintf (stderr,
  1257. "ERROR: A backup unit header length was specified that is larger than the file\n"
  1258. " size (%d > %d)\n", ucon64.backup_header_len, ucon64.file_size);
  1259. return -1;
  1260. }
  1261. if (!(ucon64.flags & WF_INIT))
  1262. return 0;
  1263. // Try to find the correct console by analysing the ROM
  1264. if (ucon64.flags & WF_PROBE)
  1265. {
  1266. if (ucon64.nfo)
  1267. {
  1268. // Restore any overrides from st_ucon64_t
  1269. // We have to do this *before* calling ucon64_probe(), *not* afterwards
  1270. if (UCON64_ISSET (ucon64.backup_header_len))
  1271. nfo.backup_header_len = ucon64.backup_header_len;
  1272. if (UCON64_ISSET (ucon64.interleaved))
  1273. nfo.interleaved = ucon64.interleaved;
  1274. // ucon64.nfo = (st_ucon64_nfo_t *) &nfo;
  1275. }
  1276. ucon64.nfo = ucon64_probe (&nfo); // determines console type
  1277. #ifdef USE_DISCMAGE
  1278. // check for disc image only if ucon64_probe() failed or --disc was used
  1279. if (ucon64.discmage_enabled)
  1280. // if (!ucon64.nfo || ucon64.force_disc)
  1281. if (ucon64.force_disc)
  1282. ucon64.image = dm_reopen (ucon64.fname, 0, (dm_image_t *) ucon64.image);
  1283. #endif
  1284. }
  1285. // end of WF_PROBE
  1286. // Does the option allow split ROMs?
  1287. if (ucon64.flags & WF_NO_SPLIT)
  1288. /*
  1289. Test for split files only if the console type knows about split files at
  1290. all. However we only know the console type after probing.
  1291. */
  1292. if ((ucon64.console == UCON64_NES || ucon64.console == UCON64_SNES ||
  1293. ucon64.console == UCON64_GEN || ucon64.console == UCON64_NG) &&
  1294. (UCON64_ISSET (ucon64.split) ? ucon64.split : ucon64_testsplit (ucon64.fname, NULL)))
  1295. {
  1296. fprintf (stderr, "ERROR: %s seems to be split. You have to join it first\n",
  1297. basename2 (ucon64.fname));
  1298. return -1;
  1299. }
  1300. /*
  1301. CRC32
  1302. Calculating the CRC32 checksum for the ROM data of a UNIF file (NES)
  1303. shouldn't be done with ucon64_fcrc32(). nes_init() uses crc32().
  1304. The CRC32 checksum is used to search in the DAT files, but at the time
  1305. of this writing (Februari the 7th 2003) all DAT files contain checksums
  1306. of files in only one format. This matters for SNES and Genesis ROMs in
  1307. interleaved format and Nintendo 64 ROMs in non-interleaved format. The
  1308. corresponding initialization functions calculate the CRC32 checksum of
  1309. the data in the format of which the checksum is stored in the DAT
  1310. files. For these "problematic" files, their "real" checksum is stored
  1311. in ucon64.fcrc32.
  1312. */
  1313. if (ucon64.crc32 == 0 && !ucon64.force_disc && // NOT for disc images
  1314. !(ucon64.flags & WF_NO_CRC32) && ucon64.file_size <= MAXROMSIZE)
  1315. ucon64_chksum (NULL, NULL, &ucon64.crc32, ucon64.fname, ucon64.file_size,
  1316. ucon64.nfo ? ucon64.nfo->backup_header_len : 0);
  1317. // DATabase
  1318. ucon64.dat = NULL;
  1319. if (ucon64.crc32 != 0 && ucon64.dat_enabled)
  1320. {
  1321. ucon64.dat = ucon64_dat_search (ucon64.crc32, NULL);
  1322. if (ucon64.dat)
  1323. {
  1324. // detected file size must match DAT file size
  1325. int size = ucon64.nfo ?
  1326. UCON64_ISSET (ucon64.nfo->data_size) ?
  1327. ucon64.nfo->data_size :
  1328. ucon64.file_size - ucon64.nfo->backup_header_len :
  1329. ucon64.file_size;
  1330. if ((int) (((st_ucon64_dat_t *) ucon64.dat)->fsize) != size)
  1331. ucon64.dat = NULL;
  1332. }
  1333. if (ucon64.dat)
  1334. switch (ucon64.console)
  1335. {
  1336. case UCON64_SNES:
  1337. case UCON64_GEN:
  1338. case UCON64_GB:
  1339. case UCON64_GBA:
  1340. case UCON64_N64:
  1341. // These ROMs have internal headers with name, country, maker, etc.
  1342. break;
  1343. default:
  1344. // Use ucon64.dat instead of ucon64.dat_enabled in case the index
  1345. // file could not be created/opened -> no segmentation fault
  1346. if (ucon64.dat && ucon64.nfo)
  1347. {
  1348. if (!ucon64.nfo->name[0])
  1349. strcpy (ucon64.nfo->name, NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->name));
  1350. else if (ucon64.console == UCON64_NES)
  1351. { // override the three-character FDS or FAM name
  1352. nes_file_t type = nes_get_file_type ();
  1353. if (type == FDS || type == FAM)
  1354. strcpy (ucon64.nfo->name, NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->name));
  1355. }
  1356. if (!ucon64.nfo->country)
  1357. ucon64.nfo->country = NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->country);
  1358. }
  1359. break;
  1360. }
  1361. }
  1362. // display info
  1363. if ((ucon64.flags & WF_NFO) && ucon64.quiet < 1)
  1364. ucon64_nfo ();
  1365. return 0;
  1366. }
  1367. static st_ucon64_nfo_t *
  1368. ucon64_probe (st_ucon64_nfo_t *nfo)
  1369. {
  1370. typedef struct
  1371. {
  1372. int console;
  1373. int (*init) (st_ucon64_nfo_t *);
  1374. uint32_t flags;
  1375. } st_probe_t;
  1376. // auto recognition
  1377. #define AUTO 1
  1378. int x = 0;
  1379. st_probe_t probe[] =
  1380. {
  1381. /*
  1382. The order of the init functions is important. snes_init() must be
  1383. called before nes_init(), but after gb_init() and sms_init().
  1384. sms_init() must be called before snes_init(), but after genesis_init().
  1385. There may be more dependencies, so don't change the order unless you
  1386. can verify it won't break anything.
  1387. */
  1388. {UCON64_GBA, gba_init, AUTO},
  1389. {UCON64_N64, n64_init, AUTO},
  1390. {UCON64_GEN, genesis_init, AUTO},
  1391. {UCON64_LYNX, lynx_init, AUTO},
  1392. {UCON64_GB, gb_init, AUTO},
  1393. {UCON64_SMS, sms_init, AUTO},
  1394. {UCON64_COLECO, coleco_init, AUTO},
  1395. {UCON64_SNES, snes_init, AUTO},
  1396. {UCON64_NES, nes_init, AUTO},
  1397. {UCON64_NGP, ngp_init, AUTO},
  1398. {UCON64_SWAN, swan_init, AUTO},
  1399. {UCON64_JAG, jaguar_init, AUTO},
  1400. {UCON64_ATA, atari_init, AUTO},
  1401. {UCON64_NDS, nds_init, AUTO},
  1402. {UCON64_VBOY, vboy_init, 0},
  1403. {UCON64_PCE, pce_init, 0}, // AUTO still works with non-PCE files
  1404. {UCON64_NG, neogeo_init, 0},
  1405. {UCON64_SWAN, swan_init, 0},
  1406. {UCON64_DC, dc_init, 0},
  1407. {UCON64_PSX, psx_init, 0},
  1408. #if 0
  1409. {UCON64_GC, NULL, 0},
  1410. {UCON64_GP32, NULL, 0},
  1411. {UCON64_INTELLI, NULL, 0},
  1412. {UCON64_S16, NULL, 0},
  1413. {UCON64_VEC, NULL, 0},
  1414. #endif
  1415. {UCON64_UNKNOWN, unknown_console_init, 0},
  1416. {0, NULL, 0}
  1417. };
  1418. if (ucon64.console != UCON64_UNKNOWN) // force recognition option was used
  1419. {
  1420. for (x = 0; probe[x].console != 0; x++)
  1421. if (probe[x].console == ucon64.console)
  1422. {
  1423. ucon64_clear_nfo (nfo);
  1424. probe[x].init (nfo);
  1425. return nfo;
  1426. }
  1427. }
  1428. else if (ucon64.file_size <= MAXROMSIZE) // give auto recognition a try
  1429. {
  1430. for (x = 0; probe[x].console != 0; x++)
  1431. if (probe[x].flags & AUTO)
  1432. {
  1433. ucon64_clear_nfo (nfo);
  1434. if (!probe[x].init (nfo))
  1435. {
  1436. ucon64.console = probe[x].console;
  1437. return nfo;
  1438. }
  1439. }
  1440. }
  1441. return NULL;
  1442. }
  1443. int
  1444. ucon64_nfo (void)
  1445. {
  1446. puts (ucon64.fname);
  1447. if (ucon64.fname_arch[0])
  1448. printf (" (%s)\n", ucon64.fname_arch);
  1449. fputc ('\n', stdout);
  1450. #ifdef USE_DISCMAGE
  1451. if (ucon64.console == UCON64_UNKNOWN && !ucon64.image)
  1452. #else
  1453. if (ucon64.console == UCON64_UNKNOWN)
  1454. #endif
  1455. fprintf (stderr, "%s\n", ucon64_msg[CONSOLE_ERROR]);
  1456. if (ucon64.nfo && ucon64.console != UCON64_UNKNOWN && !ucon64.force_disc)
  1457. ucon64_rom_nfo (ucon64.nfo);
  1458. #ifdef USE_DISCMAGE
  1459. if (ucon64.discmage_enabled && ucon64.image)
  1460. {
  1461. dm_nfo ((dm_image_t *) ucon64.image, ucon64.quiet < 0 ? 1 : 0,
  1462. #ifdef USE_ANSI_COLOR
  1463. ucon64.ansi_color ? 1 :
  1464. #endif
  1465. 0);
  1466. fputc ('\n', stdout);
  1467. return 0; // no crc calc. for disc images and therefore no DAT entry either
  1468. }
  1469. #endif
  1470. // Use ucon64.fcrc32 for SNES, Genesis & SMS interleaved/N64 non-interleaved
  1471. if (ucon64.fcrc32 && ucon64.crc32)
  1472. printf ("Search checksum (CRC32): 0x%08x\n"
  1473. "Data checksum (CRC32): 0x%08x\n", ucon64.crc32, ucon64.fcrc32);
  1474. else if (ucon64.fcrc32 || ucon64.crc32)
  1475. printf ("Checksum (CRC32): 0x%08x\n", ucon64.fcrc32 ? ucon64.fcrc32 : ucon64.crc32);
  1476. // The check for the size of the file is made, so that uCON64 won't display a
  1477. // (nonsense) DAT info line when dumping a ROM (file doesn't exist, so
  1478. // ucon64.file_size is 0).
  1479. if (ucon64.file_size > 0 && ucon64.dat_enabled && ucon64.dat)
  1480. ucon64_dat_nfo ((st_ucon64_dat_t *) ucon64.dat, 1);
  1481. fputc ('\n', stdout);
  1482. return 0;
  1483. }
  1484. static inline char *
  1485. to_func (char *s, int len, char (*func) (char))
  1486. {
  1487. char *p = s;
  1488. for (; len > 0; p++, len--)
  1489. *p = func (*p);
  1490. return s;
  1491. }
  1492. static inline char
  1493. toprint (char c)
  1494. {
  1495. if (isprint ((int) c))
  1496. return c;
  1497. // characters that also work with printf()
  1498. #ifdef USE_ANSI_COLOR
  1499. if (c == '\x1b')
  1500. return ucon64.ansi_color ? c : '.';
  1501. #endif
  1502. return strchr ("\t\n\r", c) ? c : '.';
  1503. }
  1504. static void
  1505. ucon64_rom_nfo (const st_ucon64_nfo_t *nfo)
  1506. {
  1507. unsigned int padded = ucon64_testpad (ucon64.fname),
  1508. intro = ((ucon64.file_size - nfo->backup_header_len) > MBIT) ?
  1509. ((ucon64.file_size - nfo->backup_header_len) % MBIT) : 0;
  1510. int x, split = (UCON64_ISSET (ucon64.split)) ? ucon64.split :
  1511. ucon64_testsplit (ucon64.fname, NULL);
  1512. char buf[MAXBUFSIZE];
  1513. // backup unit header
  1514. if (nfo->backup_header && nfo->backup_header_len &&
  1515. nfo->backup_header_len != UNKNOWN_BACKUP_HEADER_LEN)
  1516. {
  1517. dumper (stdout, nfo->backup_header, nfo->backup_header_len,
  1518. nfo->backup_header_start, DUMPER_HEX);
  1519. fputc ('\n', stdout);
  1520. }
  1521. else
  1522. if (nfo->backup_header_len && ucon64.quiet < 0)
  1523. {
  1524. ucon64_dump (stdout, ucon64.fname, nfo->backup_header_start,
  1525. nfo->backup_header_len, DUMPER_HEX);
  1526. fputc ('\n', stdout);
  1527. }
  1528. // backup unit type?
  1529. if (nfo->backup_usage != NULL)
  1530. {
  1531. puts (nfo->backup_usage);
  1532. fputc ('\n', stdout);
  1533. }
  1534. // ROM header
  1535. if (nfo->header && nfo->header_len)
  1536. {
  1537. dumper (stdout, nfo->header, nfo->header_len,
  1538. nfo->header_start + nfo->backup_header_len, DUMPER_HEX);
  1539. fputc ('\n', stdout);
  1540. }
  1541. // console type
  1542. if (nfo->console_usage != NULL)
  1543. puts (nfo->console_usage);
  1544. // name, maker, country and size
  1545. strcpy (buf, NULL_TO_EMPTY (nfo->name));
  1546. x = UCON64_ISSET (nfo->data_size) ?
  1547. nfo->data_size :
  1548. ucon64.file_size - nfo->backup_header_len;
  1549. printf ("%s\n%s\n%s\n%d Bytes (%.4f Mb)\n\n",
  1550. // some ROMs have a name with control chars in it -> replace control chars
  1551. to_func (buf, strlen (buf), toprint),
  1552. NULL_TO_EMPTY (nfo->maker),
  1553. NULL_TO_EMPTY (nfo->country),
  1554. x,
  1555. TOMBIT_F (x));
  1556. // padded?
  1557. if (!padded)
  1558. puts ("Padded: No");
  1559. else
  1560. printf ("Padded: Maybe, %d Bytes (%.4f Mb)\n", padded, TOMBIT_F (padded));
  1561. // intro, trainer?
  1562. // nes.c determines itself whether or not there is a trainer
  1563. if (intro && ucon64.console != UCON64_NES)
  1564. printf ("Intro/Trainer: Maybe, %d Bytes\n", intro);
  1565. // interleaved?
  1566. if (nfo->interleaved != UCON64_UNKNOWN)
  1567. // printing this is handy for SNES, N64 & Genesis ROMs, but maybe
  1568. // nonsense for others
  1569. printf ("Interleaved/Swapped: %s\n",
  1570. nfo->interleaved ?
  1571. (nfo->interleaved > 1 ? "Yes (2)" : "Yes") :
  1572. "No");
  1573. // backup unit header?
  1574. if (nfo->backup_header_len)
  1575. printf ("Backup unit/emulator header: Yes, %d Bytes\n",
  1576. nfo->backup_header_len);
  1577. else
  1578. // for NoisyB: <read only mode ON>
  1579. puts ("Backup unit/emulator header: No"); // printing No is handy for SNES ROMs
  1580. // for NoisyB: <read only mode OFF>
  1581. // split?
  1582. if (split)
  1583. {
  1584. printf ("Split: Yes, %d part%s\n", split, (split != 1) ? "s" : "");
  1585. // nes.c calculates the correct checksum for split ROMs (=Pasofami
  1586. // format), so there is no need to join the files
  1587. if (ucon64.console != UCON64_NES)
  1588. puts ("NOTE: To get the correct checksum the ROM parts must be joined");
  1589. }
  1590. // miscellaneous info
  1591. if (nfo->misc[0])
  1592. {
  1593. strcpy (buf, nfo->misc);
  1594. printf ("%s\n", to_func (buf, strlen (buf), toprint));
  1595. }
  1596. // internal checksums?
  1597. if (nfo->has_internal_crc)
  1598. {
  1599. char *fstr;
  1600. // the internal checksum of GBA ROMS stores only the checksum of the
  1601. // internal header
  1602. if (ucon64.console != UCON64_GBA)
  1603. fstr = "Checksum: %%s, 0x%%0%dlx (calculated) %%c= 0x%%0%dlx (internal)\n";
  1604. else
  1605. fstr = "Header checksum: %%s, 0x%%0%dlx (calculated) %%c= 0x%%0%dlx (internal)\n";
  1606. sprintf (buf, fstr,
  1607. nfo->internal_crc_len * 2, nfo->internal_crc_len * 2);
  1608. #ifdef USE_ANSI_COLOR
  1609. printf (buf,
  1610. ucon64.ansi_color ?
  1611. ((nfo->current_internal_crc == nfo->internal_crc) ?
  1612. "\x1b[01;32mOK\x1b[0m" : "\x1b[01;31mBad\x1b[0m")
  1613. :
  1614. ((nfo->current_internal_crc == nfo->internal_crc) ? "OK" : "Bad"),
  1615. nfo->current_internal_crc,
  1616. (nfo->current_internal_crc == nfo->internal_crc) ? '=' : '!',
  1617. nfo->internal_crc);
  1618. #else
  1619. printf (buf,
  1620. (nfo->current_internal_crc == nfo->internal_crc) ? "OK" : "Bad",
  1621. nfo->current_internal_crc,
  1622. (nfo->current_internal_crc == nfo->internal_crc) ? '=' : '!',
  1623. nfo->internal_crc);
  1624. #endif
  1625. if (nfo->internal_crc2[0])
  1626. printf ("%s\n", nfo->internal_crc2);
  1627. }
  1628. fflush (stdout);
  1629. }
  1630. #ifdef USE_ZLIB
  1631. void
  1632. ucon64_fname_arch (const char *fname)
  1633. {
  1634. char name[FILENAME_MAX];
  1635. unzFile file = unzOpen (fname);
  1636. unzip_goto_file (file, unzip_current_file_nr);
  1637. unzGetCurrentFileInfo (file, NULL, name, FILENAME_MAX, NULL, 0, NULL, 0);
  1638. unzClose (file);
  1639. #if defined _WIN32 || defined __MSDOS__
  1640. {
  1641. int n, l = strlen (name);
  1642. for (n = 0; n < l; n++)
  1643. if (name[n] == '/')
  1644. name[n] = DIR_SEPARATOR;
  1645. }
  1646. #endif
  1647. strncpy (ucon64.fname_arch, basename2 (name), FILENAME_MAX)[FILENAME_MAX - 1] = 0;
  1648. }
  1649. #endif
  1650. void
  1651. ucon64_usage (int argc, char *argv[], int view)
  1652. {
  1653. int x = 0, y = 0, c = 0, single = 0;
  1654. const char *name_exe = basename2 (argv[0]);
  1655. #ifdef USE_DISCMAGE
  1656. char *name_discmage;
  1657. #endif
  1658. (void) argc; // warning remover
  1659. #ifdef USE_ZLIB
  1660. printf ("Usage: %s [OPTION]... [ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE]...\n\n", name_exe);
  1661. #else
  1662. printf ("Usage: %s [OPTION]... [ROM|IMAGE|SRAM|FILE|DIR]...\n\n", name_exe);
  1663. #endif
  1664. // single usage
  1665. for (x = 0; arg[x].val; x++)
  1666. if (arg[x].console) // IS console
  1667. for (y = 0; option[y]; y++)
  1668. for (c = 0; option[y][c].name || option[y][c].help; c++)
  1669. if (option[y][c].object &&
  1670. ((st_ucon64_obj_t *) option[y][c].object)->console == arg[x].console)
  1671. {
  1672. getopt2_usage (option[y]);
  1673. single = 1;
  1674. break;
  1675. }
  1676. if (!single)
  1677. switch (view)
  1678. {
  1679. case USAGE_VIEW_LONG:
  1680. getopt2_usage (options);
  1681. break;
  1682. case USAGE_VIEW_PAD:
  1683. getopt2_usage (ucon64_padding_usage);
  1684. break;
  1685. case USAGE_VIEW_DAT:
  1686. getopt2_usage (ucon64_dat_usage);
  1687. break;
  1688. case USAGE_VIEW_PATCH:
  1689. getopt2_usage (patch_usage);
  1690. getopt2_usage (bsl_usage);
  1691. getopt2_usage (ips_usage);
  1692. getopt2_usage (aps_usage);
  1693. getopt2_usage (ppf_usage);
  1694. getopt2_usage (gg_usage);
  1695. break;
  1696. case USAGE_VIEW_BACKUP:
  1697. // getopt2_usage (cc2_usage);
  1698. #ifdef USE_LIBCD64
  1699. getopt2_usage (cd64_usage);
  1700. #endif
  1701. getopt2_usage (cmc_usage);
  1702. getopt2_usage (dex_usage);
  1703. getopt2_usage (doctor64_usage);
  1704. getopt2_usage (doctor64jr_usage);
  1705. getopt2_usage (f2a_usage);
  1706. getopt2_usage (fal_usage);
  1707. getopt2_usage (fig_usage);
  1708. getopt2_usage (gbx_usage);
  1709. getopt2_usage (gd_usage);
  1710. // getopt2_usage (interceptor_usage);
  1711. getopt2_usage (lynxit_usage);
  1712. getopt2_usage (mccl_usage);
  1713. getopt2_usage (mcd_usage);
  1714. getopt2_usage (mdpro_usage);
  1715. // getopt2_usage (mgd_usage);
  1716. getopt2_usage (msg_usage);
  1717. // getopt2_usage (nfc_usage);
  1718. getopt2_usage (pcepro_usage);
  1719. getopt2_usage (pl_usage);
  1720. getopt2_usage (quickdev16_usage);
  1721. // getopt2_usage (sc_usage);
  1722. getopt2_usage (sflash_usage);
  1723. getopt2_usage (smc_usage);
  1724. getopt2_usage (smd_usage);
  1725. getopt2_usage (smsggpro_usage);
  1726. // getopt2_usage (spsc_usage);
  1727. // getopt2_usage (ssc_usage);
  1728. getopt2_usage (swc_usage);
  1729. // getopt2_usage (ufo_usage);
  1730. // getopt2_usage (yoko_usage);
  1731. // getopt2_usage (z64_usage);
  1732. break;
  1733. #ifdef USE_DISCMAGE
  1734. case USAGE_VIEW_DISC:
  1735. getopt2_usage (discmage_usage);
  1736. break;
  1737. #endif
  1738. case USAGE_VIEW_SHORT:
  1739. default:
  1740. getopt2_usage (ucon64_options_usage);
  1741. }
  1742. fputc ('\n', stdout);
  1743. printf ("DATabase: %d known ROMs (DAT files: %s)\n\n",
  1744. ucon64_dat_total_entries (), ucon64.datdir);
  1745. #ifdef USE_DISCMAGE
  1746. name_discmage =
  1747. #ifdef DLOPEN
  1748. ucon64.discmage_path;
  1749. #else
  1750. #if defined __MSDOS__
  1751. "discmage.dxe";
  1752. #elif defined __CYGWIN__ || defined _WIN32
  1753. "discmage.dll";
  1754. #elif defined __APPLE__ // Mac OS X actually
  1755. "libdiscmage.dylib";
  1756. #elif defined __unix__ || defined __BEOS__
  1757. "libdiscmage.so";
  1758. #else
  1759. "library";
  1760. #endif
  1761. #endif
  1762. if (!ucon64.discmage_enabled)
  1763. {
  1764. printf (ucon64_msg[NO_LIB], name_discmage);
  1765. fputc ('\n', stdout);
  1766. }
  1767. #endif
  1768. puts ("Please report problems, fixes or ideas to ucon64-main@lists.sf.net or visit\n"
  1769. "http://ucon64.sourceforge.net\n");
  1770. }