initial import
[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 *PicoCartAlloc(int filesize)\r
61 {\r
62   int alloc_size;\r
63   unsigned char *rom;\r
64 \r
65   if (PicoMCD & 1) {\r
66     dprintf("sizeof(mcd_state): %i", sizeof(mcd_state));\r
67     if (filesize > 0x20000) return NULL; // invalid BIOS\r
68     rom=(unsigned char *)malloc(sizeof(mcd_state));\r
69     if (rom) memset(rom, 0, sizeof(mcd_state));\r
70     return rom;\r
71   }\r
72 \r
73   alloc_size=filesize+0x7ffff;\r
74   if((filesize&0x3fff)==0x200) alloc_size-=0x200;\r
75   alloc_size&=~0x7ffff; // use alloc size of multiples of 512K, so that memhandlers could be set up more efficiently\r
76   if((filesize&0x3fff)==0x200) alloc_size+=0x200;\r
77   else if(alloc_size-filesize < 4) alloc_size+=4; // padding for out-of-bound exec protection\r
78   //dprintf("alloc_size: %x\n",  alloc_size);\r
79 \r
80   // Allocate space for the rom plus padding\r
81   rom=(unsigned char *)malloc(alloc_size);\r
82   if(rom) memset(rom+alloc_size-0x80000,0,0x80000);\r
83   return rom;\r
84 }\r
85 \r
86 int PicoCartLoad(FILE *f,unsigned char **prom,unsigned int *psize)\r
87 {\r
88   unsigned char *rom=NULL; int size;\r
89   if (f==NULL) return 1;\r
90 \r
91   fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET);\r
92   if (size <= 0) return 1;\r
93   if (PicoMCD & 1) {\r
94     if (size > 0x20000) return 1; // invalid BIOS\r
95     size = 0xe0000;\r
96   } else {\r
97     size=(size+3)&~3; // Round up to a multiple of 4\r
98   }\r
99 \r
100   // Allocate space for the rom plus padding\r
101   rom=PicoCartAlloc(size);\r
102   if (rom==NULL) return 1; // { fclose(f); return 1; }\r
103 \r
104   fread(rom,1,size,f); // Load up the rom\r
105   // fclose(f); // this is confusing. From now on, caller should close it, because it opened this.\r
106 \r
107   // Check for SMD:\r
108   if ((size&0x3fff)==0x200) { DecodeSmd(rom,size); size-=0x200; } // Decode and byteswap SMD\r
109   else Byteswap(rom,size); // Just byteswap\r
110 \r
111   if (prom)  *prom=rom;\r
112   if (psize) *psize=size;\r
113 \r
114   return 0;\r
115 }\r
116 \r
117 // Insert/remove a cartridge:\r
118 int PicoCartInsert(unsigned char *rom,unsigned int romsize)\r
119 {\r
120   // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r
121   // This will hang the emu, but will prevent nasty crashes.\r
122   // note: 4 bytes are padded to every ROM\r
123   if(rom != NULL)\r
124     *(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped\r
125 \r
126   SRam.resize=1;\r
127   Pico.rom=rom;\r
128   Pico.romsize=romsize;\r
129 \r
130   return PicoReset(1);\r
131 }\r
132 \r
133 int PicoUnloadCart(unsigned char* romdata)\r
134 {\r
135   free(romdata);\r
136   return 0;\r
137 }\r
138 \r
139 \r
140 #ifdef _UNZIP_SUPPORT\r
141 \r
142 // notaz\r
143 #include "../unzip/unzip.h"\r
144 \r
145 // nearly same as PicoCartLoad, but works with zipfiles\r
146 int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize)\r
147 {\r
148         unsigned char *rom=0;\r
149         struct zipent* zipentry;\r
150         int size;\r
151         ZIP *zipfile = openzip(fname);\r
152 \r
153         if(!zipfile) return 1;\r
154 \r
155         // find first bin or smd\r
156         while((zipentry = readzip(zipfile)) != 0)\r
157         {\r
158                 char *ext;\r
159                 if(strlen(zipentry->name) < 5) continue;\r
160                 ext = zipentry->name+strlen(zipentry->name)-4;\r
161 \r
162                 if(!strcasecmp(ext, ".bin") || !strcasecmp(ext, ".smd") || !strcasecmp(ext, ".gen")) break;\r
163         }\r
164 \r
165         if(!zipentry) {\r
166                 closezip(zipfile);\r
167                 return 4; // no roms\r
168         }\r
169 \r
170         size = zipentry->uncompressed_size;\r
171 \r
172         size=(size+3)&~3; // Round up to a multiple of 4\r
173 \r
174         // Allocate space for the rom plus padding\r
175         rom=PicoCartAlloc(size);\r
176         if (rom==NULL) { closezip(zipfile); return 2; }\r
177 \r
178         if(readuncompresszip(zipfile, zipentry, (char *)rom) != 0) {\r
179                 free(rom);\r
180                 rom = 0;\r
181                 closezip(zipfile);\r
182                 return 5; // unzip failed\r
183         }\r
184 \r
185         closezip(zipfile);\r
186 \r
187         // Check for SMD:\r
188         if ((size&0x3fff)==0x200) { DecodeSmd(rom,size); size-=0x200; } // Decode and byteswap SMD\r
189         else Byteswap(rom,size); // Just byteswap\r
190 \r
191         if (prom)  *prom=rom;\r
192         if (psize) *psize=size;\r
193 \r
194         return 0;\r
195 }\r
196 \r
197 #endif\r