32x: implement standard/ssf2 mapper
[picodrive.git] / pico / cart.c
1 /*\r
2  * PicoDrive\r
3  * (c) Copyright Dave, 2004\r
4  * (C) notaz, 2006-2010\r
5  *\r
6  * This work is licensed under the terms of MAME license.\r
7  * See COPYING file in the top-level directory.\r
8  */\r
9 \r
10 #include "pico_int.h"\r
11 #include "../cpu/debug.h"\r
12 #include "../unzip/unzip.h"\r
13 #include <zlib.h>\r
14 \r
15 \r
16 static int rom_alloc_size;\r
17 static const char *rom_exts[] = { "bin", "gen", "smd", "iso", "sms", "gg", "sg" };\r
18 \r
19 void (*PicoCartUnloadHook)(void);\r
20 void (*PicoCartMemSetup)(void);\r
21 \r
22 void (*PicoCartLoadProgressCB)(int percent) = NULL;\r
23 void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled in Pico/cd/cd_file.c\r
24 \r
25 int PicoGameLoaded;\r
26 \r
27 static void PicoCartDetect(const char *carthw_cfg);\r
28 \r
29 /* cso struct */\r
30 typedef struct _cso_struct\r
31 {\r
32   unsigned char in_buff[2*2048];\r
33   unsigned char out_buff[2048];\r
34   struct {\r
35     char          magic[4];\r
36     unsigned int  unused;\r
37     unsigned int  total_bytes;\r
38     unsigned int  total_bytes_high; // ignored here\r
39     unsigned int  block_size;  // 10h\r
40     unsigned char ver;\r
41     unsigned char align;\r
42     unsigned char reserved[2];\r
43   } header;\r
44   unsigned int  fpos_in;  // input file read pointer\r
45   unsigned int  fpos_out; // pos in virtual decompressed file\r
46   int block_in_buff;      // block which we have read in in_buff\r
47   int pad;\r
48   int index[0];\r
49 }\r
50 cso_struct;\r
51 \r
52 static int uncompress_buf(void *dest, int destLen, void *source, int sourceLen)\r
53 {\r
54     z_stream stream;\r
55     int err;\r
56 \r
57     stream.next_in = (Bytef*)source;\r
58     stream.avail_in = (uInt)sourceLen;\r
59     stream.next_out = dest;\r
60     stream.avail_out = (uInt)destLen;\r
61 \r
62     stream.zalloc = NULL;\r
63     stream.zfree = NULL;\r
64 \r
65     err = inflateInit2(&stream, -15);\r
66     if (err != Z_OK) return err;\r
67 \r
68     err = inflate(&stream, Z_FINISH);\r
69     if (err != Z_STREAM_END) {\r
70         inflateEnd(&stream);\r
71         return err;\r
72     }\r
73     //*destLen = stream.total_out;\r
74 \r
75     return inflateEnd(&stream);\r
76 }\r
77 \r
78 static const char *get_ext(const char *path)\r
79 {\r
80   const char *ext;\r
81   if (strlen(path) < 4)\r
82     return ""; // no ext\r
83 \r
84   // allow 2 or 3 char extensions for now\r
85   ext = path + strlen(path) - 2;\r
86   if (ext[-1] != '.') ext--;\r
87   if (ext[-1] != '.')\r
88     return "";\r
89   return ext;\r
90 }\r
91 \r
92 struct zip_file {\r
93   pm_file file;\r
94   ZIP *zip;\r
95   struct zipent *entry;\r
96   z_stream stream;\r
97   unsigned char inbuf[16384];\r
98   long start;\r
99   unsigned int pos;\r
100 };\r
101 \r
102 pm_file *pm_open(const char *path)\r
103 {\r
104   pm_file *file = NULL;\r
105   const char *ext;\r
106   FILE *f;\r
107 \r
108   if (path == NULL)\r
109     return NULL;\r
110 \r
111   ext = get_ext(path);\r
112   if (strcasecmp(ext, "zip") == 0)\r
113   {\r
114     struct zip_file *zfile = NULL;\r
115     struct zipent *zipentry;\r
116     ZIP *zipfile;\r
117     int i, ret;\r
118 \r
119     zipfile = openzip(path);\r
120     if (zipfile != NULL)\r
121     {\r
122       /* search for suitable file (right extension or large enough file) */\r
123       while ((zipentry = readzip(zipfile)) != NULL)\r
124       {\r
125         ext = get_ext(zipentry->name);\r
126 \r
127         if (zipentry->uncompressed_size >= 32*1024)\r
128           goto found_rom_zip;\r
129 \r
130         for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r
131           if (strcasecmp(ext, rom_exts[i]) == 0)\r
132             goto found_rom_zip;\r
133       }\r
134 \r
135       /* zipfile given, but nothing found suitable for us inside */\r
136       goto zip_failed;\r
137 \r
138 found_rom_zip:\r
139       zfile = calloc(1, sizeof(*zfile));\r
140       if (zfile == NULL)\r
141         goto zip_failed;\r
142       ret = seekcompresszip(zipfile, zipentry);\r
143       if (ret != 0)\r
144         goto zip_failed;\r
145       ret = inflateInit2(&zfile->stream, -15);\r
146       if (ret != Z_OK) {\r
147         elprintf(EL_STATUS, "zip: inflateInit2 %d", ret);\r
148         goto zip_failed;\r
149       }\r
150       zfile->zip = zipfile;\r
151       zfile->entry = zipentry;\r
152       zfile->start = ftell(zipfile->fp);\r
153       zfile->file.file = zfile;\r
154       zfile->file.size = zipentry->uncompressed_size;\r
155       zfile->file.type = PMT_ZIP;\r
156       strncpy(zfile->file.ext, ext, sizeof(zfile->file.ext) - 1);\r
157       return &zfile->file;\r
158 \r
159 zip_failed:\r
160       closezip(zipfile);\r
161       free(zfile);\r
162       return NULL;\r
163     }\r
164   }\r
165   else if (strcasecmp(ext, "cso") == 0)\r
166   {\r
167     cso_struct *cso = NULL, *tmp = NULL;\r
168     int size;\r
169     f = fopen(path, "rb");\r
170     if (f == NULL)\r
171       goto cso_failed;\r
172 \r
173 #ifdef __GP2X__\r
174     /* we use our own buffering */\r
175     setvbuf(f, NULL, _IONBF, 0);\r
176 #endif\r
177 \r
178     cso = malloc(sizeof(*cso));\r
179     if (cso == NULL)\r
180       goto cso_failed;\r
181 \r
182     if (fread(&cso->header, 1, sizeof(cso->header), f) != sizeof(cso->header))\r
183       goto cso_failed;\r
184 \r
185     if (strncmp(cso->header.magic, "CISO", 4) != 0) {\r
186       elprintf(EL_STATUS, "cso: bad header");\r
187       goto cso_failed;\r
188     }\r
189 \r
190     if (cso->header.block_size != 2048) {\r
191       elprintf(EL_STATUS, "cso: bad block size (%u)", cso->header.block_size);\r
192       goto cso_failed;\r
193     }\r
194 \r
195     size = ((cso->header.total_bytes >> 11) + 1)*4 + sizeof(*cso);\r
196     tmp = realloc(cso, size);\r
197     if (tmp == NULL)\r
198       goto cso_failed;\r
199     cso = tmp;\r
200     elprintf(EL_STATUS, "allocated %i bytes for CSO struct", size);\r
201 \r
202     size -= sizeof(*cso); // index size\r
203     if (fread(cso->index, 1, size, f) != size) {\r
204       elprintf(EL_STATUS, "cso: premature EOF");\r
205       goto cso_failed;\r
206     }\r
207 \r
208     // all ok\r
209     cso->fpos_in = ftell(f);\r
210     cso->fpos_out = 0;\r
211     cso->block_in_buff = -1;\r
212     file = calloc(1, sizeof(*file));\r
213     if (file == NULL) goto cso_failed;\r
214     file->file  = f;\r
215     file->param = cso;\r
216     file->size  = cso->header.total_bytes;\r
217     file->type  = PMT_CSO;\r
218     return file;\r
219 \r
220 cso_failed:\r
221     if (cso != NULL) free(cso);\r
222     if (f != NULL) fclose(f);\r
223     return NULL;\r
224   }\r
225 \r
226   /* not a zip, treat as uncompressed file */\r
227   f = fopen(path, "rb");\r
228   if (f == NULL) return NULL;\r
229 \r
230   file = calloc(1, sizeof(*file));\r
231   if (file == NULL) {\r
232     fclose(f);\r
233     return NULL;\r
234   }\r
235   fseek(f, 0, SEEK_END);\r
236   file->file  = f;\r
237   file->param = NULL;\r
238   file->size  = ftell(f);\r
239   file->type  = PMT_UNCOMPRESSED;\r
240   strncpy(file->ext, ext, sizeof(file->ext) - 1);\r
241   fseek(f, 0, SEEK_SET);\r
242 \r
243 #ifdef __GP2X__\r
244   if (file->size > 0x400000)\r
245     /* we use our own buffering */\r
246     setvbuf(f, NULL, _IONBF, 0);\r
247 #endif\r
248 \r
249   return file;\r
250 }\r
251 \r
252 size_t pm_read(void *ptr, size_t bytes, pm_file *stream)\r
253 {\r
254   int ret;\r
255 \r
256   if (stream->type == PMT_UNCOMPRESSED)\r
257   {\r
258     ret = fread(ptr, 1, bytes, stream->file);\r
259   }\r
260   else if (stream->type == PMT_ZIP)\r
261   {\r
262     struct zip_file *z = stream->file;\r
263 \r
264     if (z->entry->compression_method == 0) {\r
265       int ret = fread(ptr, 1, bytes, z->zip->fp);\r
266       z->pos += ret;\r
267       return ret;\r
268     }\r
269 \r
270     z->stream.next_out = ptr;\r
271     z->stream.avail_out = bytes;\r
272     while (z->stream.avail_out != 0) {\r
273       if (z->stream.avail_in == 0) {\r
274         z->stream.avail_in = fread(z->inbuf, 1, sizeof(z->inbuf), z->zip->fp);\r
275         if (z->stream.avail_in == 0)\r
276           break;\r
277         z->stream.next_in = z->inbuf;\r
278       }\r
279       ret = inflate(&z->stream, Z_NO_FLUSH);\r
280       if (ret == Z_STREAM_END)\r
281         break;\r
282       if (ret != Z_OK) {\r
283         elprintf(EL_STATUS, "zip: inflate: %d", ret);\r
284         return 0;\r
285       }\r
286     }\r
287     z->pos += bytes - z->stream.avail_out;\r
288     return bytes - z->stream.avail_out;\r
289   }\r
290   else if (stream->type == PMT_CSO)\r
291   {\r
292     cso_struct *cso = stream->param;\r
293     int read_pos, read_len, out_offs, rret;\r
294     int block = cso->fpos_out >> 11;\r
295     int index = cso->index[block];\r
296     int index_end = cso->index[block+1];\r
297     unsigned char *out = ptr, *tmp_dst;\r
298 \r
299     ret = 0;\r
300     while (bytes != 0)\r
301     {\r
302       out_offs = cso->fpos_out&0x7ff;\r
303       if (out_offs == 0 && bytes >= 2048)\r
304            tmp_dst = out;\r
305       else tmp_dst = cso->out_buff;\r
306 \r
307       read_pos = (index&0x7fffffff) << cso->header.align;\r
308 \r
309       if (index < 0) {\r
310         if (read_pos != cso->fpos_in)\r
311           fseek(stream->file, read_pos, SEEK_SET);\r
312         rret = fread(tmp_dst, 1, 2048, stream->file);\r
313         cso->fpos_in = read_pos + rret;\r
314         if (rret != 2048) break;\r
315       } else {\r
316         read_len = (((index_end&0x7fffffff) << cso->header.align) - read_pos) & 0xfff;\r
317         if (block != cso->block_in_buff)\r
318         {\r
319           if (read_pos != cso->fpos_in)\r
320             fseek(stream->file, read_pos, SEEK_SET);\r
321           rret = fread(cso->in_buff, 1, read_len, stream->file);\r
322           cso->fpos_in = read_pos + rret;\r
323           if (rret != read_len) {\r
324             elprintf(EL_STATUS, "cso: read failed @ %08x", read_pos);\r
325             break;\r
326           }\r
327           cso->block_in_buff = block;\r
328         }\r
329         rret = uncompress_buf(tmp_dst, 2048, cso->in_buff, read_len);\r
330         if (rret != 0) {\r
331           elprintf(EL_STATUS, "cso: uncompress failed @ %08x with %i", read_pos, rret);\r
332           break;\r
333         }\r
334       }\r
335 \r
336       rret = 2048;\r
337       if (out_offs != 0 || bytes < 2048) {\r
338         //elprintf(EL_STATUS, "cso: unaligned/nonfull @ %08x, offs=%i, len=%u", cso->fpos_out, out_offs, bytes);\r
339         if (bytes < rret) rret = bytes;\r
340         if (2048 - out_offs < rret) rret = 2048 - out_offs;\r
341         memcpy(out, tmp_dst + out_offs, rret);\r
342       }\r
343       ret += rret;\r
344       out += rret;\r
345       cso->fpos_out += rret;\r
346       bytes -= rret;\r
347       block++;\r
348       index = index_end;\r
349       index_end = cso->index[block+1];\r
350     }\r
351   }\r
352   else\r
353     ret = 0;\r
354 \r
355   return ret;\r
356 }\r
357 \r
358 int pm_seek(pm_file *stream, long offset, int whence)\r
359 {\r
360   if (stream->type == PMT_UNCOMPRESSED)\r
361   {\r
362     fseek(stream->file, offset, whence);\r
363     return ftell(stream->file);\r
364   }\r
365   else if (stream->type == PMT_ZIP)\r
366   {\r
367     struct zip_file *z = stream->file;\r
368     unsigned int pos = z->pos;\r
369     int ret;\r
370 \r
371     switch (whence)\r
372     {\r
373       case SEEK_CUR: pos += offset; break;\r
374       case SEEK_SET: pos  = offset; break;\r
375       case SEEK_END: pos  = stream->size - offset; break;\r
376     }\r
377     if (z->entry->compression_method == 0) {\r
378       ret = fseek(z->zip->fp, z->start + pos, SEEK_SET);\r
379       if (ret == 0)\r
380         return (z->pos = pos);\r
381       return -1;\r
382     }\r
383     offset = pos - z->pos;\r
384     if (pos < z->pos) {\r
385       // full decompress from the start\r
386       fseek(z->zip->fp, z->start, SEEK_SET);\r
387       z->stream.avail_in = 0;\r
388       z->stream.next_in = z->inbuf;\r
389       inflateReset(&z->stream);\r
390       z->pos = 0;\r
391       offset = pos;\r
392     }\r
393 \r
394     if (PicoIn.osdMessage != NULL && offset > 4 * 1024 * 1024)\r
395       PicoIn.osdMessage("Decompressing data...");\r
396 \r
397     while (offset > 0) {\r
398       char buf[16 * 1024];\r
399       size_t l = offset > sizeof(buf) ? sizeof(buf) : offset;\r
400       ret = pm_read(buf, l, stream);\r
401       if (ret != l)\r
402         break;\r
403       offset -= l;\r
404     }\r
405     return z->pos;\r
406   }\r
407   else if (stream->type == PMT_CSO)\r
408   {\r
409     cso_struct *cso = stream->param;\r
410     switch (whence)\r
411     {\r
412       case SEEK_CUR: cso->fpos_out += offset; break;\r
413       case SEEK_SET: cso->fpos_out  = offset; break;\r
414       case SEEK_END: cso->fpos_out  = cso->header.total_bytes - offset; break;\r
415     }\r
416     return cso->fpos_out;\r
417   }\r
418   else\r
419     return -1;\r
420 }\r
421 \r
422 int pm_close(pm_file *fp)\r
423 {\r
424   int ret = 0;\r
425 \r
426   if (fp == NULL) return EOF;\r
427 \r
428   if (fp->type == PMT_UNCOMPRESSED)\r
429   {\r
430     fclose(fp->file);\r
431   }\r
432   else if (fp->type == PMT_ZIP)\r
433   {\r
434     struct zip_file *z = fp->file;\r
435     inflateEnd(&z->stream);\r
436     closezip(z->zip);\r
437   }\r
438   else if (fp->type == PMT_CSO)\r
439   {\r
440     free(fp->param);\r
441     fclose(fp->file);\r
442   }\r
443   else\r
444     ret = EOF;\r
445 \r
446   free(fp);\r
447   return ret;\r
448 }\r
449 \r
450 // byteswap, data needs to be int aligned, src can match dst\r
451 void Byteswap(void *dst, const void *src, int len)\r
452 {\r
453   const unsigned int *ps = src;\r
454   unsigned int *pd = dst;\r
455   int i, m;\r
456 \r
457   if (len < 2)\r
458     return;\r
459 \r
460   m = 0x00ff00ff;\r
461   for (i = 0; i < len / 4; i++) {\r
462     unsigned int t = ps[i];\r
463     pd[i] = ((t & m) << 8) | ((t & ~m) >> 8);\r
464   }\r
465 }\r
466 \r
467 // Interleve a 16k block and byteswap\r
468 static int InterleveBlock(unsigned char *dest,unsigned char *src)\r
469 {\r
470   int i=0;\r
471   for (i=0;i<0x2000;i++) dest[(i<<1)  ]=src[       i]; // Odd\r
472   for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even\r
473   return 0;\r
474 }\r
475 \r
476 // Decode a SMD file\r
477 static int DecodeSmd(unsigned char *data,int len)\r
478 {\r
479   unsigned char *temp=NULL;\r
480   int i=0;\r
481 \r
482   temp=(unsigned char *)malloc(0x4000);\r
483   if (temp==NULL) return 1;\r
484   memset(temp,0,0x4000);\r
485 \r
486   // Interleve each 16k block and shift down by 0x200:\r
487   for (i=0; i+0x4200<=len; i+=0x4000)\r
488   {\r
489     InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer\r
490     memcpy(data+i,temp,0x4000); // Copy back in\r
491   }\r
492 \r
493   free(temp);\r
494   return 0;\r
495 }\r
496 \r
497 static unsigned char *PicoCartAlloc(int filesize, int is_sms)\r
498 {\r
499   unsigned char *rom;\r
500 \r
501   if (is_sms) {\r
502     // make size power of 2 for easier banking handling\r
503     int s = 0, tmp = filesize;\r
504     while ((tmp >>= 1) != 0)\r
505       s++;\r
506     if (filesize > (1 << s))\r
507       s++;\r
508     rom_alloc_size = 1 << s;\r
509     // be sure we can cover all address space\r
510     if (rom_alloc_size < 0x10000)\r
511       rom_alloc_size = 0x10000;\r
512   }\r
513   else {\r
514     // make alloc size at least sizeof(mcd_state),\r
515     // in case we want to switch to CD mode\r
516     if (filesize < sizeof(mcd_state))\r
517       filesize = sizeof(mcd_state);\r
518 \r
519     // align to 512K for memhandlers\r
520     rom_alloc_size = (filesize + 0x7ffff) & ~0x7ffff;\r
521   }\r
522 \r
523   if (rom_alloc_size - filesize < 4)\r
524     rom_alloc_size += 4; // padding for out-of-bound exec protection\r
525 \r
526   // Allocate space for the rom plus padding\r
527   // use special address for 32x dynarec\r
528   rom = plat_mmap(0x02000000, rom_alloc_size, 0, 0);\r
529   return rom;\r
530 }\r
531 \r
532 int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize,int is_sms)\r
533 {\r
534   unsigned char *rom;\r
535   int size, bytes_read;\r
536 \r
537   if (f == NULL)\r
538     return 1;\r
539 \r
540   size = f->size;\r
541   if (size <= 0) return 1;\r
542   size = (size+3)&~3; // Round up to a multiple of 4\r
543 \r
544   // Allocate space for the rom plus padding\r
545   rom = PicoCartAlloc(size, is_sms);\r
546   if (rom == NULL) {\r
547     elprintf(EL_STATUS, "out of memory (wanted %i)", size);\r
548     return 2;\r
549   }\r
550 \r
551   if (PicoCartLoadProgressCB != NULL)\r
552   {\r
553     // read ROM in blocks, just for fun\r
554     int ret;\r
555     unsigned char *p = rom;\r
556     bytes_read=0;\r
557     do\r
558     {\r
559       int todo = size - bytes_read;\r
560       if (todo > 256*1024) todo = 256*1024;\r
561       ret = pm_read(p,todo,f);\r
562       bytes_read += ret;\r
563       p += ret;\r
564       PicoCartLoadProgressCB(bytes_read * 100 / size);\r
565     }\r
566     while (ret > 0);\r
567   }\r
568   else\r
569     bytes_read = pm_read(rom,size,f); // Load up the rom\r
570   if (bytes_read <= 0) {\r
571     elprintf(EL_STATUS, "read failed");\r
572     plat_munmap(rom, rom_alloc_size);\r
573     return 3;\r
574   }\r
575 \r
576   if (!is_sms)\r
577   {\r
578     // maybe we are loading MegaCD BIOS?\r
579     if (!(PicoIn.AHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom+0x124, "BOOT", 4) ||\r
580          !strncmp((char *)rom+0x128, "BOOT", 4))) {\r
581       PicoIn.AHW |= PAHW_MCD;\r
582     }\r
583 \r
584     // Check for SMD:\r
585     if (size >= 0x4200 && (size&0x3fff) == 0x200 &&\r
586         ((rom[0x2280] == 'S' && rom[0x280] == 'E') || (rom[0x280] == 'S' && rom[0x2281] == 'E'))) {\r
587       elprintf(EL_STATUS, "SMD format detected.");\r
588       DecodeSmd(rom,size); size-=0x200; // Decode and byteswap SMD\r
589     }\r
590     else Byteswap(rom, rom, size); // Just byteswap\r
591   }\r
592   else\r
593   {\r
594     if (size >= 0x4200 && (size&0x3fff) == 0x200) {\r
595       elprintf(EL_STATUS, "SMD format detected.");\r
596       // at least here it's not interleaved\r
597       size -= 0x200;\r
598       memmove(rom, rom + 0x200, size);\r
599     }\r
600   }\r
601 \r
602   if (prom)  *prom = rom;\r
603   if (psize) *psize = size;\r
604 \r
605   return 0;\r
606 }\r
607 \r
608 // Insert a cartridge:\r
609 int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg)\r
610 {\r
611   // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r
612   // This will hang the emu, but will prevent nasty crashes.\r
613   // note: 4 bytes are padded to every ROM\r
614   if (rom != NULL)\r
615     *(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped\r
616 \r
617   Pico.rom=rom;\r
618   Pico.romsize=romsize;\r
619 \r
620   if (Pico.sv.data) {\r
621     free(Pico.sv.data);\r
622     Pico.sv.data = NULL;\r
623   }\r
624 \r
625   if (PicoCartUnloadHook != NULL) {\r
626     PicoCartUnloadHook();\r
627     PicoCartUnloadHook = NULL;\r
628   }\r
629   pdb_cleanup();\r
630 \r
631   PicoIn.AHW &= PAHW_MCD|PAHW_SMS;\r
632 \r
633   PicoCartMemSetup = NULL;\r
634   PicoDmaHook = NULL;\r
635   PicoResetHook = NULL;\r
636   PicoLineHook = NULL;\r
637   PicoLoadStateHook = NULL;\r
638   carthw_chunks = NULL;\r
639 \r
640   if (!(PicoIn.AHW & (PAHW_MCD|PAHW_SMS)))\r
641     PicoCartDetect(carthw_cfg);\r
642 \r
643   // setup correct memory map for loaded ROM\r
644   switch (PicoIn.AHW) {\r
645     default:\r
646       elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoIn.AHW);\r
647     case 0:\r
648     case PAHW_SVP:  PicoMemSetup(); break;\r
649     case PAHW_MCD:  PicoMemSetupCD(); break;\r
650     case PAHW_PICO: PicoMemSetupPico(); break;\r
651     case PAHW_SMS:  PicoMemSetupMS(); break;\r
652   }\r
653 \r
654   if (PicoCartMemSetup != NULL)\r
655     PicoCartMemSetup();\r
656 \r
657   if (PicoIn.AHW & PAHW_SMS)\r
658     PicoPowerMS();\r
659   else\r
660     PicoPower();\r
661 \r
662   PicoGameLoaded = 1;\r
663   return 0;\r
664 }\r
665 \r
666 int PicoCartResize(int newsize)\r
667 {\r
668   void *tmp = plat_mremap(Pico.rom, rom_alloc_size, newsize);\r
669   if (tmp == NULL)\r
670     return -1;\r
671 \r
672   Pico.rom = tmp;\r
673   rom_alloc_size = newsize;\r
674   return 0;\r
675 }\r
676 \r
677 void PicoCartUnload(void)\r
678 {\r
679   if (PicoCartUnloadHook != NULL) {\r
680     PicoCartUnloadHook();\r
681     PicoCartUnloadHook = NULL;\r
682   }\r
683 \r
684   if (PicoIn.AHW & PAHW_32X)\r
685     PicoUnload32x();\r
686 \r
687   if (Pico.rom != NULL) {\r
688     SekFinishIdleDet();\r
689     plat_munmap(Pico.rom, rom_alloc_size);\r
690     Pico.rom = NULL;\r
691   }\r
692   PicoGameLoaded = 0;\r
693 }\r
694 \r
695 static unsigned int rom_crc32(void)\r
696 {\r
697   unsigned int crc;\r
698   elprintf(EL_STATUS, "caclulating CRC32..");\r
699 \r
700   // have to unbyteswap for calculation..\r
701   Byteswap(Pico.rom, Pico.rom, Pico.romsize);\r
702   crc = crc32(0, Pico.rom, Pico.romsize);\r
703   Byteswap(Pico.rom, Pico.rom, Pico.romsize);\r
704   return crc;\r
705 }\r
706 \r
707 static int rom_strcmp(int rom_offset, const char *s1)\r
708 {\r
709   int i, len = strlen(s1);\r
710   const char *s_rom = (const char *)Pico.rom;\r
711   if (rom_offset + len > Pico.romsize)\r
712     return 0;\r
713   for (i = 0; i < len; i++)\r
714     if (s1[i] != s_rom[(i + rom_offset) ^ 1])\r
715       return 1;\r
716   return 0;\r
717 }\r
718 \r
719 static unsigned int rom_read32(int addr)\r
720 {\r
721   unsigned short *m = (unsigned short *)(Pico.rom + addr);\r
722   return (m[0] << 16) | m[1];\r
723 }\r
724 \r
725 static char *sskip(char *s)\r
726 {\r
727   while (*s && isspace_(*s))\r
728     s++;\r
729   return s;\r
730 }\r
731 \r
732 static void rstrip(char *s)\r
733 {\r
734   char *p;\r
735   for (p = s + strlen(s) - 1; p >= s; p--)\r
736     if (isspace_(*p))\r
737       *p = 0;\r
738 }\r
739 \r
740 static int parse_3_vals(char *p, int *val0, int *val1, int *val2)\r
741 {\r
742   char *r;\r
743   *val0 = strtoul(p, &r, 0);\r
744   if (r == p)\r
745     goto bad;\r
746   p = sskip(r);\r
747   if (*p++ != ',')\r
748     goto bad;\r
749   *val1 = strtoul(p, &r, 0);\r
750   if (r == p)\r
751     goto bad;\r
752   p = sskip(r);\r
753   if (*p++ != ',')\r
754     goto bad;\r
755   *val2 = strtoul(p, &r, 0);\r
756   if (r == p)\r
757     goto bad;\r
758 \r
759   return 1;\r
760 bad:\r
761   return 0;\r
762 }\r
763 \r
764 static int is_expr(const char *expr, char **pr)\r
765 {\r
766   int len = strlen(expr);\r
767   char *p = *pr;\r
768 \r
769   if (strncmp(expr, p, len) != 0)\r
770     return 0;\r
771   p = sskip(p + len);\r
772   if (*p != '=')\r
773     return 0; // wrong or malformed\r
774 \r
775   *pr = sskip(p + 1);\r
776   return 1;\r
777 }\r
778 \r
779 #include "carthw_cfg.c"\r
780 \r
781 static void parse_carthw(const char *carthw_cfg, int *fill_sram,\r
782   int *hw_detected)\r
783 {\r
784   int line = 0, any_checks_passed = 0, skip_sect = 0;\r
785   const char *s, *builtin = builtin_carthw_cfg;\r
786   int tmp, rom_crc = 0;\r
787   char buff[256], *p, *r;\r
788   FILE *f;\r
789 \r
790   f = fopen(carthw_cfg, "r");\r
791   if (f == NULL)\r
792     f = fopen("pico/carthw.cfg", "r");\r
793   if (f == NULL)\r
794     elprintf(EL_STATUS, "couldn't open carthw.cfg!");\r
795 \r
796   for (;;)\r
797   {\r
798     if (f != NULL) {\r
799       p = fgets(buff, sizeof(buff), f);\r
800       if (p == NULL)\r
801         break;\r
802     }\r
803     else {\r
804       if (*builtin == 0)\r
805         break;\r
806       for (s = builtin; *s != 0 && *s != '\n'; s++)\r
807         ;\r
808       while (*s == '\n')\r
809         s++;\r
810       tmp = s - builtin;\r
811       if (tmp > sizeof(buff) - 1)\r
812         tmp = sizeof(buff) - 1;\r
813       memcpy(buff, builtin, tmp);\r
814       buff[tmp] = 0;\r
815       p = buff;\r
816       builtin = s;\r
817     }\r
818 \r
819     line++;\r
820     p = sskip(p);\r
821     if (*p == 0 || *p == '#')\r
822       continue;\r
823 \r
824     if (*p == '[') {\r
825       any_checks_passed = 0;\r
826       skip_sect = 0;\r
827       continue;\r
828     }\r
829     \r
830     if (skip_sect)\r
831       continue;\r
832 \r
833     /* look for checks */\r
834     if (is_expr("check_str", &p))\r
835     {\r
836       int offs;\r
837       offs = strtoul(p, &r, 0);\r
838       if (offs < 0 || offs > Pico.romsize) {\r
839         elprintf(EL_STATUS, "carthw:%d: check_str offs out of range: %d\n", line, offs);\r
840         goto bad;\r
841       }\r
842       p = sskip(r);\r
843       if (*p != ',')\r
844         goto bad;\r
845       p = sskip(p + 1);\r
846       if (*p != '"')\r
847         goto bad;\r
848       p++;\r
849       r = strchr(p, '"');\r
850       if (r == NULL)\r
851         goto bad;\r
852       *r = 0;\r
853 \r
854       if (rom_strcmp(offs, p) == 0)\r
855         any_checks_passed = 1;\r
856       else\r
857         skip_sect = 1;\r
858       continue;\r
859     }\r
860     else if (is_expr("check_size_gt", &p))\r
861     {\r
862       int size;\r
863       size = strtoul(p, &r, 0);\r
864       if (r == p || size < 0)\r
865         goto bad;\r
866 \r
867       if (Pico.romsize > size)\r
868         any_checks_passed = 1;\r
869       else\r
870         skip_sect = 1;\r
871       continue;\r
872     }\r
873     else if (is_expr("check_csum", &p))\r
874     {\r
875       int csum;\r
876       csum = strtoul(p, &r, 0);\r
877       if (r == p || (csum & 0xffff0000))\r
878         goto bad;\r
879 \r
880       if (csum == (rom_read32(0x18c) & 0xffff))\r
881         any_checks_passed = 1;\r
882       else\r
883         skip_sect = 1;\r
884       continue;\r
885     }\r
886     else if (is_expr("check_crc32", &p))\r
887     {\r
888       unsigned int crc;\r
889       crc = strtoul(p, &r, 0);\r
890       if (r == p)\r
891         goto bad;\r
892 \r
893       if (rom_crc == 0)\r
894         rom_crc = rom_crc32();\r
895       if (crc == rom_crc)\r
896         any_checks_passed = 1;\r
897       else\r
898         skip_sect = 1;\r
899       continue;\r
900     }\r
901 \r
902     /* now time for actions */\r
903     if (is_expr("hw", &p)) {\r
904       if (!any_checks_passed)\r
905         goto no_checks;\r
906       *hw_detected = 1;\r
907       rstrip(p);\r
908 \r
909       if      (strcmp(p, "svp") == 0)\r
910         PicoSVPStartup();\r
911       else if (strcmp(p, "pico") == 0)\r
912         PicoInitPico();\r
913       else if (strcmp(p, "prot") == 0)\r
914         carthw_sprot_startup();\r
915       else if (strcmp(p, "ssf2_mapper") == 0)\r
916         carthw_ssf2_startup();\r
917       else if (strcmp(p, "x_in_1_mapper") == 0)\r
918         carthw_Xin1_startup();\r
919       else if (strcmp(p, "realtec_mapper") == 0)\r
920         carthw_realtec_startup();\r
921       else if (strcmp(p, "radica_mapper") == 0)\r
922         carthw_radica_startup();\r
923       else if (strcmp(p, "piersolar_mapper") == 0)\r
924         carthw_pier_startup();\r
925       else if (strcmp(p, "prot_lk3") == 0)\r
926         carthw_prot_lk3_startup();\r
927       else {\r
928         elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p);\r
929         skip_sect = 1;\r
930         *hw_detected = 0;\r
931       }\r
932       continue;\r
933     }\r
934     if (is_expr("sram_range", &p)) {\r
935       int start, end;\r
936 \r
937       if (!any_checks_passed)\r
938         goto no_checks;\r
939       rstrip(p);\r
940 \r
941       start = strtoul(p, &r, 0);\r
942       if (r == p)\r
943         goto bad;\r
944       p = sskip(r);\r
945       if (*p != ',')\r
946         goto bad;\r
947       p = sskip(p + 1);\r
948       end = strtoul(p, &r, 0);\r
949       if (r == p)\r
950         goto bad;\r
951       if (((start | end) & 0xff000000) || start > end) {\r
952         elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end);\r
953         goto bad_nomsg;\r
954       }\r
955       Pico.sv.start = start;\r
956       Pico.sv.end = end;\r
957       continue;\r
958     }\r
959     else if (is_expr("prop", &p)) {\r
960       if (!any_checks_passed)\r
961         goto no_checks;\r
962       rstrip(p);\r
963 \r
964       if      (strcmp(p, "no_sram") == 0)\r
965         Pico.sv.flags &= ~SRF_ENABLED;\r
966       else if (strcmp(p, "no_eeprom") == 0)\r
967         Pico.sv.flags &= ~SRF_EEPROM;\r
968       else if (strcmp(p, "filled_sram") == 0)\r
969         *fill_sram = 1;\r
970       else if (strcmp(p, "force_6btn") == 0)\r
971         PicoIn.quirks |= PQUIRK_FORCE_6BTN;\r
972       else {\r
973         elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p);\r
974         goto bad_nomsg;\r
975       }\r
976       elprintf(EL_STATUS, "game prop: %s", p);\r
977       continue;\r
978     }\r
979     else if (is_expr("eeprom_type", &p)) {\r
980       int type;\r
981       if (!any_checks_passed)\r
982         goto no_checks;\r
983       rstrip(p);\r
984 \r
985       type = strtoul(p, &r, 0);\r
986       if (r == p || type < 0)\r
987         goto bad;\r
988       Pico.sv.eeprom_type = type;\r
989       Pico.sv.flags |= SRF_EEPROM;\r
990       continue;\r
991     }\r
992     else if (is_expr("eeprom_lines", &p)) {\r
993       int scl, sda_in, sda_out;\r
994       if (!any_checks_passed)\r
995         goto no_checks;\r
996       rstrip(p);\r
997 \r
998       if (!parse_3_vals(p, &scl, &sda_in, &sda_out))\r
999         goto bad;\r
1000       if (scl < 0 || scl > 15 || sda_in < 0 || sda_in > 15 ||\r
1001           sda_out < 0 || sda_out > 15)\r
1002         goto bad;\r
1003 \r
1004       Pico.sv.eeprom_bit_cl = scl;\r
1005       Pico.sv.eeprom_bit_in = sda_in;\r
1006       Pico.sv.eeprom_bit_out= sda_out;\r
1007       continue;\r
1008     }\r
1009     else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) {\r
1010       int addr, mask, val;\r
1011       if (!any_checks_passed)\r
1012         goto no_checks;\r
1013       rstrip(p);\r
1014 \r
1015       if (!parse_3_vals(p, &addr, &mask, &val))\r
1016         goto bad;\r
1017 \r
1018       carthw_sprot_new_location(addr, mask, val, tmp ? 1 : 0);\r
1019       continue;\r
1020     }\r
1021 \r
1022 \r
1023 bad:\r
1024     elprintf(EL_STATUS, "carthw:%d: unrecognized expression: %s", line, buff);\r
1025 bad_nomsg:\r
1026     skip_sect = 1;\r
1027     continue;\r
1028 \r
1029 no_checks:\r
1030     elprintf(EL_STATUS, "carthw:%d: command without any checks before it: %s", line, buff);\r
1031     skip_sect = 1;\r
1032     continue;\r
1033   }\r
1034 \r
1035   if (f != NULL)\r
1036     fclose(f);\r
1037 }\r
1038 \r
1039 /*\r
1040  * various cart-specific things, which can't be handled by generic code\r
1041  */\r
1042 static void PicoCartDetect(const char *carthw_cfg)\r
1043 {\r
1044   int carthw_detected = 0;\r
1045   int fill_sram = 0;\r
1046 \r
1047   memset(&Pico.sv, 0, sizeof(Pico.sv));\r
1048   if (Pico.rom[0x1B1] == 'R' && Pico.rom[0x1B0] == 'A')\r
1049   {\r
1050     Pico.sv.start =  rom_read32(0x1B4) & ~0xff000001; // align\r
1051     Pico.sv.end   = (rom_read32(0x1B8) & ~0xff000000) | 1;\r
1052     if (Pico.rom[0x1B2] & 0x40)\r
1053       // EEPROM\r
1054       Pico.sv.flags |= SRF_EEPROM;\r
1055     Pico.sv.flags |= SRF_ENABLED;\r
1056   }\r
1057   if (Pico.sv.end == 0 || Pico.sv.start > Pico.sv.end)\r
1058   {\r
1059     // some games may have bad headers, like S&K and Sonic3\r
1060     // note: majority games use 0x200000 as starting address, but there are some which\r
1061     // use something else (0x300000 by HardBall '95). Luckily they have good headers.\r
1062     Pico.sv.start = 0x200000;\r
1063     Pico.sv.end   = 0x203FFF;\r
1064     Pico.sv.flags |= SRF_ENABLED;\r
1065   }\r
1066 \r
1067   // set EEPROM defaults, in case it gets detected\r
1068   Pico.sv.eeprom_type   = 0; // 7bit (24C01)\r
1069   Pico.sv.eeprom_bit_cl = 1;\r
1070   Pico.sv.eeprom_bit_in = 0;\r
1071   Pico.sv.eeprom_bit_out= 0;\r
1072 \r
1073   if (carthw_cfg != NULL)\r
1074     parse_carthw(carthw_cfg, &fill_sram, &carthw_detected);\r
1075 \r
1076   // assume the standard mapper for large roms\r
1077   if (!carthw_detected && Pico.romsize > 0x400000)\r
1078     carthw_ssf2_startup();\r
1079 \r
1080   if (Pico.sv.flags & SRF_ENABLED)\r
1081   {\r
1082     if (Pico.sv.flags & SRF_EEPROM)\r
1083       Pico.sv.size = 0x2000;\r
1084     else\r
1085       Pico.sv.size = Pico.sv.end - Pico.sv.start + 1;\r
1086 \r
1087     Pico.sv.data = calloc(Pico.sv.size, 1);\r
1088     if (Pico.sv.data == NULL)\r
1089       Pico.sv.flags &= ~SRF_ENABLED;\r
1090 \r
1091     if (Pico.sv.eeprom_type == 1)       // 1 == 0 in PD EEPROM code\r
1092       Pico.sv.eeprom_type = 0;\r
1093   }\r
1094 \r
1095   if ((Pico.sv.flags & SRF_ENABLED) && fill_sram)\r
1096   {\r
1097     elprintf(EL_STATUS, "SRAM fill");\r
1098     memset(Pico.sv.data, 0xff, Pico.sv.size);\r
1099   }\r
1100 \r
1101   // Unusual region 'code'\r
1102   if (rom_strcmp(0x1f0, "EUROPE") == 0 || rom_strcmp(0x1f0, "Europe") == 0)\r
1103     *(int *) (Pico.rom + 0x1f0) = 0x20204520;\r
1104 }\r
1105 \r
1106 // vim:shiftwidth=2:expandtab\r