release r2, update credits
[fceu.git] / file.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 2002 Ben Parnell
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #ifdef ZLIB
26  #include <zlib.h>
27  #include "zlib/unzip.h"
28 #endif
29
30 #include "types.h"
31 #include "file.h"
32 #include "endian.h"
33 #include "memory.h"
34 #include "driver.h"
35 #include "svga.h"
36
37 static void *desctable[8]={0,0,0,0,0,0,0,0};
38 static int x;
39
40 /*
41 typedef struct {
42            uint8 *data;
43            uint32 size;
44            uint32 location;
45 } MEMWRAP;
46 */
47 typedef struct {
48            uint8 *data;
49            uint32 size;
50            uint32 location;
51 } ZIPWRAP;
52 #define MEMWRAP ZIPWRAP
53
54 void ApplyIPS(FILE *ips, int destf)
55 {
56  uint8 header[5];
57  uint32 count=0;
58  MEMWRAP *dest;
59
60  FCEU_printf(" Applying IPS...\n");
61  if(!(destf&0x8000))
62  {
63   FCEU_printf("failed (bad destf).\n");
64   return;
65  }
66
67  dest=(MEMWRAP*)desctable[(destf&255)-1];
68
69  if(fread(header,1,5,ips)!=5)
70  {
71   FCEU_printf("failed (bad header).\n");
72   fclose(ips);
73   return;
74  }
75  if(memcmp(header,"PATCH",5))
76  {
77   FCEU_printf("failed (bad header).\n");
78   fclose(ips);
79   return;
80  }
81
82  while(fread(header,1,3,ips)==3)
83  {
84   uint32 offset=(header[0]<<16)|(header[1]<<8)|header[2];
85   uint16 size;
86
87   if(!memcmp(header,"EOF",3))
88   {
89    FCEU_printf(" IPS EOF:  Did %d patches\n\n",count);
90    fclose(ips);
91    return;
92   }
93
94   size=fgetc(ips)<<8;
95   size|=fgetc(ips);
96   if(!size)     /* RLE */
97   {
98    uint8 *start;
99    uint8 b;
100    size=fgetc(ips)<<8;
101    size|=fgetc(ips);
102
103    //FCEU_printf("  Offset: %8d  Size: %5d RLE\n",offset,size);
104
105    if((offset+size)>dest->size)
106    {
107     uint8 *tmp;
108
109     // Probably a little slow.
110     tmp=(uint8 *)realloc(dest->data,offset+size);
111     if(!tmp)
112     {
113      FCEU_printf("  Oops.  IPS patch %d(type RLE) goes beyond end of file.  Could not allocate memory.\n",count);
114      fclose(ips);
115      return;
116     }
117     dest->size=offset+size;
118     dest->data=tmp;
119     memset(dest->data+dest->size,0,offset+size-dest->size);
120    }
121    b=fgetc(ips);
122    start=dest->data+offset;
123    do
124    {
125     *start=b;
126     start++;
127    } while(--size);
128   }
129   else          /* Normal patch */
130   {
131    //FCEU_printf("  Offset: %8d  Size: %5d\n",offset,size);
132    if((offset+size)>dest->size)
133    {
134     uint8 *tmp;
135
136     // Probably a little slow.
137     tmp=(uint8 *)realloc(dest->data,offset+size);
138     if(!tmp)
139     {
140      FCEU_printf("  Oops.  IPS patch %d(type normal) goes beyond end of file.  Could not allocate memory.\n",count);
141      fclose(ips);
142      return;
143     }
144     dest->data=tmp;
145     memset(dest->data+dest->size,0,offset+size-dest->size);
146    }
147    fread(dest->data+offset,1,size,ips);
148   }
149   count++;
150  }
151  fclose(ips);
152  FCEU_printf(" Hard IPS end!\n");
153 }
154
155
156 #ifdef ZLIB
157
158
159 void *MakeZipWrap(void *tz)
160 {
161  unz_file_info ufo;
162  ZIPWRAP *tmp;
163
164  if(!(tmp=FCEU_malloc(sizeof(ZIPWRAP))))
165   goto doret;
166
167  unzGetCurrentFileInfo(tz,&ufo,0,0,0,0,0,0);
168
169  tmp->location=0;
170  tmp->size=ufo.uncompressed_size;
171  if(!(tmp->data=FCEU_malloc(ufo.uncompressed_size)))
172  {
173   tmp=0;
174   goto doret;
175  }
176
177  unzReadCurrentFile(tz,tmp->data,ufo.uncompressed_size);
178
179  doret:
180
181  unzCloseCurrentFile(tz);
182  unzClose(tz);
183
184  return tmp;
185 }
186 #endif
187
188 #ifndef __GNUC__
189  #define strcasecmp strcmp
190 #endif
191
192 int FASTAPASS(2) FCEU_fopen(char *path, char *mode)
193 {
194  void *t;
195
196  #ifdef ZLIB
197   unzFile tz;
198   if((tz=unzOpen(path)))  // If it's not a zip file, use regular file handlers.
199                           // Assuming file type by extension usually works,
200                           // but I don't like it. :)
201   {
202    if(unzGoToFirstFile(tz)==UNZ_OK)
203    {
204     for(;;)
205     {
206      char tempu[512];   // Longer filenames might be possible, but I don't
207                         // think people would name files that long in zip files...
208      unzGetCurrentFileInfo(tz,0,tempu,512,0,0,0,0);
209      tempu[511]=0;
210      if(strlen(tempu)>=4)
211      {
212       char *za=tempu+strlen(tempu)-4;
213       if(!strcasecmp(za,".nes") || !strcasecmp(za,".fds") ||
214          !strcasecmp(za,".nsf") || !strcasecmp(za,".unf") ||
215          !strcasecmp(za,".nez"))
216        break;
217      }
218      if(strlen(tempu)>=5)
219      {
220       if(!strcasecmp(tempu+strlen(tempu)-5,".unif"))
221        break;
222      }
223      if(unzGoToNextFile(tz)!=UNZ_OK)
224      {
225       if(unzGoToFirstFile(tz)!=UNZ_OK) goto zpfail;
226       break;
227      }
228     }
229     if(unzOpenCurrentFile(tz)!=UNZ_OK)
230      goto zpfail;
231    }
232    else
233    {
234     zpfail:
235     unzClose(tz);
236     return 0;
237    }
238
239    for(x=0;x<8;x++)
240     if(!desctable[x])
241     {
242      if(!(desctable[x]=MakeZipWrap(tz)))
243       return(0);
244      return((x+1)|0x8000);
245     }
246   }
247 #endif
248
249  #ifdef ZLIB
250  if((t=fopen(path,"rb")))
251  {
252   uint32 magic;
253
254   magic=fgetc(t);
255   magic|=fgetc(t)<<8;
256   magic|=fgetc(t)<<16;
257
258   fclose(t);
259
260   if(magic==0x088b1f)
261   {
262    if((t=gzopen(path,mode)))
263     for(x=0;x<8;x++)
264      if(!desctable[x])
265      {
266       desctable[x]=t;
267       return((x+1)|0x4000);
268      }
269   }
270  }
271  #endif
272
273   if((t=fopen(path,mode)))
274   {
275    fseek(t,0,SEEK_SET);
276    for(x=0;x<8;x++)
277     if(!desctable[x])
278     {
279      desctable[x]=t;
280      return(x+1);
281     }
282   }
283  return 0;
284 }
285
286 int FASTAPASS(1) FCEU_fopen_forcemem(char *path)
287 {
288  MEMWRAP *tmp;
289  long size;
290  int fp;
291
292  fp=FCEU_fopen(path, "rb");
293  if (!fp) return 0;
294
295  if (fp&0x8000) return fp;
296
297  if (!(tmp=FCEU_malloc(sizeof(*tmp))))
298   goto retr;
299
300  size=FCEU_fgetsize(fp);
301  if (size <= 0) goto retr;
302  tmp->size=size;
303  tmp->data=FCEU_malloc(size);
304  if (!tmp->data) goto retr;
305  FCEU_fread(tmp->data, 1, size, fp);
306  FCEU_fclose(fp); fp=0;
307  tmp->location=0;
308
309  for(x=0;x<8;x++)
310   if(!desctable[x])
311   {
312    desctable[x]=tmp;
313    return (x+1)|0x8000;
314   }
315
316  retr:
317  if (fp) FCEU_fclose(fp);
318  if (tmp && tmp->data) FCEU_free(tmp->data);
319  if (tmp) FCEU_free(tmp);
320  return 0;
321 }
322
323 int FASTAPASS(1) FCEU_fclose(int stream)
324 {
325  #ifdef ZLIB
326  if(stream&0x4000)
327  {
328   gzclose(desctable[(stream&255)-1]);
329   desctable[(stream&255)-1]=0;
330  }
331  else if(stream&0x8000)
332  {
333   free(((ZIPWRAP*)desctable[(stream&255)-1])->data);
334   free(desctable[(stream&255)-1]);
335   desctable[(stream&255)-1]=0;
336  }
337  else // close zip file
338  {
339  #endif
340   fclose(desctable[stream-1]);
341   desctable[stream-1]=0;
342  #ifdef ZLIB
343  }
344  #endif
345  return 1;
346 }
347
348 size_t FASTAPASS(3) FCEU_fread(void *ptr, size_t size, size_t nmemb, int stream)
349 {
350  #ifdef ZLIB
351  if(stream&0x4000)
352  {
353   return gzread(desctable[(stream&255)-1],ptr,size*nmemb);
354  }
355  else if(stream&0x8000)
356  {
357   ZIPWRAP *wz;
358   uint32 total=size*nmemb;
359
360   wz=(ZIPWRAP*)desctable[(stream&255)-1];
361   if(wz->location>=wz->size) return 0;
362
363   if((wz->location+total)>wz->size)
364   {
365    int ak=wz->size-wz->location;
366    memcpy((uint8*)ptr,wz->data+wz->location,ak);
367    wz->location=wz->size;
368    return(ak/size);
369   }
370   else
371   {
372    memcpy((uint8*)ptr,wz->data+wz->location,total);
373    wz->location+=total;
374    return nmemb;
375   }
376  }
377  else
378  {
379  #endif
380  return fread(ptr,size,nmemb,desctable[stream-1]);
381  #ifdef ZLIB
382  }
383  #endif
384 }
385
386 size_t FASTAPASS(3) FCEU_fwrite(void *ptr, size_t size, size_t nmemb, int stream)
387 {
388  #ifdef ZLIB
389  if(stream&0x4000)
390  {
391   return gzwrite(desctable[(stream&255)-1],ptr,size*nmemb);
392  }
393  else if(stream&0x8000)
394  {
395   return 0;
396  }
397  else
398  #endif
399   return fwrite(ptr,size,nmemb,desctable[stream-1]);
400 }
401
402 int FASTAPASS(3) FCEU_fseek(int stream, long offset, int whence)
403 {
404  #ifdef ZLIB
405  if(stream&0x4000)
406  {
407   return gzseek(desctable[(stream&255)-1],offset,whence);
408  }
409  else if(stream&0x8000)
410  {
411   ZIPWRAP *wz;
412   wz=(ZIPWRAP*)desctable[(stream&255)-1];
413
414   switch(whence)
415   {
416    case SEEK_SET:if(offset>=wz->size)
417                   return(-1);
418                  wz->location=offset;break;
419    case SEEK_CUR:if(offset+wz->location>wz->size)
420                   return (-1);
421                  wz->location+=offset;
422                  break;
423   }
424   return 0;
425  }
426  else
427  #endif
428   return fseek(desctable[stream-1],offset,whence);
429 }
430
431 long FASTAPASS(1) FCEU_ftell(int stream)
432 {
433  #ifdef ZLIB
434  if(stream&0x4000)
435  {
436   return gztell(desctable[(stream&255)-1]);
437  }
438  else if(stream&0x8000)
439  {
440   return (((ZIPWRAP *)desctable[(stream&255)-1])->location);
441  }
442  else
443  #endif
444   return ftell(desctable[stream-1]);
445 }
446
447 void FASTAPASS(1)FCEU_rewind(int stream)
448 {
449  #ifdef ZLIB
450  if(stream&0x4000)
451  {
452   gzrewind(desctable[(stream&255)-1]);
453  }
454  else if(stream&0x8000)
455  {
456   ((ZIPWRAP *)desctable[(stream&255)-1])->location=0;
457  }
458  else
459  #endif
460  #ifdef _WIN32_WCE
461   fseek(desctable[stream-1],0,SEEK_SET);
462  #else
463   rewind(desctable[stream-1]);
464  #endif
465 }
466
467 int FASTAPASS(2) FCEU_read32(void *Bufo, int stream)
468 {
469  #ifdef ZLIB
470  if(stream&0xC000)
471  {
472   uint32 t;
473   #ifndef LSB_FIRST
474   uint8 x[4];
475   #endif
476   if(stream&0x8000)
477   {
478    ZIPWRAP *wz;
479    wz=(ZIPWRAP*)desctable[(stream&255)-1];
480    if(wz->location+4>wz->size)
481     {return 0;}
482    memcpy(&t, wz->data+wz->location, 4);
483    wz->location+=4;
484   }
485   else if(stream&0x4000)
486    gzread(desctable[(stream&255)-1],&t,4);
487   #ifndef LSB_FIRST
488   x[0]=t[3];
489   x[1]=t[2];
490   x[2]=t[1];
491   x[3]=t[0];
492   *(uint32*)Bufo=*(uint32*)x;
493   #else
494   memcpy(Bufo, &t, 4);
495   #endif
496   return 1;
497  }
498  else
499  #endif
500  {
501   return read32(Bufo,desctable[stream-1]);
502  }
503 }
504
505 int FASTAPASS(1) FCEU_fgetc(int stream)
506 {
507  #ifdef ZLIB
508  if(stream&0x4000)
509   return gzgetc(desctable[(stream&255)-1]);
510  else if(stream&0x8000)
511  {
512   ZIPWRAP *wz;
513   wz=(ZIPWRAP*)desctable[(stream&255)-1];
514   if(wz->location<wz->size)
515    return wz->data[wz->location++];
516   return EOF;
517  }
518  else
519 #endif
520   return fgetc(desctable[stream-1]);
521 }
522
523 long FASTAPASS(1) FCEU_fgetsize(int stream)
524 {
525  #ifdef ZLIB
526  if(stream&0x4000)
527  {
528   int x,t;
529   t=gztell(desctable[(stream&255)-1]);
530   gzrewind(desctable[(stream&255)-1]);
531   for(x=0;gzgetc(desctable[(stream&255)-1]) != EOF; x++);
532   gzseek(desctable[(stream&255)-1],t,SEEK_SET);
533   return(x);
534  }
535  else if(stream&0x8000)
536   return ((ZIPWRAP*)desctable[(stream&255)-1])->size;
537  else
538  #endif
539  {
540   long t,r;
541   t=ftell(desctable[stream-1]);
542   fseek(desctable[stream-1],0,SEEK_END);
543   r=ftell(desctable[stream-1]);
544   fseek(desctable[stream-1],t,SEEK_SET);
545   return r;
546  }
547 }
548
549 int FASTAPASS(1) FCEU_fisarchive(int stream)
550 {
551  #ifdef ZLIB
552  if(stream&0x8000)
553   return 1;
554  #endif
555  return 0;
556 }