platform ps2, handle audio similar to psp
[picodrive.git] / pico / cart.c
CommitLineData
cff531af 1/*\r
2 * PicoDrive\r
3 * (c) Copyright Dave, 2004\r
4 * (C) notaz, 2006-2010\r
7bf552b5 5 * (C) irixxxx, 2020-2024\r
cff531af 6 *\r
7 * This work is licensed under the terms of MAME license.\r
8 * See COPYING file in the top-level directory.\r
9 */\r
cc68a136 10\r
efcba75f 11#include "pico_int.h"\r
f821bb70 12#include <cpu/debug.h>\r
83bd0b76 13\r
15ca7152 14#if defined(USE_LIBCHDR)\r
15#include "libchdr/chd.h"\r
16#include "libchdr/cdrom.h"\r
17#endif\r
18\r
4bb0b70e 19#include <unzip/unzip.h>\r
20#include <zlib.h>\r
21\r
b081408f 22static int rom_alloc_size;\r
6ce10a43 23static const char *rom_exts[] = { "bin", "gen", "smd", "md", "32x", "pco", "iso", "sms", "gg", "sg", "sc" };\r
83bd0b76 24\r
45f2f245 25void (*PicoCartUnloadHook)(void);\r
26void (*PicoCartMemSetup)(void);\r
e807ac75 27\r
a9b3ffd3 28void (*PicoCartLoadProgressCB)(int percent) = NULL;\r
b2451ac6 29void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled in Pico/cd/cd_file.c\r
a9b3ffd3 30\r
4c2e3554 31int PicoGameLoaded;\r
32\r
ae87bffa 33static void PicoCartDetect(const char *carthw_cfg);\r
b784d4a5 34static void PicoCartDetectMS(void);\r
d6114368 35\r
71de3cd9 36/* cso struct */\r
37typedef struct _cso_struct\r
38{\r
39 unsigned char in_buff[2*2048];\r
40 unsigned char out_buff[2048];\r
41 struct {\r
42 char magic[4];\r
43 unsigned int unused;\r
44 unsigned int total_bytes;\r
45 unsigned int total_bytes_high; // ignored here\r
46 unsigned int block_size; // 10h\r
47 unsigned char ver;\r
48 unsigned char align;\r
49 unsigned char reserved[2];\r
50 } header;\r
51 unsigned int fpos_in; // input file read pointer\r
52 unsigned int fpos_out; // pos in virtual decompressed file\r
53 int block_in_buff; // block which we have read in in_buff\r
54 int pad;\r
55 int index[0];\r
56}\r
57cso_struct;\r
58\r
ad949800 59static int uncompress_buf(void *dest, int destLen, void *source, int sourceLen)\r
71de3cd9 60{\r
61 z_stream stream;\r
62 int err;\r
63\r
64 stream.next_in = (Bytef*)source;\r
65 stream.avail_in = (uInt)sourceLen;\r
66 stream.next_out = dest;\r
67 stream.avail_out = (uInt)destLen;\r
68\r
69 stream.zalloc = NULL;\r
70 stream.zfree = NULL;\r
71\r
72 err = inflateInit2(&stream, -15);\r
73 if (err != Z_OK) return err;\r
74\r
75 err = inflate(&stream, Z_FINISH);\r
76 if (err != Z_STREAM_END) {\r
77 inflateEnd(&stream);\r
78 return err;\r
79 }\r
80 //*destLen = stream.total_out;\r
81\r
82 return inflateEnd(&stream);\r
83}\r
83bd0b76 84\r
87b0845f 85static const char *get_ext(const char *path)\r
86{\r
87 const char *ext;\r
88 if (strlen(path) < 4)\r
89 return ""; // no ext\r
90\r
91 // allow 2 or 3 char extensions for now\r
92 ext = path + strlen(path) - 2;\r
93 if (ext[-1] != '.') ext--;\r
94 if (ext[-1] != '.')\r
95 return "";\r
96 return ext;\r
97}\r
98\r
b4c2331e 99struct zip_file {\r
100 pm_file file;\r
101 ZIP *zip;\r
102 struct zipent *entry;\r
103 z_stream stream;\r
104 unsigned char inbuf[16384];\r
105 long start;\r
106 unsigned int pos;\r
107};\r
108\r
15ca7152 109#if defined(USE_LIBCHDR)\r
110struct chd_struct {\r
111 pm_file file;\r
112 int fpos;\r
113 int sectorsize;\r
114 chd_file *chd;\r
115 int unitbytes;\r
116 int hunkunits;\r
117 u8 *hunk;\r
118 int hunknum;\r
119};\r
120#endif\r
121\r
83bd0b76 122pm_file *pm_open(const char *path)\r
123{\r
124 pm_file *file = NULL;\r
125 const char *ext;\r
126 FILE *f;\r
127\r
87b0845f 128 if (path == NULL)\r
129 return NULL;\r
83bd0b76 130\r
87b0845f 131 ext = get_ext(path);\r
132 if (strcasecmp(ext, "zip") == 0)\r
83bd0b76 133 {\r
b4c2331e 134 struct zip_file *zfile = NULL;\r
83bd0b76 135 struct zipent *zipentry;\r
83bd0b76 136 ZIP *zipfile;\r
b4c2331e 137 int i, ret;\r
83bd0b76 138\r
139 zipfile = openzip(path);\r
83bd0b76 140 if (zipfile != NULL)\r
141 {\r
142 /* search for suitable file (right extension or large enough file) */\r
143 while ((zipentry = readzip(zipfile)) != NULL)\r
144 {\r
87b0845f 145 ext = get_ext(zipentry->name);\r
83bd0b76 146\r
87b0845f 147 if (zipentry->uncompressed_size >= 32*1024)\r
148 goto found_rom_zip;\r
3e49ffd0 149\r
83bd0b76 150 for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r
87b0845f 151 if (strcasecmp(ext, rom_exts[i]) == 0)\r
152 goto found_rom_zip;\r
83bd0b76 153 }\r
154\r
155 /* zipfile given, but nothing found suitable for us inside */\r
156 goto zip_failed;\r
157\r
158found_rom_zip:\r
b4c2331e 159 zfile = calloc(1, sizeof(*zfile));\r
160 if (zfile == NULL)\r
161 goto zip_failed;\r
162 ret = seekcompresszip(zipfile, zipentry);\r
163 if (ret != 0)\r
164 goto zip_failed;\r
165 ret = inflateInit2(&zfile->stream, -15);\r
166 if (ret != Z_OK) {\r
167 elprintf(EL_STATUS, "zip: inflateInit2 %d", ret);\r
168 goto zip_failed;\r
169 }\r
170 zfile->zip = zipfile;\r
171 zfile->entry = zipentry;\r
172 zfile->start = ftell(zipfile->fp);\r
173 zfile->file.file = zfile;\r
174 zfile->file.size = zipentry->uncompressed_size;\r
175 zfile->file.type = PMT_ZIP;\r
176 strncpy(zfile->file.ext, ext, sizeof(zfile->file.ext) - 1);\r
177 return &zfile->file;\r
83bd0b76 178\r
179zip_failed:\r
83bd0b76 180 closezip(zipfile);\r
b4c2331e 181 free(zfile);\r
83bd0b76 182 return NULL;\r
183 }\r
184 }\r
87b0845f 185 else if (strcasecmp(ext, "cso") == 0)\r
71de3cd9 186 {\r
187 cso_struct *cso = NULL, *tmp = NULL;\r
0d8d97f8 188 int i, size;\r
71de3cd9 189 f = fopen(path, "rb");\r
190 if (f == NULL)\r
191 goto cso_failed;\r
192\r
274fcc35 193#ifdef __GP2X__\r
2d2247c2 194 /* we use our own buffering */\r
195 setvbuf(f, NULL, _IONBF, 0);\r
f8af9634 196#endif\r
2d2247c2 197\r
71de3cd9 198 cso = malloc(sizeof(*cso));\r
199 if (cso == NULL)\r
200 goto cso_failed;\r
201\r
202 if (fread(&cso->header, 1, sizeof(cso->header), f) != sizeof(cso->header))\r
203 goto cso_failed;\r
57c5a5e5 204 cso->header.block_size = CPU_LE4(cso->header.block_size);\r
205 cso->header.total_bytes = CPU_LE4(cso->header.total_bytes);\r
206 cso->header.total_bytes_high = CPU_LE4(cso->header.total_bytes_high);\r
71de3cd9 207\r
208 if (strncmp(cso->header.magic, "CISO", 4) != 0) {\r
209 elprintf(EL_STATUS, "cso: bad header");\r
210 goto cso_failed;\r
211 }\r
212\r
213 if (cso->header.block_size != 2048) {\r
214 elprintf(EL_STATUS, "cso: bad block size (%u)", cso->header.block_size);\r
215 goto cso_failed;\r
216 }\r
217\r
218 size = ((cso->header.total_bytes >> 11) + 1)*4 + sizeof(*cso);\r
219 tmp = realloc(cso, size);\r
220 if (tmp == NULL)\r
221 goto cso_failed;\r
222 cso = tmp;\r
223 elprintf(EL_STATUS, "allocated %i bytes for CSO struct", size);\r
224\r
225 size -= sizeof(*cso); // index size\r
226 if (fread(cso->index, 1, size, f) != size) {\r
227 elprintf(EL_STATUS, "cso: premature EOF");\r
228 goto cso_failed;\r
229 }\r
0d8d97f8 230 for (i = 0; i < size/4; i++)\r
231 cso->index[i] = CPU_LE4(cso->index[i]);\r
71de3cd9 232\r
233 // all ok\r
234 cso->fpos_in = ftell(f);\r
235 cso->fpos_out = 0;\r
236 cso->block_in_buff = -1;\r
87b0845f 237 file = calloc(1, sizeof(*file));\r
71de3cd9 238 if (file == NULL) goto cso_failed;\r
239 file->file = f;\r
240 file->param = cso;\r
241 file->size = cso->header.total_bytes;\r
242 file->type = PMT_CSO;\r
14cd01be 243 strncpy(file->ext, ext, sizeof(file->ext) - 1);\r
71de3cd9 244 return file;\r
245\r
246cso_failed:\r
247 if (cso != NULL) free(cso);\r
248 if (f != NULL) fclose(f);\r
249 return NULL;\r
250 }\r
15ca7152 251#if defined(USE_LIBCHDR)\r
252 else if (strcasecmp(ext, "chd") == 0)\r
253 {\r
254 struct chd_struct *chd = NULL;\r
255 chd_file *cf = NULL;\r
256 const chd_header *head;\r
257\r
258 if (chd_open(path, CHD_OPEN_READ, NULL, &cf) != CHDERR_NONE)\r
259 goto chd_failed;\r
260\r
261 // sanity check\r
262 head = chd_get_header(cf);\r
263 if ((head->hunkbytes == 0) || (head->hunkbytes % CD_FRAME_SIZE))\r
264 goto chd_failed;\r
265\r
266 chd = calloc(1, sizeof(*chd));\r
267 if (chd == NULL)\r
268 goto chd_failed;\r
269 chd->hunk = (u8 *)malloc(head->hunkbytes);\r
270 if (!chd->hunk)\r
271 goto chd_failed;\r
272\r
273 chd->chd = cf;\r
274 chd->unitbytes = head->unitbytes;\r
275 chd->hunkunits = head->hunkbytes / head->unitbytes;\r
276 chd->sectorsize = CD_MAX_SECTOR_DATA; // default to RAW mode\r
277\r
278 chd->fpos = 0;\r
279 chd->hunknum = -1;\r
280\r
281 chd->file.file = chd;\r
282 chd->file.type = PMT_CHD;\r
283 // subchannel data is skipped, remove it from total size\r
0d8d97f8 284 chd->file.size = head->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;\r
15ca7152 285 strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1);\r
286 return &chd->file;\r
287\r
288chd_failed:\r
289 /* invalid CHD file */\r
290 if (chd != NULL) free(chd);\r
291 if (cf != NULL) chd_close(cf);\r
292 return NULL;\r
293 }\r
294#endif\r
83bd0b76 295\r
296 /* not a zip, treat as uncompressed file */\r
297 f = fopen(path, "rb");\r
298 if (f == NULL) return NULL;\r
299\r
87b0845f 300 file = calloc(1, sizeof(*file));\r
83bd0b76 301 if (file == NULL) {\r
302 fclose(f);\r
303 return NULL;\r
304 }\r
305 fseek(f, 0, SEEK_END);\r
306 file->file = f;\r
307 file->param = NULL;\r
308 file->size = ftell(f);\r
309 file->type = PMT_UNCOMPRESSED;\r
87b0845f 310 strncpy(file->ext, ext, sizeof(file->ext) - 1);\r
83bd0b76 311 fseek(f, 0, SEEK_SET);\r
d6114368 312\r
274fcc35 313#ifdef __GP2X__\r
f8af9634 314 if (file->size > 0x400000)\r
315 /* we use our own buffering */\r
316 setvbuf(f, NULL, _IONBF, 0);\r
317#endif\r
318\r
83bd0b76 319 return file;\r
320}\r
321\r
15ca7152 322void pm_sectorsize(int length, pm_file *stream)\r
323{\r
324 // CHD reading needs to know how much binary data is in one data sector(=unit)\r
325#if defined(USE_LIBCHDR)\r
326 if (stream->type == PMT_CHD) {\r
327 struct chd_struct *chd = stream->file;\r
328 chd->sectorsize = length;\r
329 if (chd->sectorsize > chd->unitbytes)\r
330 elprintf(EL_STATUS|EL_ANOMALY, "cd: sector size %d too large for unit %d", chd->sectorsize, chd->unitbytes);\r
331 }\r
332#endif\r
333}\r
334\r
335#if defined(USE_LIBCHDR)\r
336static size_t _pm_read_chd(void *ptr, size_t bytes, pm_file *stream, int is_audio)\r
337{\r
338 int ret = 0;\r
339\r
340 if (stream->type == PMT_CHD) {\r
341 struct chd_struct *chd = stream->file;\r
342 // calculate sector and offset in sector\r
343 int sectsz = is_audio ? CD_MAX_SECTOR_DATA : chd->sectorsize;\r
344 int sector = chd->fpos / sectsz;\r
345 int offset = chd->fpos - (sector * sectsz);\r
346 // calculate hunk and sector offset in hunk\r
347 int hunknum = sector / chd->hunkunits;\r
348 int hunksec = sector - (hunknum * chd->hunkunits);\r
349 int hunkofs = hunksec * chd->unitbytes;\r
350\r
351 while (bytes != 0) {\r
352 // data left in current sector\r
353 int len = sectsz - offset;\r
354\r
355 // update hunk cache if needed\r
356 if (hunknum != chd->hunknum) {\r
357 chd_read(chd->chd, hunknum, chd->hunk);\r
358 chd->hunknum = hunknum;\r
359 }\r
360 if (len > bytes)\r
361 len = bytes;\r
362\r
0d8d97f8 363#if CPU_IS_LE\r
15ca7152 364 if (is_audio) {\r
365 // convert big endian audio samples\r
366 u16 *dst = ptr, v;\r
367 u8 *src = chd->hunk + hunkofs + offset;\r
368 int i;\r
369\r
370 for (i = 0; i < len; i += 4) {\r
371 v = *src++ << 8; *dst++ = v | *src++;\r
372 v = *src++ << 8; *dst++ = v | *src++;\r
373 }\r
374 } else\r
375#endif\r
376 memcpy(ptr, chd->hunk + hunkofs + offset, len);\r
377\r
378 // house keeping\r
379 ret += len;\r
380 chd->fpos += len;\r
381 bytes -= len;\r
382\r
383 // no need to advance internals if there's no more data to read\r
384 if (bytes) {\r
385 ptr += len;\r
386 offset = 0;\r
387\r
388 sector ++;\r
389 hunksec ++;\r
390 hunkofs += chd->unitbytes;\r
391 if (hunksec >= chd->hunkunits) {\r
392 hunksec = 0;\r
393 hunkofs = 0;\r
394 hunknum ++;\r
395 }\r
396 }\r
397 }\r
398 }\r
399\r
400 return ret;\r
401}\r
402#endif\r
403\r
83bd0b76 404size_t pm_read(void *ptr, size_t bytes, pm_file *stream)\r
405{\r
406 int ret;\r
407\r
178a9b68 408 if (stream == NULL)\r
409 return -1;\r
410 else if (stream->type == PMT_UNCOMPRESSED)\r
83bd0b76 411 {\r
412 ret = fread(ptr, 1, bytes, stream->file);\r
413 }\r
414 else if (stream->type == PMT_ZIP)\r
415 {\r
b4c2331e 416 struct zip_file *z = stream->file;\r
417\r
418 if (z->entry->compression_method == 0) {\r
419 int ret = fread(ptr, 1, bytes, z->zip->fp);\r
420 z->pos += ret;\r
421 return ret;\r
422 }\r
423\r
424 z->stream.next_out = ptr;\r
425 z->stream.avail_out = bytes;\r
426 while (z->stream.avail_out != 0) {\r
427 if (z->stream.avail_in == 0) {\r
428 z->stream.avail_in = fread(z->inbuf, 1, sizeof(z->inbuf), z->zip->fp);\r
429 if (z->stream.avail_in == 0)\r
430 break;\r
431 z->stream.next_in = z->inbuf;\r
432 }\r
433 ret = inflate(&z->stream, Z_NO_FLUSH);\r
434 if (ret == Z_STREAM_END)\r
435 break;\r
436 if (ret != Z_OK) {\r
437 elprintf(EL_STATUS, "zip: inflate: %d", ret);\r
438 return 0;\r
439 }\r
440 }\r
441 z->pos += bytes - z->stream.avail_out;\r
442 return bytes - z->stream.avail_out;\r
83bd0b76 443 }\r
71de3cd9 444 else if (stream->type == PMT_CSO)\r
445 {\r
446 cso_struct *cso = stream->param;\r
447 int read_pos, read_len, out_offs, rret;\r
448 int block = cso->fpos_out >> 11;\r
449 int index = cso->index[block];\r
450 int index_end = cso->index[block+1];\r
451 unsigned char *out = ptr, *tmp_dst;\r
452\r
453 ret = 0;\r
454 while (bytes != 0)\r
455 {\r
456 out_offs = cso->fpos_out&0x7ff;\r
457 if (out_offs == 0 && bytes >= 2048)\r
458 tmp_dst = out;\r
459 else tmp_dst = cso->out_buff;\r
460\r
461 read_pos = (index&0x7fffffff) << cso->header.align;\r
462\r
463 if (index < 0) {\r
464 if (read_pos != cso->fpos_in)\r
465 fseek(stream->file, read_pos, SEEK_SET);\r
466 rret = fread(tmp_dst, 1, 2048, stream->file);\r
467 cso->fpos_in = read_pos + rret;\r
468 if (rret != 2048) break;\r
469 } else {\r
470 read_len = (((index_end&0x7fffffff) << cso->header.align) - read_pos) & 0xfff;\r
471 if (block != cso->block_in_buff)\r
472 {\r
473 if (read_pos != cso->fpos_in)\r
ff6b7429 474 fseek(stream->file, read_pos, SEEK_SET);\r
71de3cd9 475 rret = fread(cso->in_buff, 1, read_len, stream->file);\r
476 cso->fpos_in = read_pos + rret;\r
477 if (rret != read_len) {\r
478 elprintf(EL_STATUS, "cso: read failed @ %08x", read_pos);\r
479 break;\r
480 }\r
481 cso->block_in_buff = block;\r
482 }\r
ad949800 483 rret = uncompress_buf(tmp_dst, 2048, cso->in_buff, read_len);\r
71de3cd9 484 if (rret != 0) {\r
485 elprintf(EL_STATUS, "cso: uncompress failed @ %08x with %i", read_pos, rret);\r
486 break;\r
487 }\r
488 }\r
489\r
490 rret = 2048;\r
491 if (out_offs != 0 || bytes < 2048) {\r
492 //elprintf(EL_STATUS, "cso: unaligned/nonfull @ %08x, offs=%i, len=%u", cso->fpos_out, out_offs, bytes);\r
493 if (bytes < rret) rret = bytes;\r
494 if (2048 - out_offs < rret) rret = 2048 - out_offs;\r
495 memcpy(out, tmp_dst + out_offs, rret);\r
496 }\r
497 ret += rret;\r
498 out += rret;\r
499 cso->fpos_out += rret;\r
500 bytes -= rret;\r
501 block++;\r
502 index = index_end;\r
503 index_end = cso->index[block+1];\r
504 }\r
505 }\r
15ca7152 506#if defined(USE_LIBCHDR)\r
507 else if (stream->type == PMT_CHD)\r
508 {\r
509 ret = _pm_read_chd(ptr, bytes, stream, 0);\r
510 }\r
511#endif\r
83bd0b76 512 else\r
513 ret = 0;\r
514\r
515 return ret;\r
516}\r
517\r
15ca7152 518size_t pm_read_audio(void *ptr, size_t bytes, pm_file *stream)\r
519{\r
178a9b68 520 if (stream == NULL)\r
521 return -1;\r
15ca7152 522#if !(CPU_IS_LE)\r
178a9b68 523 else if (stream->type == PMT_UNCOMPRESSED)\r
15ca7152 524 {\r
525 // convert little endian audio samples from WAV file\r
526 int ret = pm_read(ptr, bytes, stream);\r
527 u16 *dst = ptr, v;\r
528 u8 *src = ptr;\r
529 int i;\r
530\r
531 for (i = 0; i < ret; i += 4) {\r
532 v = *src++; *dst++ = v | (*src++ << 8);\r
533 v = *src++; *dst++ = v | (*src++ << 8);\r
534 }\r
535 return ret;\r
536 }\r
537 else\r
538#endif\r
539#if defined(USE_LIBCHDR)\r
540 if (stream->type == PMT_CHD)\r
541 {\r
542 return _pm_read_chd(ptr, bytes, stream, 1);\r
543 }\r
544#endif\r
545 return pm_read(ptr, bytes, stream);\r
546}\r
547\r
83bd0b76 548int pm_seek(pm_file *stream, long offset, int whence)\r
549{\r
178a9b68 550 if (stream == NULL)\r
551 return -1;\r
552 else if (stream->type == PMT_UNCOMPRESSED)\r
83bd0b76 553 {\r
7336a99a 554 fseek(stream->file, offset, whence);\r
555 return ftell(stream->file);\r
83bd0b76 556 }\r
557 else if (stream->type == PMT_ZIP)\r
558 {\r
b4c2331e 559 struct zip_file *z = stream->file;\r
560 unsigned int pos = z->pos;\r
561 int ret;\r
562\r
563 switch (whence)\r
564 {\r
565 case SEEK_CUR: pos += offset; break;\r
566 case SEEK_SET: pos = offset; break;\r
567 case SEEK_END: pos = stream->size - offset; break;\r
568 }\r
569 if (z->entry->compression_method == 0) {\r
570 ret = fseek(z->zip->fp, z->start + pos, SEEK_SET);\r
571 if (ret == 0)\r
572 return (z->pos = pos);\r
573 return -1;\r
574 }\r
575 offset = pos - z->pos;\r
576 if (pos < z->pos) {\r
577 // full decompress from the start\r
578 fseek(z->zip->fp, z->start, SEEK_SET);\r
579 z->stream.avail_in = 0;\r
580 z->stream.next_in = z->inbuf;\r
581 inflateReset(&z->stream);\r
582 z->pos = 0;\r
583 offset = pos;\r
584 }\r
585\r
6311a3ba 586 if (PicoIn.osdMessage != NULL && offset > 4 * 1024 * 1024)\r
587 PicoIn.osdMessage("Decompressing data...");\r
b4c2331e 588\r
589 while (offset > 0) {\r
590 char buf[16 * 1024];\r
591 size_t l = offset > sizeof(buf) ? sizeof(buf) : offset;\r
592 ret = pm_read(buf, l, stream);\r
593 if (ret != l)\r
594 break;\r
595 offset -= l;\r
66fdc0f0 596 }\r
b4c2331e 597 return z->pos;\r
83bd0b76 598 }\r
71de3cd9 599 else if (stream->type == PMT_CSO)\r
600 {\r
601 cso_struct *cso = stream->param;\r
602 switch (whence)\r
603 {\r
604 case SEEK_CUR: cso->fpos_out += offset; break;\r
605 case SEEK_SET: cso->fpos_out = offset; break;\r
606 case SEEK_END: cso->fpos_out = cso->header.total_bytes - offset; break;\r
607 }\r
608 return cso->fpos_out;\r
609 }\r
15ca7152 610#if defined(USE_LIBCHDR)\r
611 else if (stream->type == PMT_CHD)\r
612 {\r
613 struct chd_struct *chd = stream->file;\r
614 switch (whence)\r
615 {\r
616 case SEEK_CUR: chd->fpos += offset; break;\r
617 case SEEK_SET: chd->fpos = offset; break;\r
618 case SEEK_END: chd->fpos = stream->size - offset; break;\r
619 }\r
620 return chd->fpos;\r
621 }\r
622#endif\r
83bd0b76 623 else\r
624 return -1;\r
625}\r
626\r
627int pm_close(pm_file *fp)\r
628{\r
629 int ret = 0;\r
630\r
631 if (fp == NULL) return EOF;\r
632\r
633 if (fp->type == PMT_UNCOMPRESSED)\r
634 {\r
635 fclose(fp->file);\r
636 }\r
637 else if (fp->type == PMT_ZIP)\r
638 {\r
b4c2331e 639 struct zip_file *z = fp->file;\r
640 inflateEnd(&z->stream);\r
641 closezip(z->zip);\r
83bd0b76 642 }\r
71de3cd9 643 else if (fp->type == PMT_CSO)\r
644 {\r
645 free(fp->param);\r
646 fclose(fp->file);\r
647 }\r
15ca7152 648#if defined(USE_LIBCHDR)\r
649 else if (fp->type == PMT_CHD)\r
650 {\r
651 struct chd_struct *chd = fp->file;\r
652 chd_close(chd->chd);\r
653 if (chd->hunk)\r
654 free(chd->hunk);\r
655 }\r
656#endif\r
83bd0b76 657 else\r
658 ret = EOF;\r
659\r
660 free(fp);\r
661 return ret;\r
662}\r
663\r
83ff19ec 664// byteswap, data needs to be int aligned, src can match dst\r
665void Byteswap(void *dst, const void *src, int len)\r
cc68a136 666{\r
57c5a5e5 667#if CPU_IS_LE\r
83ff19ec 668 const unsigned int *ps = src;\r
669 unsigned int *pd = dst;\r
670 int i, m;\r
cc68a136 671\r
83ff19ec 672 if (len < 2)\r
673 return;\r
cc68a136 674\r
83ff19ec 675 m = 0x00ff00ff;\r
676 for (i = 0; i < len / 4; i++) {\r
677 unsigned int t = ps[i];\r
678 pd[i] = ((t & m) << 8) | ((t & ~m) >> 8);\r
cc68a136 679 }\r
57c5a5e5 680#endif\r
cc68a136 681}\r
682\r
683// Interleve a 16k block and byteswap\r
684static int InterleveBlock(unsigned char *dest,unsigned char *src)\r
685{\r
686 int i=0;\r
57c5a5e5 687 for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(1)]=src[ i]; // Odd\r
688 for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(0)]=src[0x2000+i]; // Even\r
cc68a136 689 return 0;\r
690}\r
691\r
692// Decode a SMD file\r
693static int DecodeSmd(unsigned char *data,int len)\r
694{\r
695 unsigned char *temp=NULL;\r
696 int i=0;\r
697\r
698 temp=(unsigned char *)malloc(0x4000);\r
699 if (temp==NULL) return 1;\r
700 memset(temp,0,0x4000);\r
701\r
702 // Interleve each 16k block and shift down by 0x200:\r
703 for (i=0; i+0x4200<=len; i+=0x4000)\r
704 {\r
705 InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer\r
706 memcpy(data+i,temp,0x4000); // Copy back in\r
707 }\r
708\r
709 free(temp);\r
710 return 0;\r
711}\r
712\r
87b0845f 713static unsigned char *PicoCartAlloc(int filesize, int is_sms)\r
cc68a136 714{\r
cc68a136 715 unsigned char *rom;\r
716\r
28165a19 717 // make size power of 2 for easier banking handling\r
718 int s = 0, tmp = filesize;\r
719 while ((tmp >>= 1) != 0)\r
720 s++;\r
721 if (filesize > (1 << s))\r
722 s++;\r
723 rom_alloc_size = 1 << s;\r
724\r
87b0845f 725 if (is_sms) {\r
b4db550e 726 // be sure we can cover all address space\r
727 if (rom_alloc_size < 0x10000)\r
728 rom_alloc_size = 0x10000;\r
87b0845f 729 }\r
730 else {\r
731 // align to 512K for memhandlers\r
28165a19 732 rom_alloc_size = (rom_alloc_size + 0x7ffff) & ~0x7ffff;\r
87b0845f 733 }\r
cc68a136 734\r
b081408f 735 if (rom_alloc_size - filesize < 4)\r
736 rom_alloc_size += 4; // padding for out-of-bound exec protection\r
cc68a136 737\r
738 // Allocate space for the rom plus padding\r
b081408f 739 // use special address for 32x dynarec\r
e743be20 740 rom = plat_mmap(0x02000000, rom_alloc_size, 0, 0);\r
cc68a136 741 return rom;\r
742}\r
743\r
a5085db3 744int PicoCartLoad(pm_file *f, const unsigned char *rom, unsigned int romsize,\r
745 unsigned char **prom, unsigned int *psize, int is_sms)\r
cc68a136 746{\r
a5085db3 747 unsigned char *rom_data = NULL;\r
87b0845f 748 int size, bytes_read;\r
cc68a136 749\r
a5085db3 750 if (!f && !rom)\r
87b0845f 751 return 1;\r
752\r
a5085db3 753 if (!rom)\r
754 size = f->size;\r
755 else\r
756 size = romsize;\r
757\r
cc68a136 758 if (size <= 0) return 1;\r
87b0845f 759 size = (size+3)&~3; // Round up to a multiple of 4\r
cc68a136 760\r
761 // Allocate space for the rom plus padding\r
a5085db3 762 rom_data = PicoCartAlloc(size, is_sms);\r
763 if (rom_data == NULL) {\r
c6196c0f 764 elprintf(EL_STATUS, "out of memory (wanted %i)", size);\r
f8af9634 765 return 2;\r
7336a99a 766 }\r
cc68a136 767\r
a5085db3 768 if (!rom) {\r
769 if (PicoCartLoadProgressCB != NULL)\r
a9b3ffd3 770 {\r
a5085db3 771 // read ROM in blocks, just for fun\r
772 int ret;\r
773 unsigned char *p = rom_data;\r
774 bytes_read=0;\r
775 do\r
776 {\r
777 int todo = size - bytes_read;\r
778 if (todo > 256*1024) todo = 256*1024;\r
779 ret = pm_read(p,todo,f);\r
780 bytes_read += ret;\r
781 p += ret;\r
782 PicoCartLoadProgressCB(bytes_read * 100LL / size);\r
783 }\r
784 while (ret > 0);\r
785 }\r
786 else\r
787 bytes_read = pm_read(rom_data,size,f); // Load up the rom\r
788\r
789 if (bytes_read <= 0) {\r
790 elprintf(EL_STATUS, "read failed");\r
791 plat_munmap(rom_data, rom_alloc_size);\r
792 return 3;\r
a9b3ffd3 793 }\r
a9b3ffd3 794 }\r
795 else\r
a5085db3 796 memcpy(rom_data, rom, romsize);\r
bf098bc5 797\r
3e49ffd0 798 if (!is_sms)\r
799 {\r
800 // maybe we are loading MegaCD BIOS?\r
a5085db3 801 if (!(PicoIn.AHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom_data+0x124, "BOOT", 4) ||\r
802 !strncmp((char *)rom_data+0x128, "BOOT", 4))) {\r
93f9619e 803 PicoIn.AHW |= PAHW_MCD;\r
3e49ffd0 804 }\r
cc68a136 805\r
3e49ffd0 806 // Check for SMD:\r
87b0845f 807 if (size >= 0x4200 && (size&0x3fff) == 0x200 &&\r
a5085db3 808 ((rom_data[0x2280] == 'S' && rom_data[0x280] == 'E') || (rom_data[0x280] == 'S' && rom_data[0x2281] == 'E'))) {\r
87b0845f 809 elprintf(EL_STATUS, "SMD format detected.");\r
a5085db3 810 DecodeSmd(rom_data,size); size-=0x200; // Decode and byteswap SMD\r
3e49ffd0 811 }\r
a5085db3 812 else Byteswap(rom_data, rom_data, size); // Just byteswap\r
21395e38 813 }\r
87b0845f 814 else\r
815 {\r
816 if (size >= 0x4200 && (size&0x3fff) == 0x200) {\r
817 elprintf(EL_STATUS, "SMD format detected.");\r
818 // at least here it's not interleaved\r
819 size -= 0x200;\r
a5085db3 820 memmove(rom_data, rom_data + 0x200, size);\r
87b0845f 821 }\r
822 }\r
cc68a136 823\r
a5085db3 824 if (prom) *prom = rom_data;\r
87b0845f 825 if (psize) *psize = size;\r
cc68a136 826\r
827 return 0;\r
828}\r
829\r
f8ef8ff7 830// Insert a cartridge:\r
ae87bffa 831int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg)\r
cc68a136 832{\r
833 // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r
834 // This will hang the emu, but will prevent nasty crashes.\r
835 // note: 4 bytes are padded to every ROM\r
d6114368 836 if (rom != NULL)\r
91ea9406 837 *(u32 *)(rom+romsize) = CPU_BE2(0x4EFAFFFE);\r
cc68a136 838\r
cc68a136 839 Pico.rom=rom;\r
840 Pico.romsize=romsize;\r
841\r
88fd63ad 842 if (Pico.sv.data) {\r
843 free(Pico.sv.data);\r
844 Pico.sv.data = NULL;\r
d6114368 845 }\r
846\r
e807ac75 847 if (PicoCartUnloadHook != NULL) {\r
848 PicoCartUnloadHook();\r
849 PicoCartUnloadHook = NULL;\r
850 }\r
a2b8c5a5 851 pdb_cleanup();\r
e807ac75 852\r
0aa63fce 853 PicoIn.AHW &= ~(PAHW_32X|PAHW_SVP);\r
602133e1 854\r
45f2f245 855 PicoCartMemSetup = NULL;\r
f8ef8ff7 856 PicoDmaHook = NULL;\r
857 PicoResetHook = NULL;\r
017512f2 858 PicoLineHook = NULL;\r
fad24893 859 PicoLoadStateHook = NULL;\r
945c2fdc 860 carthw_chunks = NULL;\r
f8ef8ff7 861\r
4fc85c80 862 if (!(PicoIn.AHW & (PAHW_MCD|PAHW_SMS|PAHW_PICO)))\r
ae87bffa 863 PicoCartDetect(carthw_cfg);\r
4fc85c80 864 if (PicoIn.AHW & PAHW_SMS)\r
b784d4a5 865 PicoCartDetectMS();\r
f1e31ac6
VS
866 if (PicoIn.AHW & PAHW_SVP)\r
867 PicoSVPStartup();\r
868 if (PicoIn.AHW & PAHW_PICO)\r
869 PicoInitPico();\r
f8ef8ff7 870\r
1dceadae 871 // setup correct memory map for loaded ROM\r
0aa63fce 872 switch (PicoIn.AHW & ~(PAHW_GG|PAHW_SG|PAHW_SC)) {\r
9037e45d 873 default:\r
93f9619e 874 elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoIn.AHW);\r
9037e45d 875 case 0:\r
876 case PAHW_SVP: PicoMemSetup(); break;\r
877 case PAHW_MCD: PicoMemSetupCD(); break;\r
878 case PAHW_PICO: PicoMemSetupPico(); break;\r
3e49ffd0 879 case PAHW_SMS: PicoMemSetupMS(); break;\r
9037e45d 880 }\r
1dceadae 881\r
45f2f245 882 if (PicoCartMemSetup != NULL)\r
883 PicoCartMemSetup();\r
884\r
93f9619e 885 if (PicoIn.AHW & PAHW_SMS)\r
3e49ffd0 886 PicoPowerMS();\r
887 else\r
888 PicoPower();\r
889\r
4c2e3554 890 PicoGameLoaded = 1;\r
1cb1584b 891 return 0;\r
cc68a136 892}\r
893\r
a736af3e 894int PicoCartResize(int newsize)\r
895{\r
896 void *tmp = plat_mremap(Pico.rom, rom_alloc_size, newsize);\r
897 if (tmp == NULL)\r
898 return -1;\r
899\r
900 Pico.rom = tmp;\r
901 rom_alloc_size = newsize;\r
902 return 0;\r
903}\r
904\r
2aa27095 905void PicoCartUnload(void)\r
cc68a136 906{\r
3e49ffd0 907 if (PicoCartUnloadHook != NULL) {\r
908 PicoCartUnloadHook();\r
909 PicoCartUnloadHook = NULL;\r
910 }\r
911\r
9961d9fd 912 PicoUnload32x();\r
5e49c3a8 913\r
a12b1b29 914 if (Pico.rom != NULL) {\r
053fd9b4 915 SekFinishIdleDet();\r
b081408f 916 plat_munmap(Pico.rom, rom_alloc_size);\r
917 Pico.rom = NULL;\r
a12b1b29 918 }\r
4c2e3554 919 PicoGameLoaded = 0;\r
cc68a136 920}\r
921\r
e1e8ca17 922static unsigned int rom_crc32(int size)\r
000f5335 923{\r
924 unsigned int crc;\r
925 elprintf(EL_STATUS, "caclulating CRC32..");\r
e1e8ca17 926 if (size <= 0 || size > Pico.romsize) size = Pico.romsize;\r
000f5335 927\r
928 // have to unbyteswap for calculation..\r
e1e8ca17 929 Byteswap(Pico.rom, Pico.rom, size);\r
930 crc = crc32(0, Pico.rom, size);\r
931 Byteswap(Pico.rom, Pico.rom, size);\r
000f5335 932 return crc;\r
933}\r
934\r
1f4e9f14 935static int rom_strcmp(int rom_offset, const char *s1)\r
1dceadae 936{\r
1f4e9f14 937 int i, len = strlen(s1);\r
45f2f245 938 const char *s_rom = (const char *)Pico.rom;\r
a76fad41 939 if (rom_offset + len > Pico.romsize)\r
940 return 0;\r
1dceadae 941 for (i = 0; i < len; i++)\r
57c5a5e5 942 if (s1[i] != s_rom[MEM_BE2(i + rom_offset)])\r
1dceadae 943 return 1;\r
944 return 0;\r
945}\r
946\r
af37bca8 947static unsigned int rom_read32(int addr)\r
948{\r
949 unsigned short *m = (unsigned short *)(Pico.rom + addr);\r
950 return (m[0] << 16) | m[1];\r
951}\r
952\r
45f2f245 953static char *sskip(char *s)\r
954{\r
955 while (*s && isspace_(*s))\r
956 s++;\r
957 return s;\r
958}\r
959\r
960static void rstrip(char *s)\r
961{\r
962 char *p;\r
963 for (p = s + strlen(s) - 1; p >= s; p--)\r
964 if (isspace_(*p))\r
965 *p = 0;\r
966}\r
967\r
000f5335 968static int parse_3_vals(char *p, int *val0, int *val1, int *val2)\r
969{\r
970 char *r;\r
971 *val0 = strtoul(p, &r, 0);\r
972 if (r == p)\r
973 goto bad;\r
974 p = sskip(r);\r
975 if (*p++ != ',')\r
976 goto bad;\r
977 *val1 = strtoul(p, &r, 0);\r
978 if (r == p)\r
979 goto bad;\r
980 p = sskip(r);\r
981 if (*p++ != ',')\r
982 goto bad;\r
983 *val2 = strtoul(p, &r, 0);\r
984 if (r == p)\r
985 goto bad;\r
986\r
987 return 1;\r
988bad:\r
989 return 0;\r
990}\r
991\r
45f2f245 992static int is_expr(const char *expr, char **pr)\r
993{\r
994 int len = strlen(expr);\r
995 char *p = *pr;\r
996\r
997 if (strncmp(expr, p, len) != 0)\r
998 return 0;\r
999 p = sskip(p + len);\r
1000 if (*p != '=')\r
1001 return 0; // wrong or malformed\r
1002\r
1003 *pr = sskip(p + 1);\r
1004 return 1;\r
1005}\r
1006\r
005651ce 1007#include "carthw_cfg.c"\r
1008\r
8b9dbcde 1009static void parse_carthw(const char *carthw_cfg, int *fill_sram,\r
1010 int *hw_detected)\r
45f2f245 1011{\r
1012 int line = 0, any_checks_passed = 0, skip_sect = 0;\r
005651ce 1013 const char *s, *builtin = builtin_carthw_cfg;\r
000f5335 1014 int tmp, rom_crc = 0;\r
45f2f245 1015 char buff[256], *p, *r;\r
1016 FILE *f;\r
1017\r
ae87bffa 1018 f = fopen(carthw_cfg, "r");\r
005651ce 1019 if (f == NULL)\r
1020 f = fopen("pico/carthw.cfg", "r");\r
1021 if (f == NULL)\r
ae87bffa 1022 elprintf(EL_STATUS, "couldn't open carthw.cfg!");\r
45f2f245 1023\r
005651ce 1024 for (;;)\r
45f2f245 1025 {\r
005651ce 1026 if (f != NULL) {\r
1027 p = fgets(buff, sizeof(buff), f);\r
1028 if (p == NULL)\r
1029 break;\r
1030 }\r
1031 else {\r
1032 if (*builtin == 0)\r
1033 break;\r
1034 for (s = builtin; *s != 0 && *s != '\n'; s++)\r
1035 ;\r
1036 while (*s == '\n')\r
1037 s++;\r
1038 tmp = s - builtin;\r
1039 if (tmp > sizeof(buff) - 1)\r
1040 tmp = sizeof(buff) - 1;\r
1041 memcpy(buff, builtin, tmp);\r
1042 buff[tmp] = 0;\r
1043 p = buff;\r
1044 builtin = s;\r
1045 }\r
1046\r
45f2f245 1047 line++;\r
1048 p = sskip(p);\r
1049 if (*p == 0 || *p == '#')\r
1050 continue;\r
1051\r
1052 if (*p == '[') {\r
1053 any_checks_passed = 0;\r
1054 skip_sect = 0;\r
1055 continue;\r
1056 }\r
1057 \r
1058 if (skip_sect)\r
1059 continue;\r
1060\r
1061 /* look for checks */\r
1062 if (is_expr("check_str", &p))\r
1063 {\r
1064 int offs;\r
1065 offs = strtoul(p, &r, 0);\r
1066 if (offs < 0 || offs > Pico.romsize) {\r
1067 elprintf(EL_STATUS, "carthw:%d: check_str offs out of range: %d\n", line, offs);\r
1068 goto bad;\r
1069 }\r
1070 p = sskip(r);\r
1071 if (*p != ',')\r
1072 goto bad;\r
1073 p = sskip(p + 1);\r
1074 if (*p != '"')\r
1075 goto bad;\r
1076 p++;\r
1077 r = strchr(p, '"');\r
1078 if (r == NULL)\r
1079 goto bad;\r
1080 *r = 0;\r
1081\r
1082 if (rom_strcmp(offs, p) == 0)\r
1083 any_checks_passed = 1;\r
1084 else\r
1085 skip_sect = 1;\r
1086 continue;\r
1087 }\r
1088 else if (is_expr("check_size_gt", &p))\r
1089 {\r
1090 int size;\r
1091 size = strtoul(p, &r, 0);\r
1092 if (r == p || size < 0)\r
1093 goto bad;\r
1094\r
1095 if (Pico.romsize > size)\r
1096 any_checks_passed = 1;\r
1097 else\r
1098 skip_sect = 1;\r
1099 continue;\r
1100 }\r
1101 else if (is_expr("check_csum", &p))\r
1102 {\r
1103 int csum;\r
1104 csum = strtoul(p, &r, 0);\r
1105 if (r == p || (csum & 0xffff0000))\r
1106 goto bad;\r
1107\r
1108 if (csum == (rom_read32(0x18c) & 0xffff))\r
1109 any_checks_passed = 1;\r
1110 else\r
1111 skip_sect = 1;\r
1112 continue;\r
1113 }\r
000f5335 1114 else if (is_expr("check_crc32", &p))\r
1115 {\r
1116 unsigned int crc;\r
1117 crc = strtoul(p, &r, 0);\r
1118 if (r == p)\r
1119 goto bad;\r
1120\r
1121 if (rom_crc == 0)\r
e1e8ca17 1122 rom_crc = rom_crc32(64*1024);\r
000f5335 1123 if (crc == rom_crc)\r
1124 any_checks_passed = 1;\r
1125 else\r
1126 skip_sect = 1;\r
1127 continue;\r
1128 }\r
45f2f245 1129\r
1130 /* now time for actions */\r
1131 if (is_expr("hw", &p)) {\r
1132 if (!any_checks_passed)\r
1133 goto no_checks;\r
8b9dbcde 1134 *hw_detected = 1;\r
45f2f245 1135 rstrip(p);\r
1136\r
1137 if (strcmp(p, "svp") == 0)\r
f1e31ac6 1138 PicoIn.AHW = PAHW_SVP;\r
45f2f245 1139 else if (strcmp(p, "pico") == 0)\r
f1e31ac6 1140 PicoIn.AHW = PAHW_PICO;\r
000f5335 1141 else if (strcmp(p, "prot") == 0)\r
1142 carthw_sprot_startup();\r
1143 else if (strcmp(p, "ssf2_mapper") == 0)\r
1144 carthw_ssf2_startup();\r
45f2f245 1145 else if (strcmp(p, "x_in_1_mapper") == 0)\r
1146 carthw_Xin1_startup();\r
1147 else if (strcmp(p, "realtec_mapper") == 0)\r
1148 carthw_realtec_startup();\r
1149 else if (strcmp(p, "radica_mapper") == 0)\r
1150 carthw_radica_startup();\r
30f0fdd4 1151 else if (strcmp(p, "piersolar_mapper") == 0)\r
1152 carthw_pier_startup();\r
60392bf4 1153 else if (strcmp(p, "sf001_mapper") == 0)\r
1154 carthw_sf001_startup();\r
1155 else if (strcmp(p, "sf002_mapper") == 0)\r
1156 carthw_sf002_startup();\r
b7697ca3 1157 else if (strcmp(p, "sf004_mapper") == 0)\r
1158 carthw_sf004_startup();\r
e1e8ca17 1159 else if (strcmp(p, "lk3_mapper") == 0)\r
1160 carthw_lk3_startup();\r
1161 else if (strcmp(p, "smw64_mapper") == 0)\r
1162 carthw_smw64_startup();\r
45f2f245 1163 else {\r
1164 elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p);\r
1165 skip_sect = 1;\r
8b9dbcde 1166 *hw_detected = 0;\r
45f2f245 1167 }\r
1168 continue;\r
1169 }\r
1170 if (is_expr("sram_range", &p)) {\r
1171 int start, end;\r
1172\r
1173 if (!any_checks_passed)\r
1174 goto no_checks;\r
1175 rstrip(p);\r
1176\r
1177 start = strtoul(p, &r, 0);\r
1178 if (r == p)\r
1179 goto bad;\r
1180 p = sskip(r);\r
1181 if (*p != ',')\r
1182 goto bad;\r
1183 p = sskip(p + 1);\r
1184 end = strtoul(p, &r, 0);\r
1185 if (r == p)\r
1186 goto bad;\r
1187 if (((start | end) & 0xff000000) || start > end) {\r
1188 elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end);\r
1189 goto bad_nomsg;\r
1190 }\r
88fd63ad 1191 Pico.sv.start = start;\r
1192 Pico.sv.end = end;\r
45f2f245 1193 continue;\r
1194 }\r
1195 else if (is_expr("prop", &p)) {\r
1196 if (!any_checks_passed)\r
1197 goto no_checks;\r
1198 rstrip(p);\r
1199\r
1200 if (strcmp(p, "no_sram") == 0)\r
88fd63ad 1201 Pico.sv.flags &= ~SRF_ENABLED;\r
45f2f245 1202 else if (strcmp(p, "no_eeprom") == 0)\r
88fd63ad 1203 Pico.sv.flags &= ~SRF_EEPROM;\r
45f2f245 1204 else if (strcmp(p, "filled_sram") == 0)\r
1205 *fill_sram = 1;\r
fe8f2d96 1206 else if (strcmp(p, "wwfraw_hack") == 0)\r
1207 PicoIn.quirks |= PQUIRK_WWFRAW_HACK;\r
1208 else if (strcmp(p, "blackthorne_hack") == 0)\r
1209 PicoIn.quirks |= PQUIRK_BLACKTHORNE_HACK;\r
1210 else if (strcmp(p, "marscheck_hack") == 0)\r
1211 PicoIn.quirks |= PQUIRK_MARSCHECK_HACK;\r
a76fad41 1212 else if (strcmp(p, "force_6btn") == 0)\r
93f9619e 1213 PicoIn.quirks |= PQUIRK_FORCE_6BTN;\r
a67db32a 1214 else if (strcmp(p, "no_z80_bus_lock") == 0)\r
1215 PicoIn.quirks |= PQUIRK_NO_Z80_BUS_LOCK;\r
45f2f245 1216 else {\r
1217 elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p);\r
1218 goto bad_nomsg;\r
1219 }\r
a76fad41 1220 elprintf(EL_STATUS, "game prop: %s", p);\r
45f2f245 1221 continue;\r
1222 }\r
1223 else if (is_expr("eeprom_type", &p)) {\r
1224 int type;\r
1225 if (!any_checks_passed)\r
1226 goto no_checks;\r
1227 rstrip(p);\r
1228\r
1229 type = strtoul(p, &r, 0);\r
1230 if (r == p || type < 0)\r
1231 goto bad;\r
88fd63ad 1232 Pico.sv.eeprom_type = type;\r
1233 Pico.sv.flags |= SRF_EEPROM;\r
45f2f245 1234 continue;\r
1235 }\r
1236 else if (is_expr("eeprom_lines", &p)) {\r
1237 int scl, sda_in, sda_out;\r
1238 if (!any_checks_passed)\r
1239 goto no_checks;\r
1240 rstrip(p);\r
1241\r
000f5335 1242 if (!parse_3_vals(p, &scl, &sda_in, &sda_out))\r
45f2f245 1243 goto bad;\r
000f5335 1244 if (scl < 0 || scl > 15 || sda_in < 0 || sda_in > 15 ||\r
1245 sda_out < 0 || sda_out > 15)\r
45f2f245 1246 goto bad;\r
1247\r
88fd63ad 1248 Pico.sv.eeprom_bit_cl = scl;\r
1249 Pico.sv.eeprom_bit_in = sda_in;\r
1250 Pico.sv.eeprom_bit_out= sda_out;\r
45f2f245 1251 continue;\r
1252 }\r
000f5335 1253 else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) {\r
1254 int addr, mask, val;\r
1255 if (!any_checks_passed)\r
1256 goto no_checks;\r
1257 rstrip(p);\r
1258\r
1259 if (!parse_3_vals(p, &addr, &mask, &val))\r
1260 goto bad;\r
1261\r
1262 carthw_sprot_new_location(addr, mask, val, tmp ? 1 : 0);\r
1263 continue;\r
1264 }\r
45f2f245 1265\r
1266\r
1267bad:\r
1268 elprintf(EL_STATUS, "carthw:%d: unrecognized expression: %s", line, buff);\r
1269bad_nomsg:\r
1270 skip_sect = 1;\r
1271 continue;\r
1272\r
1273no_checks:\r
1274 elprintf(EL_STATUS, "carthw:%d: command without any checks before it: %s", line, buff);\r
1275 skip_sect = 1;\r
1276 continue;\r
1277 }\r
005651ce 1278\r
1279 if (f != NULL)\r
1280 fclose(f);\r
45f2f245 1281}\r
1282\r
a12b1b29 1283/*\r
1284 * various cart-specific things, which can't be handled by generic code\r
a12b1b29 1285 */\r
ae87bffa 1286static void PicoCartDetect(const char *carthw_cfg)\r
1dceadae 1287{\r
8b9dbcde 1288 int carthw_detected = 0;\r
000f5335 1289 int fill_sram = 0;\r
1dceadae 1290\r
88fd63ad 1291 memset(&Pico.sv, 0, sizeof(Pico.sv));\r
78c2237a 1292 if (Pico.rom[MEM_BE2(0x1B0)] == 'R' && Pico.rom[MEM_BE2(0x1B1)] == 'A')\r
1dceadae 1293 {\r
88fd63ad 1294 Pico.sv.start = rom_read32(0x1B4) & ~0xff000001; // align\r
1295 Pico.sv.end = (rom_read32(0x1B8) & ~0xff000000) | 1;\r
78c2237a 1296 if (Pico.rom[MEM_BE2(0x1B3)] & 0x40)\r
1dceadae 1297 // EEPROM\r
88fd63ad 1298 Pico.sv.flags |= SRF_EEPROM;\r
1299 Pico.sv.flags |= SRF_ENABLED;\r
1dceadae 1300 }\r
88fd63ad 1301 if (Pico.sv.end == 0 || Pico.sv.start > Pico.sv.end)\r
1dceadae 1302 {\r
1303 // some games may have bad headers, like S&K and Sonic3\r
1304 // note: majority games use 0x200000 as starting address, but there are some which\r
1305 // use something else (0x300000 by HardBall '95). Luckily they have good headers.\r
88fd63ad 1306 Pico.sv.start = 0x200000;\r
1307 Pico.sv.end = 0x203FFF;\r
1308 Pico.sv.flags |= SRF_ENABLED;\r
1dceadae 1309 }\r
1dceadae 1310\r
1311 // set EEPROM defaults, in case it gets detected\r
88fd63ad 1312 Pico.sv.eeprom_type = 0; // 7bit (24C01)\r
1313 Pico.sv.eeprom_bit_cl = 1;\r
1314 Pico.sv.eeprom_bit_in = 0;\r
1315 Pico.sv.eeprom_bit_out= 0;\r
1dceadae 1316\r
ae87bffa 1317 if (carthw_cfg != NULL)\r
8b9dbcde 1318 parse_carthw(carthw_cfg, &fill_sram, &carthw_detected);\r
1319\r
1320 // assume the standard mapper for large roms\r
1321 if (!carthw_detected && Pico.romsize > 0x400000)\r
1322 carthw_ssf2_startup();\r
9037e45d 1323\r
88fd63ad 1324 if (Pico.sv.flags & SRF_ENABLED)\r
757f8dae 1325 {\r
88fd63ad 1326 if (Pico.sv.flags & SRF_EEPROM)\r
1327 Pico.sv.size = 0x2000;\r
45f2f245 1328 else\r
88fd63ad 1329 Pico.sv.size = Pico.sv.end - Pico.sv.start + 1;\r
757f8dae 1330\r
88fd63ad 1331 Pico.sv.data = calloc(Pico.sv.size, 1);\r
1332 if (Pico.sv.data == NULL)\r
1333 Pico.sv.flags &= ~SRF_ENABLED;\r
a12b1b29 1334\r
88fd63ad 1335 if (Pico.sv.eeprom_type == 1) // 1 == 0 in PD EEPROM code\r
1336 Pico.sv.eeprom_type = 0;\r
0b35350d 1337 }\r
1338\r
88fd63ad 1339 if ((Pico.sv.flags & SRF_ENABLED) && fill_sram)\r
757f8dae 1340 {\r
45f2f245 1341 elprintf(EL_STATUS, "SRAM fill");\r
88fd63ad 1342 memset(Pico.sv.data, 0xff, Pico.sv.size);\r
757f8dae 1343 }\r
1f4e9f14 1344\r
fe8f2d96 1345 // tweak for Blackthorne: master SH2 overwrites stack of slave SH2 being in PWM\r
1346 // interrupt. On real hardware, nothing happens since slave fetches the values\r
1347 // it has written from its cache, but picodrive doesn't emulate caching.\r
1348 // move master memory area down by 0x100 bytes.\r
1349 // XXX replace this abominable hack. It might cause other problems in the game!\r
1350 if (PicoIn.quirks & PQUIRK_BLACKTHORNE_HACK) {\r
1351 int i;\r
1352 unsigned a = 0;\r
1353 for (i = 0; i < Pico.romsize; i += 4) {\r
1354 unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r
1355 if (a && v == a + 0x400) { // patch if 2 pointers with offset 0x400 are found\r
0aa63fce 1356 elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v - 0x100);\r
fe8f2d96 1357 *(u32 *) (Pico.rom + i) = CPU_BE2(v - 0x100);\r
1358 }\r
1359 // detect a pointer into the incriminating area\r
1360 a = 0;\r
1361 if (v >> 12 == 0x0603f000 >> 12 && !(v & 3))\r
1362 a = v;\r
1363 }\r
1364 }\r
1365\r
1366 // tweak for Mars Check Program: copies 32K longwords (128KB) from a 64KB buffer\r
1367 // in ROM or DRAM to SDRAM with DMA in 4-longword mode, overwriting an SDRAM comm\r
1368 // area in turn. This crashes the test on emulators without CPU cache emulation.\r
1369 // This may be a bug in Mars Check, since it's only checking for the 64KB result.\r
1370 // Patch the DMA transfers so that they transfer only 64KB.\r
1371 if (PicoIn.quirks & PQUIRK_MARSCHECK_HACK) {\r
1372 int i;\r
1373 unsigned a = 0;\r
1374 for (i = 0; i < Pico.romsize; i += 4) {\r
1375 unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r
1376 if (a == 0xffffff8c && v == 0x5ee1) { // patch if 4-long xfer written to CHCR\r
0aa63fce 1377 elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v & ~0x800);\r
fe8f2d96 1378 *(u32 *) (Pico.rom + i) = CPU_BE2(v & ~0x800); // change to half-sized xfer\r
1379 }\r
1380 a = v;\r
1381 }\r
1382 }\r
1dceadae 1383}\r
1384\r
b784d4a5 1385static void PicoCartDetectMS(void)\r
1386{\r
1387 memset(&Pico.sv, 0, sizeof(Pico.sv));\r
1388\r
1389 // Always map SRAM, since there's no indicator in ROM if it's needed or not\r
1390 // TODO: this should somehow be coming from a cart database!\r
1391\r
1392 Pico.sv.size = 0x8000; // Sega mapper, 2 banks of 16 KB each\r
1393 Pico.sv.flags |= SRF_ENABLED;\r
1394 Pico.sv.data = calloc(Pico.sv.size, 1);\r
1395 if (Pico.sv.data == NULL)\r
1396 Pico.sv.flags &= ~SRF_ENABLED;\r
1397}\r
005651ce 1398// vim:shiftwidth=2:expandtab\r