/ MCUME_teensy / teensynofrendo / nes_rom_light.c
nes_rom_light.c
  1  /*
  2  ** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
  3  **
  4  **
  5  ** This program is free software; you can redistribute it and/or
  6  ** modify it under the terms of version 2 of the GNU Library General 
  7  ** Public License as published by the Free Software Foundation.
  8  **
  9  ** This program is distributed in the hope that it will be useful, 
 10  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 12  ** Library General Public License for more details.  To obtain a 
 13  ** copy of the GNU Library General Public License, write to the Free 
 14  ** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 15  **
 16  ** Any permitted reproduction of these routines, in whole or in part,
 17  ** must bear this legend.
 18  **
 19  **
 20  ** nes_rom.c
 21  **
 22  ** NES ROM loading/saving related functions
 23  ** $Id: nes_rom.c,v 1.2 2001/04/27 14:37:11 neil Exp $
 24  */
 25  
 26  /* TODO: make this a generic ROM loading routine */
 27  
 28  #include <stdio.h>
 29  #include <string.h>
 30  #include "noftypes.h"
 31  #include "nes_rom.h"
 32  #include "nes_mmc.h"
 33  #include "nes_ppu.h"
 34  #include "nes.h"
 35  #include "log.h"
 36  #include "osd.h"
 37  
 38  extern char *osd_getromdata();
 39  
 40  /* Max length for displayed filename */
 41  #define  ROM_DISP_MAXLEN   20
 42  
 43  
 44  #define  ROM_FOURSCREEN    0x08
 45  #define  ROM_TRAINER       0x04
 46  #define  ROM_BATTERY       0x02
 47  #define  ROM_MIRRORTYPE    0x01
 48  #define  ROM_INES_MAGIC    "NES\x1A"
 49  
 50  //ToDo: packed - JD
 51  typedef struct inesheader_s
 52  {
 53     uint8 ines_magic[4]    ;
 54     uint8 rom_banks        ;
 55     uint8 vrom_banks       ;
 56     uint8 rom_type         ;
 57     uint8 mapper_hinybble  ;
 58     uint8 reserved[8]      ;
 59  } inesheader_t;
 60  
 61  
 62  #define  TRAINER_OFFSET    0x1000
 63  #define  TRAINER_LENGTH    0x200
 64  #define  VRAM_LENGTH       0x2000
 65  
 66  #define  ROM_BANK_LENGTH   0x4000
 67  #define  VROM_BANK_LENGTH  0x2000
 68  
 69  #define  SRAM_BANK_LENGTH  0x0400
 70  #define  VRAM_BANK_LENGTH  0x2000
 71  
 72  
 73  
 74  
 75  
 76  static int rom_loadrom(unsigned char **rom, rominfo_t *rominfo)
 77  {
 78     ASSERT(rom);
 79     ASSERT(rominfo);
 80     /* Allocate ROM space, and load it up! */
 81     rominfo->rom=*rom;
 82     *rom+=ROM_BANK_LENGTH*rominfo->rom_banks;
 83  
 84     /* If there's VROM, allocate and stuff it in */
 85     if (rominfo->vrom_banks)
 86     {
 87        rominfo->vrom=*rom;
 88        *rom+=VROM_BANK_LENGTH*rominfo->vrom_banks;
 89     }
 90     else
 91     {
 92        rominfo->vram = emu_Malloc(VRAM_LENGTH);
 93        if (NULL == rominfo->vram)
 94        {
 95           return -1;
 96        }
 97        memset(rominfo->vram, 0, VRAM_LENGTH);
 98     }
 99     return 0;
100  }
101  
102  
103  static int *rom_findrom(const char *filename, rominfo_t *rominfo)
104  {
105     int fp;
106  
107     ASSERT(rominfo);
108  
109     if (NULL == filename)
110        return NULL;
111  
112     /* Make a copy of the name so we can extend it */
113     osd_fullname(rominfo->filename, filename);
114  
115     fp = emu_FileOpen(rominfo->filename);
116     if (!fp)
117     {
118        /* Didn't find the file?  Maybe the .NES extension was omitted */
119        if (NULL == strrchr(rominfo->filename, '.'))
120           strncat(rominfo->filename, ".nes", PATH_MAX - strlen(rominfo->filename));
121  
122        /* this will either return NULL or a valid file pointer */
123        fp = emu_FileOpen(rominfo->filename);
124     }
125  
126     return fp;
127  }
128  
129  /* Add ROM name to a list with dirty headers */
130  static int rom_adddirty(char *filename)
131  {
132     return 0;
133  }
134  
135  /* return 0 if this *is* an iNES file */
136  int rom_checkmagic(const char *filename)
137  {
138     inesheader_t head;
139     rominfo_t rominfo;
140     int fp;
141  
142     fp = rom_findrom(filename, &rominfo);
143     if (0 == fp)
144        return -1;
145  
146     emu_FileRead(&head, 1*sizeof(head));
147  
148     emu_FileClose();
149  
150     if (0 == memcmp(head.ines_magic, ROM_INES_MAGIC, 4))
151        /* not an iNES file */
152        return 0;
153  
154     return -1;
155  }
156  
157  
158  
159  static int rom_getheader(unsigned char **rom, rominfo_t *rominfo)
160  {
161  #define  RESERVED_LENGTH   8
162     inesheader_t head;
163     uint8 reserved[RESERVED_LENGTH];
164     bool header_dirty;
165  
166     ASSERT(rom);
167     ASSERT(*rom);
168     ASSERT(rominfo);
169  
170     /* Read in the header */
171  //   _fread(&head, 1, sizeof(head), fp);
172  	log_printf("Head: %p (%x %x %x %x)\n", *rom, (*rom)[0], (*rom)[1], (*rom)[2], (*rom)[3]);
173  	memcpy(&head, *rom, sizeof(head));
174  	*rom+=sizeof(head);
175  
176     if (memcmp(head.ines_magic, ROM_INES_MAGIC, 4))
177     {
178        return -1;
179     }
180  
181     rominfo->rom_banks = head.rom_banks;
182     rominfo->vrom_banks = head.vrom_banks;
183     /* iNES assumptions */
184     rominfo->sram_banks = 8; /* 1kB banks, so 8KB */
185     rominfo->vram_banks = 1; /* 8kB banks, so 8KB */
186     rominfo->mirror = (head.rom_type & ROM_MIRRORTYPE) ? MIRROR_VERT : MIRROR_HORIZ;
187     rominfo->flags = 0;
188     if (head.rom_type & ROM_BATTERY)
189        rominfo->flags |= ROM_FLAG_BATTERY;
190     if (head.rom_type & ROM_TRAINER)
191        rominfo->flags |= ROM_FLAG_TRAINER;
192     if (head.rom_type & ROM_FOURSCREEN)
193        rominfo->flags |= ROM_FLAG_FOURSCREEN;
194     /* TODO: fourscreen a mirroring type? */
195     rominfo->mapper_number = head.rom_type >> 4;
196  
197     /* Do a compare - see if we've got a clean extended header */
198     memset(reserved, 0, RESERVED_LENGTH);
199     if (0 == memcmp(head.reserved, reserved, RESERVED_LENGTH))
200     {
201        /* We were clean */
202        header_dirty = false;
203        rominfo->mapper_number |= (head.mapper_hinybble & 0xF0);
204     }
205     else
206     {
207        header_dirty = true;
208  
209        /* @!?#@! DiskDude. */
210        if (('D' == head.mapper_hinybble) && (0 == memcmp(head.reserved, "iskDude!", 8)))
211           log_printf("`DiskDude!' found in ROM header, ignoring high mapper nybble\n");
212        else
213        {
214           log_printf("ROM header dirty, possible problem\n");
215           rominfo->mapper_number |= (head.mapper_hinybble & 0xF0);
216        }
217  
218        rom_adddirty(rominfo->filename);
219     }
220  
221     /* TODO: this is an ugly hack, but necessary, I guess */
222     /* Check for VS unisystem mapper */
223     if (99 == rominfo->mapper_number)
224        rominfo->flags |= ROM_FLAG_VERSUS;
225  
226     return 0;
227  }
228  
229  /* Build the info string for ROM display */
230  char *rom_getinfo(rominfo_t *rominfo)
231  {
232     static char info[PATH_MAX + 1];
233     char romname[PATH_MAX + 1], temp[PATH_MAX + 1];
234  
235     /* Look to see if we were given a path along with filename */
236     /* TODO: strip extensions */
237     if (strrchr(rominfo->filename, PATH_SEP))
238        strncpy(romname, strrchr(rominfo->filename, PATH_SEP) + 1, PATH_MAX);
239     else
240        strncpy(romname, rominfo->filename, PATH_MAX);
241  
242     /* If our filename is too long, truncate our displayed filename */
243     if (strlen(romname) > ROM_DISP_MAXLEN)
244     {
245        strncpy(info, romname, ROM_DISP_MAXLEN - 3);
246        strcpy(info + (ROM_DISP_MAXLEN - 3), "...");
247     }
248     else
249     {
250        strcpy(info, romname);
251     }
252  
253     sprintf(temp, " [%d] %dk/%dk %c", rominfo->mapper_number,
254             rominfo->rom_banks * 16, rominfo->vrom_banks * 8,
255             (rominfo->mirror == MIRROR_VERT) ? 'V' : 'H');
256     
257     /* Stick it on there! */
258     strncat(info, temp, PATH_MAX - strlen(info));
259  
260     if (rominfo->flags & ROM_FLAG_BATTERY)
261        strncat(info, "B", PATH_MAX - strlen(info));
262     if (rominfo->flags & ROM_FLAG_TRAINER)
263        strncat(info, "T", PATH_MAX - strlen(info));
264     if (rominfo->flags & ROM_FLAG_FOURSCREEN)
265        strncat(info, "4", PATH_MAX - strlen(info));
266  
267     return info;
268  }
269  
270  /* Load a ROM image into memory */
271  rominfo_t *rom_load(const char *filename)
272  {
273     unsigned char *rom=(unsigned char*)osd_getromdata();
274     rominfo_t *rominfo;
275  
276     rominfo = emu_Malloc(sizeof(rominfo_t));
277     if (NULL == rominfo)
278        return NULL;
279  
280     memset(rominfo, 0, sizeof(rominfo_t));
281  
282     /* Get the header and stick it into rominfo struct */
283  	if (rom_getheader(&rom, rominfo))
284        goto _fail;
285  
286     /* Make sure we really support the mapper */
287     if (false == mmc_peek(rominfo->mapper_number))
288     {
289        goto _fail;
290     }
291  
292  	if (rom_loadrom(&rom, rominfo))
293        goto _fail;
294  
295     return rominfo;
296  
297  _fail:
298     rom_free(&rominfo);
299     return NULL;
300  }
301  
302  /* Free a ROM */
303  void rom_free(rominfo_t **rominfo)
304  {
305     if (NULL == *rominfo)
306     {
307        return;
308     }
309  
310     /* Restore palette if we loaded in a VS jobber */
311     if ((*rominfo)->flags & ROM_FLAG_VERSUS)
312     {
313        /* TODO: bad idea calling nes_getcontextptr... */
314        ppu_setdefaultpal(nes_getcontextptr()->ppu);
315        log_printf("Default NES palette restored\n");
316     }
317  
318  
319     if ((*rominfo)->sram)
320        free((*rominfo)->sram);
321     if ((*rominfo)->rom)
322        free((*rominfo)->rom);
323     if ((*rominfo)->vrom)
324        free((*rominfo)->vrom);
325     if ((*rominfo)->vram)
326        free((*rominfo)->vram);
327  
328     free(*rominfo);
329  }
330  
331  /*
332  ** $Log: nes_rom.c,v $
333  ** Revision 1.2  2001/04/27 14:37:11  neil
334  ** wheeee
335  **
336  ** Revision 1.1.1.1  2001/04/27 07:03:54  neil
337  ** initial
338  **
339  ** Revision 1.8  2000/11/21 13:28:40  matt
340  ** take care to zero allocated mem
341  **
342  ** Revision 1.7  2000/11/09 14:07:28  matt
343  ** state load fixed, state save mostly fixed
344  **
345  ** Revision 1.6  2000/10/28 14:24:54  matt
346  ** where did I put that underscore?
347  **
348  ** Revision 1.5  2000/10/27 12:56:35  matt
349  ** api change for ppu palette functions
350  **
351  ** Revision 1.4  2000/10/26 22:51:44  matt
352  ** correct NULL filename handling
353  **
354  ** Revision 1.3  2000/10/25 01:23:08  matt
355  ** basic system autodetection
356  **
357  ** Revision 1.2  2000/10/25 00:23:16  matt
358  ** makefiles updated for new directory structure
359  **
360  ** Revision 1.1  2000/10/24 12:20:28  matt
361  ** changed directory structure
362  **
363  ** Revision 1.19  2000/10/21 14:35:58  matt
364  ** typo
365  **
366  ** Revision 1.18  2000/10/17 03:22:37  matt
367  ** cleaning up rom module
368  **
369  ** Revision 1.17  2000/10/10 13:58:13  matt
370  ** stroustrup squeezing his way in the door
371  **
372  ** Revision 1.16  2000/10/10 13:03:54  matt
373  ** Mr. Clean makes a guest appearance
374  **
375  ** Revision 1.15  2000/07/31 04:28:46  matt
376  ** one million cleanups
377  **
378  ** Revision 1.14  2000/07/30 04:31:26  matt
379  ** automagic loading of the nofrendo intro
380  **
381  ** Revision 1.13  2000/07/25 02:20:58  matt
382  ** cleanups
383  **
384  ** Revision 1.12  2000/07/20 01:53:27  matt
385  ** snprintf() ain't no standard function, eh?
386  **
387  ** Revision 1.11  2000/07/19 16:06:54  neil
388  ** little error fixed (tempinfo vs rominfo->info)
389  **
390  ** Revision 1.10  2000/07/19 15:59:39  neil
391  ** PATH_MAX, strncpy, snprintf, and strncat are our friends
392  **
393  ** Revision 1.9  2000/07/17 01:52:27  matt
394  ** made sure last line of all source files is a newline
395  **
396  ** Revision 1.8  2000/07/06 16:47:50  matt
397  ** new ppu palette setting calls
398  **
399  ** Revision 1.7  2000/07/05 23:21:54  neil
400  ** fclose(fp) should not be done if fp == NULL
401  **
402  ** Revision 1.6  2000/07/04 04:45:14  matt
403  ** changed include
404  **
405  ** Revision 1.5  2000/06/26 04:56:10  matt
406  ** minor cleanup
407  **
408  ** Revision 1.4  2000/06/09 15:12:25  matt
409  ** initial revision
410  **
411  */