patch/gg support, 1.201 release
[picodrive.git] / Pico / Cart.c
1 // This is part of Pico Library\r
2 \r
3 // (c) Copyright 2004 Dave, All rights reserved.\r
4 // (c) Copyright 2006 notaz, All rights reserved.\r
5 // Free for non-commercial use.\r
6 \r
7 // For commercial use, separate licencing terms must be obtained.\r
8 \r
9 \r
10 #include "PicoInt.h"\r
11 \r
12 void Byteswap(unsigned char *data,int len)\r
13 {\r
14   int i=0;\r
15 \r
16   if (len<2) return; // Too short\r
17 \r
18   do\r
19   {\r
20     unsigned short *pd=(unsigned short *)(data+i);\r
21     int value=*pd; // Get 2 bytes\r
22 \r
23     value=(value<<8)|(value>>8); // Byteswap it\r
24     *pd=(unsigned short)value; // Put 2b ytes\r
25     i+=2;\r
26   }\r
27   while (i+2<=len);\r
28 }\r
29 \r
30 // Interleve a 16k block and byteswap\r
31 static int InterleveBlock(unsigned char *dest,unsigned char *src)\r
32 {\r
33   int i=0;\r
34   for (i=0;i<0x2000;i++) dest[(i<<1)  ]=src[       i]; // Odd\r
35   for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even\r
36   return 0;\r
37 }\r
38 \r
39 // Decode a SMD file\r
40 static int DecodeSmd(unsigned char *data,int len)\r
41 {\r
42   unsigned char *temp=NULL;\r
43   int i=0;\r
44 \r
45   temp=(unsigned char *)malloc(0x4000);\r
46   if (temp==NULL) return 1;\r
47   memset(temp,0,0x4000);\r
48 \r
49   // Interleve each 16k block and shift down by 0x200:\r
50   for (i=0; i+0x4200<=len; i+=0x4000)\r
51   {\r
52     InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer\r
53     memcpy(data+i,temp,0x4000); // Copy back in\r
54   }\r
55 \r
56   free(temp);\r
57   return 0;\r
58 }\r
59 \r
60 static unsigned char *cd_realloc(void *old, int filesize)\r
61 {\r
62   unsigned char *rom;\r
63   dprintf("sizeof(mcd_state): %i", sizeof(mcd_state));\r
64   rom=realloc(old, sizeof(mcd_state));\r
65   if (rom) memset(rom+0x20000, 0, sizeof(mcd_state)-0x20000);\r
66   return rom;\r
67 }\r
68 \r
69 static unsigned char *PicoCartAlloc(int filesize)\r
70 {\r
71   int alloc_size;\r
72   unsigned char *rom;\r
73 \r
74   if (PicoMCD & 1) return cd_realloc(NULL, filesize);\r
75 \r
76   alloc_size=filesize+0x7ffff;\r
77   if((filesize&0x3fff)==0x200) alloc_size-=0x200;\r
78   alloc_size&=~0x7ffff; // use alloc size of multiples of 512K, so that memhandlers could be set up more efficiently\r
79   if((filesize&0x3fff)==0x200) alloc_size+=0x200;\r
80   else if(alloc_size-filesize < 4) alloc_size+=4; // padding for out-of-bound exec protection\r
81   //dprintf("alloc_size: %x\n",  alloc_size);\r
82 \r
83   // Allocate space for the rom plus padding\r
84   rom=(unsigned char *)malloc(alloc_size);\r
85   if(rom) memset(rom+alloc_size-0x80000,0,0x80000);\r
86   return rom;\r
87 }\r
88 \r
89 int PicoCartLoad(FILE *f,unsigned char **prom,unsigned int *psize)\r
90 {\r
91   unsigned char *rom=NULL; int size;\r
92   if (f==NULL) return 1;\r
93 \r
94   fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);\r
95   if (size <= 0) return 1;\r
96   size=(size+3)&~3; // Round up to a multiple of 4\r
97 \r
98   // Allocate space for the rom plus padding\r
99   rom=PicoCartAlloc(size);\r
100   if (rom==NULL) return 1; // { fclose(f); return 1; }\r
101 \r
102   fread(rom,1,size,f); // Load up the rom\r
103 \r
104   // maybe we are loading MegaCD BIOS?\r
105   if (!(PicoMCD&1) && size == 0x20000 && (!strncmp((char *)rom+0x124, "BOOT", 4) || !strncmp((char *)rom+0x128, "BOOT", 4))) {\r
106     PicoMCD |= 1;\r
107     rom = cd_realloc(rom, size);\r
108   }\r
109 \r
110   // Check for SMD:\r
111   if ((size&0x3fff)==0x200) { DecodeSmd(rom,size); size-=0x200; } // Decode and byteswap SMD\r
112   else Byteswap(rom,size); // Just byteswap\r
113 \r
114   if (prom)  *prom=rom;\r
115   if (psize) *psize=size;\r
116 \r
117   return 0;\r
118 }\r
119 \r
120 // Insert/remove a cartridge:\r
121 int PicoCartInsert(unsigned char *rom,unsigned int romsize)\r
122 {\r
123   // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r
124   // This will hang the emu, but will prevent nasty crashes.\r
125   // note: 4 bytes are padded to every ROM\r
126   if(rom != NULL)\r
127     *(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped\r
128 \r
129   SRam.resize=1;\r
130   Pico.rom=rom;\r
131   Pico.romsize=romsize;\r
132 \r
133   return PicoReset(1);\r
134 }\r
135 \r
136 int PicoUnloadCart(unsigned char* romdata)\r
137 {\r
138   free(romdata);\r
139   return 0;\r
140 }\r
141 \r
142 \r
143 #ifdef _UNZIP_SUPPORT\r
144 \r
145 // notaz\r
146 #include "../unzip/unzip.h"\r
147 \r
148 // nearly same as PicoCartLoad, but works with zipfiles\r
149 int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize)\r
150 {\r
151         unsigned char *rom=0;\r
152         struct zipent* zipentry;\r
153         int size;\r
154         ZIP *zipfile = openzip(fname);\r
155 \r
156         if(!zipfile) return 1;\r
157 \r
158         // find first bin or smd\r
159         while((zipentry = readzip(zipfile)) != 0)\r
160         {\r
161                 char *ext;\r
162                 if(strlen(zipentry->name) < 5) continue;\r
163                 ext = zipentry->name+strlen(zipentry->name)-4;\r
164 \r
165                 if(!strcasecmp(ext, ".bin") || !strcasecmp(ext, ".smd") || !strcasecmp(ext, ".gen")) break;\r
166         }\r
167 \r
168         if(!zipentry) {\r
169                 closezip(zipfile);\r
170                 return 4; // no roms\r
171         }\r
172 \r
173         size = zipentry->uncompressed_size;\r
174 \r
175         size=(size+3)&~3; // Round up to a multiple of 4\r
176 \r
177         // Allocate space for the rom plus padding\r
178         rom=PicoCartAlloc(size);\r
179         if (rom==NULL) { closezip(zipfile); return 2; }\r
180 \r
181         if(readuncompresszip(zipfile, zipentry, (char *)rom) != 0) {\r
182                 free(rom);\r
183                 rom = 0;\r
184                 closezip(zipfile);\r
185                 return 5; // unzip failed\r
186         }\r
187 \r
188         closezip(zipfile);\r
189 \r
190         // maybe we are loading MegaCD BIOS?\r
191         if (!(PicoMCD&1) && size == 0x20000 &&\r
192                         (!strncmp((char *)rom+0x124, "BOOT", 4) || !strncmp((char *)rom+0x128, "BOOT", 4))) {\r
193                 PicoMCD |= 1;\r
194                 rom = cd_realloc(rom, size);\r
195         }\r
196 \r
197         // Check for SMD:\r
198         if ((size&0x3fff)==0x200) { DecodeSmd(rom,size); size-=0x200; } // Decode and byteswap SMD\r
199         else Byteswap(rom,size); // Just byteswap\r
200 \r
201         if (prom)  *prom=rom;\r
202         if (psize) *psize=size;\r
203 \r
204         return 0;\r
205 }\r
206 \r
207 #endif\r