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 */