aaf91fff5274ba463dc35991c892062aa2a334d2
[picodrive.git] / pico / cart.c
1 /*\r
2  * PicoDrive\r
3  * (c) Copyright Dave, 2004\r
4  * (C) notaz, 2006-2010\r
5  * (C) irixxxx, 2020-2024\r
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
10 \r
11 #include "pico_int.h"\r
12 #include <cpu/debug.h>\r
13 \r
14 #if defined(USE_LIBCHDR)\r
15 #include "libchdr/chd.h"\r
16 #include "libchdr/cdrom.h"\r
17 #endif\r
18 \r
19 #include <unzip/unzip.h>\r
20 #include <zlib.h>\r
21 \r
22 static int rom_alloc_size;\r
23 static const char *rom_exts[] = { "bin", "gen", "smd", "md", "32x", "pco", "iso", "sms", "gg", "sg", "sc" };\r
24 \r
25 void (*PicoCartUnloadHook)(void);\r
26 void (*PicoCartMemSetup)(void);\r
27 \r
28 void (*PicoCartLoadProgressCB)(int percent) = NULL;\r
29 void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled in Pico/cd/cd_file.c\r
30 \r
31 int PicoGameLoaded;\r
32 \r
33 static void PicoCartDetect(const char *carthw_cfg);\r
34 static void PicoCartDetectMS(void);\r
35 \r
36 /* cso struct */\r
37 typedef 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
57 cso_struct;\r
58 \r
59 static int uncompress_buf(void *dest, int destLen, void *source, int sourceLen)\r
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
84 \r
85 static 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
99 struct 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
109 #if defined(USE_LIBCHDR)\r
110 struct 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
122 pm_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
128   if (path == NULL)\r
129     return NULL;\r
130 \r
131   ext = get_ext(path);\r
132   if (strcasecmp(ext, "zip") == 0)\r
133   {\r
134     struct zip_file *zfile = NULL;\r
135     struct zipent *zipentry;\r
136     ZIP *zipfile;\r
137     int i, ret;\r
138 \r
139     zipfile = openzip(path);\r
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
145         ext = get_ext(zipentry->name);\r
146 \r
147         if (zipentry->uncompressed_size >= 32*1024)\r
148           goto found_rom_zip;\r
149 \r
150         for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r
151           if (strcasecmp(ext, rom_exts[i]) == 0)\r
152             goto found_rom_zip;\r
153       }\r
154 \r
155       /* zipfile given, but nothing found suitable for us inside */\r
156       goto zip_failed;\r
157 \r
158 found_rom_zip:\r
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
178 \r
179 zip_failed:\r
180       closezip(zipfile);\r
181       free(zfile);\r
182       return NULL;\r
183     }\r
184   }\r
185   else if (strcasecmp(ext, "cso") == 0)\r
186   {\r
187     cso_struct *cso = NULL, *tmp = NULL;\r
188     int i, size;\r
189     f = fopen(path, "rb");\r
190     if (f == NULL)\r
191       goto cso_failed;\r
192 \r
193 #ifdef __GP2X__\r
194     /* we use our own buffering */\r
195     setvbuf(f, NULL, _IONBF, 0);\r
196 #endif\r
197 \r
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
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
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
230     for (i = 0; i < size/4; i++)\r
231       cso->index[i] = CPU_LE4(cso->index[i]);\r
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
237     file = calloc(1, sizeof(*file));\r
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
243     strncpy(file->ext, ext, sizeof(file->ext) - 1);\r
244     return file;\r
245 \r
246 cso_failed:\r
247     if (cso != NULL) free(cso);\r
248     if (f != NULL) fclose(f);\r
249     return NULL;\r
250   }\r
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
284     chd->file.size = head->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;\r
285     strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1);\r
286     return &chd->file;\r
287 \r
288 chd_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
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
300   file = calloc(1, sizeof(*file));\r
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
310   strncpy(file->ext, ext, sizeof(file->ext) - 1);\r
311   fseek(f, 0, SEEK_SET);\r
312 \r
313 #ifdef __GP2X__\r
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
319   return file;\r
320 }\r
321 \r
322 void 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
336 static 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
363 #if CPU_IS_LE\r
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
404 size_t pm_read(void *ptr, size_t bytes, pm_file *stream)\r
405 {\r
406   int ret;\r
407 \r
408   if (stream == NULL)\r
409     return -1;\r
410   else if (stream->type == PMT_UNCOMPRESSED)\r
411   {\r
412     ret = fread(ptr, 1, bytes, stream->file);\r
413   }\r
414   else if (stream->type == PMT_ZIP)\r
415   {\r
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
443   }\r
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
474             fseek(stream->file, read_pos, SEEK_SET);\r
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
483         rret = uncompress_buf(tmp_dst, 2048, cso->in_buff, read_len);\r
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
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
512   else\r
513     ret = 0;\r
514 \r
515   return ret;\r
516 }\r
517 \r
518 size_t pm_read_audio(void *ptr, size_t bytes, pm_file *stream)\r
519 {\r
520   if (stream == NULL)\r
521     return -1;\r
522 #if !(CPU_IS_LE)\r
523   else if (stream->type == PMT_UNCOMPRESSED)\r
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
548 int pm_seek(pm_file *stream, long offset, int whence)\r
549 {\r
550   if (stream == NULL)\r
551     return -1;\r
552   else if (stream->type == PMT_UNCOMPRESSED)\r
553   {\r
554     fseek(stream->file, offset, whence);\r
555     return ftell(stream->file);\r
556   }\r
557   else if (stream->type == PMT_ZIP)\r
558   {\r
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
586     if (PicoIn.osdMessage != NULL && offset > 4 * 1024 * 1024)\r
587       PicoIn.osdMessage("Decompressing data...");\r
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
596     }\r
597     return z->pos;\r
598   }\r
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
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
623   else\r
624     return -1;\r
625 }\r
626 \r
627 int 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
639     struct zip_file *z = fp->file;\r
640     inflateEnd(&z->stream);\r
641     closezip(z->zip);\r
642   }\r
643   else if (fp->type == PMT_CSO)\r
644   {\r
645     free(fp->param);\r
646     fclose(fp->file);\r
647   }\r
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
657   else\r
658     ret = EOF;\r
659 \r
660   free(fp);\r
661   return ret;\r
662 }\r
663 \r
664 // byteswap, data needs to be int aligned, src can match dst\r
665 void Byteswap(void *dst, const void *src, int len)\r
666 {\r
667 #if CPU_IS_LE\r
668   const unsigned int *ps = src;\r
669   unsigned int *pd = dst;\r
670   int i, m;\r
671 \r
672   if (len < 2)\r
673     return;\r
674 \r
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
679   }\r
680 #endif\r
681 }\r
682 \r
683 // Interleve a 16k block and byteswap\r
684 static int InterleveBlock(unsigned char *dest,unsigned char *src)\r
685 {\r
686   int i=0;\r
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
689   return 0;\r
690 }\r
691 \r
692 // Decode a SMD file\r
693 static 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
713 static unsigned char *PicoCartAlloc(int filesize, int is_sms)\r
714 {\r
715   unsigned char *rom;\r
716 \r
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
725   if (is_sms) {\r
726     // be sure we can cover all address space\r
727     if (rom_alloc_size < 0x10000)\r
728       rom_alloc_size = 0x10000;\r
729   }\r
730   else {\r
731     // align to 512K for memhandlers\r
732     rom_alloc_size = (rom_alloc_size + 0x7ffff) & ~0x7ffff;\r
733   }\r
734 \r
735   if (rom_alloc_size - filesize < 4)\r
736     rom_alloc_size += 4; // padding for out-of-bound exec protection\r
737 \r
738   // Allocate space for the rom plus padding\r
739   // use special address for 32x dynarec\r
740   rom = plat_mmap(0x02000000, rom_alloc_size, 0, 0);\r
741   return rom;\r
742 }\r
743 \r
744 int PicoCartLoad(pm_file *f, const unsigned char *rom, unsigned int romsize,\r
745   unsigned char **prom, unsigned int *psize, int is_sms)\r
746 {\r
747   unsigned char *rom_data = NULL;\r
748   int size, bytes_read;\r
749 \r
750   if (!f && !rom)\r
751     return 1;\r
752 \r
753   if (!rom)\r
754     size = f->size;\r
755   else\r
756     size = romsize;\r
757 \r
758   if (size <= 0) return 1;\r
759   size = (size+3)&~3; // Round up to a multiple of 4\r
760 \r
761   // Allocate space for the rom plus padding\r
762   rom_data = PicoCartAlloc(size, is_sms);\r
763   if (rom_data == NULL) {\r
764     elprintf(EL_STATUS, "out of memory (wanted %i)", size);\r
765     return 2;\r
766   }\r
767 \r
768   if (!rom) {\r
769     if (PicoCartLoadProgressCB != NULL)\r
770     {\r
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
793     }\r
794   }\r
795   else\r
796     memcpy(rom_data, rom, romsize);\r
797 \r
798   if (!is_sms)\r
799   {\r
800     // Check for SMD:\r
801     if (size >= 0x4200 && (size&0x3fff) == 0x200 &&\r
802         ((rom_data[0x2280] == 'S' && rom_data[0x280] == 'E') || (rom_data[0x280] == 'S' && rom_data[0x2281] == 'E'))) {\r
803       elprintf(EL_STATUS, "SMD format detected.");\r
804       DecodeSmd(rom_data,size); size-=0x200; // Decode and byteswap SMD\r
805     }\r
806     else Byteswap(rom_data, rom_data, size); // Just byteswap\r
807   }\r
808   else\r
809   {\r
810     if (size >= 0x4200 && (size&0x3fff) == 0x200) {\r
811       elprintf(EL_STATUS, "SMD format detected.");\r
812       // at least here it's not interleaved\r
813       size -= 0x200;\r
814       memmove(rom_data, rom_data + 0x200, size);\r
815     }\r
816   }\r
817 \r
818   if (prom)  *prom = rom_data;\r
819   if (psize) *psize = size;\r
820 \r
821   return 0;\r
822 }\r
823 \r
824 // Insert a cartridge:\r
825 int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg)\r
826 {\r
827   // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r
828   // This will hang the emu, but will prevent nasty crashes.\r
829   // note: 4 bytes are padded to every ROM\r
830   if (rom != NULL)\r
831     *(u32 *)(rom+romsize) = CPU_BE2(0x6000FFFE);\r
832 \r
833   Pico.rom=rom;\r
834   Pico.romsize=romsize;\r
835 \r
836   if (Pico.sv.data) {\r
837     free(Pico.sv.data);\r
838     Pico.sv.data = NULL;\r
839   }\r
840 \r
841   if (PicoCartUnloadHook != NULL) {\r
842     PicoCartUnloadHook();\r
843     PicoCartUnloadHook = NULL;\r
844   }\r
845   pdb_cleanup();\r
846 \r
847   PicoIn.AHW &= ~(PAHW_32X|PAHW_SVP);\r
848 \r
849   PicoCartMemSetup = NULL;\r
850   PicoDmaHook = NULL;\r
851   PicoResetHook = NULL;\r
852   PicoLineHook = NULL;\r
853   PicoLoadStateHook = NULL;\r
854   carthw_chunks = NULL;\r
855 \r
856   if (!(PicoIn.AHW & (PAHW_SMS|PAHW_PICO)))\r
857     PicoCartDetect(carthw_cfg);\r
858   if (PicoIn.AHW & PAHW_SMS)\r
859     PicoCartDetectMS();\r
860   if (PicoIn.AHW & PAHW_SVP)\r
861     PicoSVPStartup();\r
862   if (PicoIn.AHW & PAHW_PICO)\r
863     PicoInitPico();\r
864 \r
865   // setup correct memory map for loaded ROM\r
866   switch (PicoIn.AHW & ~(PAHW_GG|PAHW_SG|PAHW_SC)) {\r
867     default:\r
868       elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoIn.AHW);\r
869     case 0:\r
870     case PAHW_SVP:  PicoMemSetup(); break;\r
871     case PAHW_MCD:  PicoMemSetupCD(); break;\r
872     case PAHW_PICO: PicoMemSetupPico(); break;\r
873     case PAHW_SMS:  PicoMemSetupMS(); break;\r
874   }\r
875 \r
876   if (PicoCartMemSetup != NULL)\r
877     PicoCartMemSetup();\r
878 \r
879   if (PicoIn.AHW & PAHW_SMS)\r
880     PicoPowerMS();\r
881   else\r
882     PicoPower();\r
883 \r
884   PicoGameLoaded = 1;\r
885   return 0;\r
886 }\r
887 \r
888 int PicoCartResize(int newsize)\r
889 {\r
890   void *tmp = plat_mremap(Pico.rom, rom_alloc_size, newsize);\r
891   if (tmp == NULL)\r
892     return -1;\r
893 \r
894   Pico.rom = tmp;\r
895   rom_alloc_size = newsize;\r
896   return 0;\r
897 }\r
898 \r
899 void PicoCartUnload(void)\r
900 {\r
901   if (PicoCartUnloadHook != NULL) {\r
902     PicoCartUnloadHook();\r
903     PicoCartUnloadHook = NULL;\r
904   }\r
905 \r
906   PicoUnload32x();\r
907 \r
908   if (Pico.rom != NULL) {\r
909     SekFinishIdleDet();\r
910     plat_munmap(Pico.rom, rom_alloc_size);\r
911     Pico.rom = NULL;\r
912   }\r
913   PicoGameLoaded = 0;\r
914 }\r
915 \r
916 static unsigned int rom_crc32(int size)\r
917 {\r
918   unsigned int crc;\r
919   elprintf(EL_STATUS, "calculating CRC32..");\r
920   if (size <= 0 || size > Pico.romsize) size = Pico.romsize;\r
921 \r
922   // have to unbyteswap for calculation..\r
923   Byteswap(Pico.rom, Pico.rom, size);\r
924   crc = crc32(0, Pico.rom, size);\r
925   Byteswap(Pico.rom, Pico.rom, size);\r
926   return crc;\r
927 }\r
928 \r
929 int rom_strcmp(void *rom, int size, int offset, const char *s1)\r
930 {\r
931   int i, len = strlen(s1);\r
932   const char *s_rom = (const char *)rom;\r
933   if (offset + len > size)\r
934     return 1;\r
935 \r
936   if (PicoIn.AHW & PAHW_SMS)\r
937     return strncmp(s_rom + offset, s1, strlen(s1));\r
938 \r
939   for (i = 0; i < len; i++)\r
940     if (s1[i] != s_rom[MEM_BE2(i + offset)])\r
941       return 1;\r
942   return 0;\r
943 }\r
944 \r
945 static unsigned int rom_read32(int addr)\r
946 {\r
947   unsigned short *m = (unsigned short *)(Pico.rom + addr);\r
948   return (m[0] << 16) | m[1];\r
949 }\r
950 \r
951 static char *sskip(char *s)\r
952 {\r
953   while (*s && isspace_(*s))\r
954     s++;\r
955   return s;\r
956 }\r
957 \r
958 static void rstrip(char *s)\r
959 {\r
960   char *p;\r
961   for (p = s + strlen(s) - 1; p >= s; p--)\r
962     if (isspace_(*p))\r
963       *p = 0;\r
964 }\r
965 \r
966 static int parse_3_vals(char *p, int *val0, int *val1, int *val2)\r
967 {\r
968   char *r;\r
969   *val0 = strtoul(p, &r, 0);\r
970   if (r == p)\r
971     goto bad;\r
972   p = sskip(r);\r
973   if (*p++ != ',')\r
974     goto bad;\r
975   *val1 = strtoul(p, &r, 0);\r
976   if (r == p)\r
977     goto bad;\r
978   p = sskip(r);\r
979   if (*p++ != ',')\r
980     goto bad;\r
981   *val2 = strtoul(p, &r, 0);\r
982   if (r == p)\r
983     goto bad;\r
984 \r
985   return 1;\r
986 bad:\r
987   return 0;\r
988 }\r
989 \r
990 static int is_expr(const char *expr, char **pr)\r
991 {\r
992   int len = strlen(expr);\r
993   char *p = *pr;\r
994 \r
995   if (strncmp(expr, p, len) != 0)\r
996     return 0;\r
997   p = sskip(p + len);\r
998   if (*p != '=')\r
999     return 0; // wrong or malformed\r
1000 \r
1001   *pr = sskip(p + 1);\r
1002   return 1;\r
1003 }\r
1004 \r
1005 #include "carthw_cfg.c"\r
1006 \r
1007 static void parse_carthw(const char *carthw_cfg, int *fill_sram,\r
1008   int *hw_detected)\r
1009 {\r
1010   int line = 0, any_checks_passed = 0, skip_sect = 0;\r
1011   const char *s, *builtin = builtin_carthw_cfg;\r
1012   int tmp, rom_crc = 0;\r
1013   char buff[256], *p, *r;\r
1014   FILE *f;\r
1015 \r
1016   f = fopen(carthw_cfg, "r");\r
1017   if (f == NULL)\r
1018     f = fopen("pico/carthw.cfg", "r");\r
1019   if (f == NULL)\r
1020     elprintf(EL_STATUS, "couldn't open carthw.cfg!");\r
1021 \r
1022   for (;;)\r
1023   {\r
1024     if (f != NULL) {\r
1025       p = fgets(buff, sizeof(buff), f);\r
1026       if (p == NULL)\r
1027         break;\r
1028     }\r
1029     else {\r
1030       if (*builtin == 0)\r
1031         break;\r
1032       for (s = builtin; *s != 0 && *s != '\n'; s++)\r
1033         ;\r
1034       while (*s == '\n')\r
1035         s++;\r
1036       tmp = s - builtin;\r
1037       if (tmp > sizeof(buff) - 1)\r
1038         tmp = sizeof(buff) - 1;\r
1039       memcpy(buff, builtin, tmp);\r
1040       buff[tmp] = 0;\r
1041       p = buff;\r
1042       builtin = s;\r
1043     }\r
1044 \r
1045     line++;\r
1046     p = sskip(p);\r
1047     if (*p == 0 || *p == '#')\r
1048       continue;\r
1049 \r
1050     if (*p == '[') {\r
1051       any_checks_passed = 0;\r
1052       skip_sect = 0;\r
1053       continue;\r
1054     }\r
1055     \r
1056     if (skip_sect)\r
1057       continue;\r
1058 \r
1059     /* look for checks */\r
1060     if (is_expr("check_str", &p))\r
1061     {\r
1062       int offs;\r
1063       offs = strtoul(p, &r, 0);\r
1064       if (offs < 0) {\r
1065         elprintf(EL_STATUS, "carthw:%d: check_str offs out of range: %d\n", line, offs);\r
1066         goto bad;\r
1067       }\r
1068       p = sskip(r);\r
1069       if (*p != ',')\r
1070         goto bad;\r
1071       p = sskip(p + 1);\r
1072       if (*p != '"')\r
1073         goto bad;\r
1074       p++;\r
1075       r = strchr(p, '"');\r
1076       if (r == NULL)\r
1077         goto bad;\r
1078       *r = 0;\r
1079 \r
1080       if (rom_strcmp(Pico.rom, Pico.romsize, offs, p) == 0)\r
1081         any_checks_passed = 1;\r
1082       else\r
1083         skip_sect = 1;\r
1084       continue;\r
1085     }\r
1086     else if (is_expr("check_size_gt", &p))\r
1087     {\r
1088       int size;\r
1089       size = strtoul(p, &r, 0);\r
1090       if (r == p || size < 0)\r
1091         goto bad;\r
1092 \r
1093       if (Pico.romsize > size)\r
1094         any_checks_passed = 1;\r
1095       else\r
1096         skip_sect = 1;\r
1097       continue;\r
1098     }\r
1099     else if (is_expr("check_csum", &p))\r
1100     {\r
1101       int csum;\r
1102       csum = strtoul(p, &r, 0);\r
1103       if (r == p || (csum & 0xffff0000))\r
1104         goto bad;\r
1105 \r
1106       if (csum == (rom_read32(0x18c) & 0xffff))\r
1107         any_checks_passed = 1;\r
1108       else\r
1109         skip_sect = 1;\r
1110       continue;\r
1111     }\r
1112     else if (is_expr("check_crc32", &p))\r
1113     {\r
1114       unsigned int crc;\r
1115       crc = strtoul(p, &r, 0);\r
1116       if (r == p)\r
1117         goto bad;\r
1118 \r
1119       if (rom_crc == 0)\r
1120         rom_crc = rom_crc32(64*1024);\r
1121       if (crc == rom_crc)\r
1122         any_checks_passed = 1;\r
1123       else\r
1124         skip_sect = 1;\r
1125       continue;\r
1126     }\r
1127 \r
1128     /* now time for actions */\r
1129     if (is_expr("hw", &p)) {\r
1130       if (!any_checks_passed)\r
1131         goto no_checks;\r
1132       *hw_detected = 1;\r
1133       rstrip(p);\r
1134 \r
1135       if      (strcmp(p, "svp") == 0)\r
1136         PicoIn.AHW = PAHW_SVP;\r
1137       else if (strcmp(p, "pico") == 0)\r
1138         PicoIn.AHW = PAHW_PICO;\r
1139       else if (strcmp(p, "j_cart") == 0)\r
1140         carthw_jcart_startup();\r
1141       else if (strcmp(p, "prot") == 0)\r
1142         carthw_sprot_startup();\r
1143       else if (strcmp(p, "flash") == 0)\r
1144         carthw_flash_startup();\r
1145       else if (strcmp(p, "ssf2_mapper") == 0)\r
1146         carthw_ssf2_startup();\r
1147       else if (strcmp(p, "x_in_1_mapper") == 0)\r
1148         carthw_Xin1_startup();\r
1149       else if (strcmp(p, "realtec_mapper") == 0)\r
1150         carthw_realtec_startup();\r
1151       else if (strcmp(p, "radica_mapper") == 0)\r
1152         carthw_radica_startup();\r
1153       else if (strcmp(p, "piersolar_mapper") == 0)\r
1154         carthw_pier_startup();\r
1155       else if (strcmp(p, "sf001_mapper") == 0)\r
1156         carthw_sf001_startup();\r
1157       else if (strcmp(p, "sf002_mapper") == 0)\r
1158         carthw_sf002_startup();\r
1159       else if (strcmp(p, "sf004_mapper") == 0)\r
1160         carthw_sf004_startup();\r
1161       else if (strcmp(p, "lk3_mapper") == 0)\r
1162         carthw_lk3_startup();\r
1163       else if (strcmp(p, "smw64_mapper") == 0)\r
1164         carthw_smw64_startup();\r
1165       else {\r
1166         elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p);\r
1167         skip_sect = 1;\r
1168         *hw_detected = 0;\r
1169       }\r
1170       continue;\r
1171     }\r
1172     if (is_expr("sram_range", &p)) {\r
1173       int start, end;\r
1174 \r
1175       if (!any_checks_passed)\r
1176         goto no_checks;\r
1177       rstrip(p);\r
1178 \r
1179       start = strtoul(p, &r, 0);\r
1180       if (r == p)\r
1181         goto bad;\r
1182       p = sskip(r);\r
1183       if (*p != ',')\r
1184         goto bad;\r
1185       p = sskip(p + 1);\r
1186       end = strtoul(p, &r, 0);\r
1187       if (r == p)\r
1188         goto bad;\r
1189       if (((start | end) & 0xff000000) || start > end) {\r
1190         elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end);\r
1191         goto bad_nomsg;\r
1192       }\r
1193       Pico.sv.start = start;\r
1194       Pico.sv.end = end;\r
1195       continue;\r
1196     }\r
1197     else if (is_expr("prop", &p)) {\r
1198       if (!any_checks_passed)\r
1199         goto no_checks;\r
1200       rstrip(p);\r
1201 \r
1202       if      (strcmp(p, "no_sram") == 0)\r
1203         Pico.sv.flags &= ~SRF_ENABLED;\r
1204       else if (strcmp(p, "no_eeprom") == 0)\r
1205         Pico.sv.flags &= ~SRF_EEPROM;\r
1206       else if (strcmp(p, "filled_sram") == 0)\r
1207         *fill_sram = 1;\r
1208       else if (strcmp(p, "wwfraw_hack") == 0)\r
1209         PicoIn.quirks |= PQUIRK_WWFRAW_HACK;\r
1210       else if (strcmp(p, "blackthorne_hack") == 0)\r
1211         PicoIn.quirks |= PQUIRK_BLACKTHORNE_HACK;\r
1212       else if (strcmp(p, "marscheck_hack") == 0)\r
1213         PicoIn.quirks |= PQUIRK_MARSCHECK_HACK;\r
1214       else if (strcmp(p, "force_6btn") == 0)\r
1215         PicoIn.quirks |= PQUIRK_FORCE_6BTN;\r
1216       else if (strcmp(p, "no_z80_bus_lock") == 0)\r
1217         PicoIn.quirks |= PQUIRK_NO_Z80_BUS_LOCK;\r
1218       else {\r
1219         elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p);\r
1220         goto bad_nomsg;\r
1221       }\r
1222       elprintf(EL_STATUS, "game prop: %s", p);\r
1223       continue;\r
1224     }\r
1225     else if (is_expr("eeprom_type", &p)) {\r
1226       int type;\r
1227       if (!any_checks_passed)\r
1228         goto no_checks;\r
1229       rstrip(p);\r
1230 \r
1231       type = strtoul(p, &r, 0);\r
1232       if (r == p || type < 0)\r
1233         goto bad;\r
1234       Pico.sv.eeprom_type = type;\r
1235       Pico.sv.flags |= SRF_EEPROM;\r
1236       continue;\r
1237     }\r
1238     else if (is_expr("eeprom_lines", &p)) {\r
1239       int scl, sda_in, sda_out;\r
1240       if (!any_checks_passed)\r
1241         goto no_checks;\r
1242       rstrip(p);\r
1243 \r
1244       if (!parse_3_vals(p, &scl, &sda_in, &sda_out))\r
1245         goto bad;\r
1246       if (scl < 0 || scl > 15 || sda_in < 0 || sda_in > 15 ||\r
1247           sda_out < 0 || sda_out > 15)\r
1248         goto bad;\r
1249 \r
1250       Pico.sv.eeprom_bit_cl = scl;\r
1251       Pico.sv.eeprom_bit_in = sda_in;\r
1252       Pico.sv.eeprom_bit_out= sda_out;\r
1253       continue;\r
1254     }\r
1255     else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) {\r
1256       int addr, mask, val;\r
1257       if (!any_checks_passed)\r
1258         goto no_checks;\r
1259       rstrip(p);\r
1260 \r
1261       if (!parse_3_vals(p, &addr, &mask, &val))\r
1262         goto bad;\r
1263 \r
1264       carthw_sprot_new_location(addr, mask, val, tmp ? 1 : 0);\r
1265       continue;\r
1266     }\r
1267 \r
1268 \r
1269 bad:\r
1270     elprintf(EL_STATUS, "carthw:%d: unrecognized expression: %s", line, buff);\r
1271 bad_nomsg:\r
1272     skip_sect = 1;\r
1273     continue;\r
1274 \r
1275 no_checks:\r
1276     elprintf(EL_STATUS, "carthw:%d: command without any checks before it: %s", line, buff);\r
1277     skip_sect = 1;\r
1278     continue;\r
1279   }\r
1280 \r
1281   if (f != NULL)\r
1282     fclose(f);\r
1283 }\r
1284 \r
1285 /*\r
1286  * various cart-specific things, which can't be handled by generic code\r
1287  */\r
1288 static void PicoCartDetect(const char *carthw_cfg)\r
1289 {\r
1290   int carthw_detected = 0;\r
1291   int fill_sram = 0;\r
1292 \r
1293   memset(&Pico.sv, 0, sizeof(Pico.sv));\r
1294   if (Pico.rom[MEM_BE2(0x1B0)] == 'R' && Pico.rom[MEM_BE2(0x1B1)] == 'A')\r
1295   {\r
1296     Pico.sv.start =  rom_read32(0x1B4) & ~0xff000001; // align\r
1297     Pico.sv.end   = (rom_read32(0x1B8) & ~0xff000000) | 1;\r
1298     if (Pico.rom[MEM_BE2(0x1B3)] & 0x40)\r
1299       // EEPROM\r
1300       Pico.sv.flags |= SRF_EEPROM;\r
1301     Pico.sv.flags |= SRF_ENABLED;\r
1302   }\r
1303   if (Pico.sv.end == 0 || Pico.sv.start > Pico.sv.end)\r
1304   {\r
1305     // some games may have bad headers, like S&K and Sonic3\r
1306     // note: majority games use 0x200000 as starting address, but there are some which\r
1307     // use something else (0x300000 by HardBall '95). Luckily they have good headers.\r
1308     Pico.sv.start = 0x200000;\r
1309     Pico.sv.end   = 0x203FFF;\r
1310     Pico.sv.flags |= SRF_ENABLED;\r
1311   }\r
1312 \r
1313   // set EEPROM defaults, in case it gets detected\r
1314   Pico.sv.eeprom_type   = 0; // 7bit (24C01)\r
1315   Pico.sv.eeprom_bit_cl = 1;\r
1316   Pico.sv.eeprom_bit_in = 0;\r
1317   Pico.sv.eeprom_bit_out= 0;\r
1318 \r
1319   if (carthw_cfg != NULL)\r
1320     parse_carthw(carthw_cfg, &fill_sram, &carthw_detected);\r
1321 \r
1322   // assume the standard mapper for large roms\r
1323   if (!carthw_detected && Pico.romsize > 0x400000)\r
1324     carthw_ssf2_startup();\r
1325 \r
1326   if (Pico.sv.flags & SRF_ENABLED)\r
1327   {\r
1328     if (Pico.sv.flags & SRF_EEPROM)\r
1329       Pico.sv.size = 0x2000;\r
1330     else\r
1331       Pico.sv.size = Pico.sv.end - Pico.sv.start + 1;\r
1332 \r
1333     Pico.sv.data = calloc(Pico.sv.size, 1);\r
1334     if (Pico.sv.data == NULL)\r
1335       Pico.sv.flags &= ~SRF_ENABLED;\r
1336 \r
1337     if (Pico.sv.eeprom_type == 1)       // 1 == 0 in PD EEPROM code\r
1338       Pico.sv.eeprom_type = 0;\r
1339   }\r
1340 \r
1341   if ((Pico.sv.flags & SRF_ENABLED) && fill_sram)\r
1342   {\r
1343     elprintf(EL_STATUS, "SRAM fill");\r
1344     memset(Pico.sv.data, 0xff, Pico.sv.size);\r
1345   }\r
1346 \r
1347   // tweak for Blackthorne: master SH2 overwrites stack of slave SH2 being in PWM\r
1348   // interrupt. On real hardware, nothing happens since slave fetches the values\r
1349   // it has written from its cache, but picodrive doesn't emulate caching.\r
1350   // move master memory area down by 0x100 bytes.\r
1351   // XXX replace this abominable hack. It might cause other problems in the game!\r
1352   if (PicoIn.quirks & PQUIRK_BLACKTHORNE_HACK) {\r
1353     int i;\r
1354     unsigned a = 0;\r
1355     for (i = 0; i < Pico.romsize; i += 4) {\r
1356       unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r
1357       if (a && v == a + 0x400) { // patch if 2 pointers with offset 0x400 are found\r
1358         elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v - 0x100);\r
1359         *(u32 *) (Pico.rom + i) = CPU_BE2(v - 0x100);\r
1360       }\r
1361       // detect a pointer into the incriminating area\r
1362       a = 0;\r
1363       if (v >> 12 == 0x0603f000 >> 12 && !(v & 3))\r
1364         a = v;\r
1365     }\r
1366   }\r
1367 \r
1368   // tweak for Mars Check Program: copies 32K longwords (128KB) from a 64KB buffer\r
1369   // in ROM or DRAM to SDRAM with DMA in 4-longword mode, overwriting an SDRAM comm\r
1370   // area in turn. This crashes the test on emulators without CPU cache emulation.\r
1371   // This may be a bug in Mars Check, since it's only checking for the 64KB result.\r
1372   // Patch the DMA transfers so that they transfer only 64KB.\r
1373   if (PicoIn.quirks & PQUIRK_MARSCHECK_HACK) {\r
1374     int i;\r
1375     unsigned a = 0;\r
1376     for (i = 0; i < Pico.romsize; i += 4) {\r
1377       unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r
1378       if (a == 0xffffff8c && v == 0x5ee1) { // patch if 4-long xfer written to CHCR\r
1379         elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v & ~0x800);\r
1380         *(u32 *) (Pico.rom + i) = CPU_BE2(v & ~0x800); // change to half-sized xfer\r
1381       }\r
1382       a = v;\r
1383     }\r
1384   }\r
1385 }\r
1386 \r
1387 static void PicoCartDetectMS(void)\r
1388 {\r
1389   memset(&Pico.sv, 0, sizeof(Pico.sv));\r
1390 \r
1391   // Always map SRAM, since there's no indicator in ROM if it's needed or not\r
1392   // TODO: this should somehow be coming from a cart database!\r
1393 \r
1394   Pico.sv.size  = 0x8000; // Sega mapper, 2 banks of 16 KB each\r
1395   Pico.sv.flags |= SRF_ENABLED;\r
1396   Pico.sv.data = calloc(Pico.sv.size, 1);\r
1397   if (Pico.sv.data == NULL)\r
1398     Pico.sv.flags &= ~SRF_ENABLED;\r
1399 }\r
1400 // vim:shiftwidth=2:expandtab\r