mgd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. mgd.c - Multi Game Doctor/Hunter support for uCON64
  3. Copyright (c) 1999 - 2001 NoisyB <noisyb@gmx.net>
  4. Copyright (c) 2001 - 2004 dbjh
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include "config.h"
  19. #endif
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include "misc/misc.h"
  25. #include "misc/file.h"
  26. #ifdef USE_ZLIB
  27. #include "misc/archive.h"
  28. #endif
  29. #include "misc/getopt2.h" // st_getopt2_t
  30. #include "ucon64.h"
  31. #include "ucon64_misc.h"
  32. #include "misc/string.h"
  33. #include "mgd.h"
  34. const st_getopt2_t mgd_usage[] =
  35. {
  36. {
  37. NULL, 0, 0, 0,
  38. NULL, "Multi Game Doctor (2)/Multi Game Hunter/MGH"
  39. /*"19XX Bung Enterprises Ltd http://www.bung.com.hk\n" "?Makko Toys Co., Ltd.?"*/,
  40. NULL
  41. },
  42. #if 0
  43. {
  44. "xmgd", 0, 0, UCON64_XMGD,
  45. NULL, "(TODO) send/receive ROM to/from Multi Game* /MGD2/MGH; " OPTION_LONG_S "port=PORT\n"
  46. "receives automatically when " OPTION_LONG_S "rom does not exist",
  47. NULL
  48. },
  49. #endif
  50. {NULL, 0, 0, 0, NULL, NULL, NULL}
  51. };
  52. // the following four functions are used by non-transfer code in genesis.c
  53. void
  54. mgd_interleave (unsigned char **buffer, int size)
  55. {
  56. int n;
  57. unsigned char *src = *buffer;
  58. if (!(*buffer = (unsigned char *) malloc (size)))
  59. {
  60. fprintf (stderr, ucon64_msg[BUFFER_ERROR], size);
  61. exit (1);
  62. }
  63. for (n = 0; n < size / 2; n++)
  64. {
  65. (*buffer)[n] = src[n * 2 + 1];
  66. (*buffer)[size / 2 + n] = src[n * 2];
  67. }
  68. free (src);
  69. }
  70. void
  71. mgd_deinterleave (unsigned char **buffer, int data_size, int buffer_size)
  72. {
  73. int n = 0, offset;
  74. unsigned char *src = *buffer;
  75. if (!(*buffer = (unsigned char *) malloc (buffer_size)))
  76. {
  77. fprintf (stderr, ucon64_msg[BUFFER_ERROR], buffer_size);
  78. exit (1);
  79. }
  80. for (offset = 0; offset < data_size / 2; offset++)
  81. {
  82. (*buffer)[n++] = src[data_size / 2 + offset];
  83. (*buffer)[n++] = src[offset];
  84. }
  85. free (src);
  86. }
  87. int
  88. fread_mgd (void *buffer, size_t size, size_t number, FILE *fh)
  89. /*
  90. This function is used to handle a Genesis MGD file as if it wasn't
  91. interleaved, without the overhead of reading the entire file into memory.
  92. This is important for genesis_init(). When the file turns out to be a Genesis
  93. dump in MGD format it is much more efficient for compressed files to read the
  94. entire file into memory and then deinterleave it (as load_rom() does).
  95. In order to speed this function up a bit ucon64.file_size is used. That means
  96. it can't be used for an arbitrary file.
  97. */
  98. {
  99. int n = 0, bpos = 0, fpos, fpos_org, block_size, bytesread = 0,
  100. len = number * size, fsize = ucon64.file_size /* fsizeof (filename) */;
  101. unsigned char tmp1[MAXBUFSIZE], tmp2[MAXBUFSIZE];
  102. fpos = fpos_org = ftell (fh);
  103. if (fpos >= fsize)
  104. return 0;
  105. if (len == 0)
  106. return 0;
  107. else if (len == 1)
  108. {
  109. if (fpos_org & 1)
  110. {
  111. fseek (fh, fpos / 2, SEEK_SET);
  112. *((unsigned char *) buffer) = fgetc (fh);
  113. }
  114. else
  115. {
  116. fseek (fh, fpos / 2 + fsize / 2, SEEK_SET);
  117. *((unsigned char *) buffer) = fgetc (fh);
  118. }
  119. fseek (fh, fpos_org + 1, SEEK_SET);
  120. return 1;
  121. }
  122. while (len > 0 && !feof (fh))
  123. {
  124. block_size = len > MAXBUFSIZE ? MAXBUFSIZE : len;
  125. fseek (fh, fpos / 2, SEEK_SET);
  126. bytesread += fread (tmp1, 1, block_size / 2, fh); // read odd bytes
  127. fseek (fh, (fpos + 1) / 2 + fsize / 2, SEEK_SET);
  128. bytesread += fread (tmp2, 1, block_size / 2, fh); // read even bytes
  129. if (fpos_org & 1)
  130. for (n = 0; n < block_size / 2; n++)
  131. {
  132. ((unsigned char *) buffer)[bpos + n * 2] = tmp1[n];
  133. ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp2[n];
  134. }
  135. else
  136. for (n = 0; n < block_size / 2; n++)
  137. {
  138. ((unsigned char *) buffer)[bpos + n * 2] = tmp2[n];
  139. ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp1[n];
  140. }
  141. fpos += block_size;
  142. bpos += block_size;
  143. len -= block_size;
  144. }
  145. fseek (fh, fpos_org + bytesread, SEEK_SET);
  146. return bytesread / size;
  147. }
  148. int
  149. q_fread_mgd (void *buffer, size_t start, size_t len, const char *filename)
  150. {
  151. int result;
  152. FILE *fh;
  153. if ((fh = fopen (filename, "rb")) == NULL)
  154. return -1;
  155. fseek (fh, start, SEEK_SET);
  156. result = (int) fread_mgd (buffer, 1, len, fh);
  157. fclose (fh);
  158. return result;
  159. }
  160. static void
  161. remove_mgd_id (char *name, const char *id)
  162. {
  163. char *p = name;
  164. while ((p = strstr (p, id)))
  165. {
  166. *p = 'X';
  167. *(p + 1) = 'X';
  168. p += 2;
  169. }
  170. }
  171. void
  172. mgd_make_name (const char *filename, int console, int size, char *name)
  173. // these characters are also valid in MGD file names: !@#$%^&_
  174. {
  175. char *prefix = 0, *p, *size_str = 0, *suffix = 0;
  176. const char *fname;
  177. int n;
  178. switch (console)
  179. {
  180. default: // falling through
  181. case UCON64_SNES:
  182. prefix = "SF";
  183. suffix = ".048";
  184. if (size <= 1 * MBIT)
  185. size_str = "1";
  186. else if (size <= 2 * MBIT)
  187. size_str = "2";
  188. else if (size <= 4 * MBIT)
  189. size_str = "4";
  190. else if (size <= 8 * MBIT)
  191. {
  192. size_str = "8";
  193. suffix = ".058";
  194. }
  195. else
  196. {
  197. suffix = ".078";
  198. if (size <= 10 * MBIT)
  199. size_str = "10";
  200. else if (size <= 12 * MBIT)
  201. size_str = "12";
  202. else if (size <= 16 * MBIT)
  203. size_str = "16";
  204. else if (size <= 20 * MBIT)
  205. size_str = "20";
  206. else if (size <= 24 * MBIT)
  207. size_str = "24";
  208. else // MGD supports SNES games with sizes up to 32 Mbit
  209. size_str = "32";
  210. }
  211. break;
  212. case UCON64_GEN:
  213. prefix = "MD";
  214. suffix = ".000";
  215. if (size <= 1 * MBIT)
  216. size_str = "1";
  217. else if (size <= 2 * MBIT)
  218. size_str = "2";
  219. else if (size <= 4 * MBIT)
  220. size_str = "4";
  221. else
  222. {
  223. if (size <= 8 * MBIT)
  224. {
  225. size_str = "8";
  226. suffix = ".008";
  227. }
  228. else if (size <= 16 * MBIT)
  229. {
  230. size_str = "16";
  231. suffix = ".018";
  232. }
  233. else
  234. {
  235. suffix = ".038";
  236. if (size <= 20 * MBIT)
  237. size_str = "20";
  238. else if (size <= 24 * MBIT)
  239. size_str = "24";
  240. else // MGD supports Genesis games with sizes up to 32 Mbit
  241. size_str = "32";
  242. }
  243. }
  244. break;
  245. case UCON64_PCE:
  246. prefix = "PC";
  247. suffix = ".040";
  248. if (size <= 1 * MBIT)
  249. size_str = "1";
  250. else if (size <= 2 * MBIT)
  251. size_str = "2";
  252. else if (size <= 3 * MBIT)
  253. {
  254. size_str = "3";
  255. suffix = ".030";
  256. }
  257. else if (size <= 4 * MBIT)
  258. {
  259. size_str = "4";
  260. suffix = ".048";
  261. }
  262. else
  263. {
  264. suffix = ".058";
  265. if (size <= 6 * MBIT)
  266. size_str = "6";
  267. else // MGD supports PC-Engine games with sizes up to 8 Mbit
  268. size_str = "8";
  269. }
  270. break;
  271. case UCON64_SMS:
  272. prefix = "GG";
  273. suffix = ".060";
  274. if (size < 1 * MBIT)
  275. size_str = "0";
  276. else if (size == 1 * MBIT)
  277. size_str = "1";
  278. else if (size <= 2 * MBIT)
  279. size_str = "2";
  280. else
  281. {
  282. suffix = ".078";
  283. if (size <= 3 * MBIT)
  284. size_str = "3";
  285. else if (size <= 4 * MBIT)
  286. size_str = "4";
  287. else if (size <= 6 * MBIT)
  288. size_str = "6";
  289. else // MGD supports Sega Master System games with sizes up to 8 Mbit
  290. size_str = "8";
  291. }
  292. break;
  293. case UCON64_GAMEGEAR:
  294. prefix = "GG";
  295. suffix = ".040";
  296. if (size < 1 * MBIT)
  297. size_str = "0";
  298. else if (size == 1 * MBIT)
  299. size_str = "1";
  300. else if (size <= 2 * MBIT)
  301. size_str = "2";
  302. else
  303. {
  304. suffix = ".048";
  305. if (size <= 3 * MBIT)
  306. size_str = "3";
  307. else if (size <= 4 * MBIT)
  308. size_str = "4";
  309. else
  310. {
  311. suffix = ".078";
  312. if (size <= 6 * MBIT)
  313. size_str = "6";
  314. else // MGD supports Game Gear games with sizes up to 8 Mbit
  315. size_str = "8";
  316. }
  317. }
  318. break;
  319. case UCON64_GB:
  320. prefix = "GB";
  321. /*
  322. What is the maximum game size the MGD2 supports for GB (color) games?
  323. At least one 64 Mbit game exists, Densha De Go! 2 (J) [C][!].
  324. */
  325. suffix = ".040";
  326. if (size < 1 * MBIT)
  327. size_str = "0";
  328. else if (size == 1 * MBIT)
  329. size_str = "1";
  330. else if (size <= 2 * MBIT)
  331. size_str = "2";
  332. else if (size <= 3 * MBIT)
  333. {
  334. size_str = "3";
  335. suffix = ".030";
  336. }
  337. else if (size <= 4 * MBIT)
  338. {
  339. size_str = "4";
  340. suffix = ".048";
  341. }
  342. else
  343. {
  344. suffix = ".058";
  345. if (size <= 6 * MBIT)
  346. size_str = "6";
  347. else
  348. size_str = "8";
  349. }
  350. break;
  351. }
  352. fname = basename2 (filename);
  353. // Do NOT mess with prefix (strupr()/strlwr()). See below (remove_mgd_id()).
  354. sprintf (name, "%s%s%s", prefix, size_str, fname);
  355. if (size >= 10 * MBIT)
  356. {
  357. if (!strnicmp (name, fname, 4))
  358. strcpy (name, fname);
  359. }
  360. else
  361. {
  362. if (!strnicmp (name, fname, 3))
  363. strcpy (name, fname);
  364. }
  365. if ((p = strchr (name, '.')))
  366. *p = 0;
  367. n = strlen (name);
  368. if (size >= 10 * MBIT)
  369. {
  370. if (n < 7)
  371. strcat (name, "XXX"); // in case fname is 1 character long
  372. n = 7;
  373. }
  374. else
  375. {
  376. if (n < 6)
  377. strcat (name, "XX");
  378. n = 6;
  379. }
  380. name[n] = '0'; // last character must be a number
  381. name[n + 1] = 0;
  382. for (n = 3; n < 8; n++) // we can skip the prefix
  383. if (name[n] == ' ')
  384. name[n] = 'X';
  385. /*
  386. the transfer program "pclink" contains a bug in that it looks at the
  387. entire file name for an ID string (it should look only at the first 2
  388. characters).
  389. */
  390. strupr (name);
  391. remove_mgd_id (name + 3, "SF");
  392. remove_mgd_id (name + 3, "MD");
  393. remove_mgd_id (name + 3, "PC");
  394. remove_mgd_id (name + 3, "GG");
  395. remove_mgd_id (name + 3, "GB");
  396. set_suffix (name, suffix);
  397. }
  398. void
  399. mgd_write_index_file (void *ptr, int n_names)
  400. {
  401. char buf[100 * 10], *p, name[16], dest_name[FILENAME_MAX];
  402. // one line in the index file takes 10 bytes at max (name (8) + "\r\n" (2)),
  403. // so buf is large enough for 44 files of 1/4 Mbit (max for 1 diskette)
  404. if (n_names == 1)
  405. {
  406. strcpy (name, (char *) ptr);
  407. if ((p = strrchr (name, '.')))
  408. *p = 0;
  409. sprintf (buf, "%s\r\n", name); // DOS text file format
  410. }
  411. else if (n_names > 1)
  412. {
  413. int n = 0, offset = 0;
  414. for (; n < n_names; n++)
  415. {
  416. strcpy (name, ((char **) ptr)[n]);
  417. if ((p = strrchr (name, '.')))
  418. *p = 0;
  419. sprintf (buf + offset, "%s\r\n", name);
  420. offset += strlen (name) + 2; // + 2 for "\r\n"
  421. }
  422. }
  423. else // n_names <= 0
  424. return;
  425. strcpy (dest_name, "MULTI-GD");
  426. ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME);
  427. ucon64_fwrite (buf, 0, strlen (buf), dest_name, "wb");
  428. printf (ucon64_msg[WROTE], dest_name);
  429. }