mgd.c 12 KB

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