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