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