ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / rom.c
CommitLineData
451ab91e 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - rom.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2008 Tillin9 *
5 * Copyright (C) 2002 Hacktarux *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <ctype.h>
27
28#define M64P_CORE_PROTOTYPES 1
29#include "api/m64p_types.h"
30#include "api/callbacks.h"
31#include "api/config.h"
32#include "api/m64p_config.h"
33
34#include "md5.h"
35#include "rom.h"
36#include "main.h"
37#include "util.h"
38
39#include "memory/memory.h"
40#include "osal/preproc.h"
41#include "osd/osd.h"
42
43#define DEFAULT 16
44
45#define CHUNKSIZE 1024*128 /* Read files 128KB at a time. */
46
47static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5);
48
49static _romdatabase g_romdatabase;
50
51/* Global loaded rom memory space. */
52unsigned char* rom = NULL;
53/* Global loaded rom size. */
54int rom_size = 0;
55
56unsigned char isGoldeneyeRom = 0;
57
2d262872 58extern int count_per_op;
59
451ab91e 60m64p_rom_header ROM_HEADER;
61rom_params ROM_PARAMS;
62m64p_rom_settings ROM_SETTINGS;
63
64static m64p_system_type rom_country_code_to_system_type(unsigned short country_code);
65static int rom_system_type_to_ai_dac_rate(m64p_system_type system_type);
66static int rom_system_type_to_vi_limit(m64p_system_type system_type);
67
68/* Tests if a file is a valid N64 rom by checking the first 4 bytes. */
69static int is_valid_rom(const unsigned char *buffer)
70{
71 /* Test if rom is a native .z64 image with header 0x80371240. [ABCD] */
72 if((buffer[0]==0x80)&&(buffer[1]==0x37)&&(buffer[2]==0x12)&&(buffer[3]==0x40))
73 return 1;
74 /* Test if rom is a byteswapped .v64 image with header 0x37804012. [BADC] */
75 else if((buffer[0]==0x37)&&(buffer[1]==0x80)&&(buffer[2]==0x40)&&(buffer[3]==0x12))
76 return 1;
77 /* Test if rom is a wordswapped .n64 image with header 0x40123780. [DCBA] */
78 else if((buffer[0]==0x40)&&(buffer[1]==0x12)&&(buffer[2]==0x37)&&(buffer[3]==0x80))
79 return 1;
80 else
81 return 0;
82}
83
84/* If rom is a .v64 or .n64 image, byteswap or wordswap loadlength amount of
85 * rom data to native .z64 before forwarding. Makes sure that data extraction
86 * and MD5ing routines always deal with a .z64 image.
87 */
88static void swap_rom(unsigned char* localrom, unsigned char* imagetype, int loadlength)
89{
90 unsigned char temp;
91 int i;
92
93 /* Btyeswap if .v64 image. */
94 if(localrom[0]==0x37)
95 {
96 *imagetype = V64IMAGE;
97 for (i = 0; i < loadlength; i+=2)
98 {
99 temp=localrom[i];
100 localrom[i]=localrom[i+1];
101 localrom[i+1]=temp;
102 }
103 }
104 /* Wordswap if .n64 image. */
105 else if(localrom[0]==0x40)
106 {
107 *imagetype = N64IMAGE;
108 for (i = 0; i < loadlength; i+=4)
109 {
110 temp=localrom[i];
111 localrom[i]=localrom[i+3];
112 localrom[i+3]=temp;
113 temp=localrom[i+1];
114 localrom[i+1]=localrom[i+2];
115 localrom[i+2]=temp;
116 }
117 }
118 else
119 *imagetype = Z64IMAGE;
120}
121
122m64p_error open_rom(const unsigned char* romimage, unsigned int size)
123{
124 md5_state_t state;
125 md5_byte_t digest[16];
126 romdatabase_entry* entry;
127 char buffer[256];
128 unsigned char imagetype;
129 int i;
130
131 /* check input requirements */
132 if (rom != NULL)
133 {
134 DebugMessage(M64MSG_ERROR, "open_rom(): previous ROM image was not freed");
135 return M64ERR_INTERNAL;
136 }
137 if (romimage == NULL || !is_valid_rom(romimage))
138 {
139 DebugMessage(M64MSG_ERROR, "open_rom(): not a valid ROM image");
140 return M64ERR_INPUT_INVALID;
141 }
142
143 /* Clear Byte-swapped flag, since ROM is now deleted. */
144 g_MemHasBeenBSwapped = 0;
145 /* allocate new buffer for ROM and copy into this buffer */
146 rom_size = size;
147 rom = (unsigned char *) malloc(size);
148 if (rom == NULL)
149 return M64ERR_NO_MEMORY;
150 memcpy(rom, romimage, size);
151 swap_rom(rom, &imagetype, rom_size);
152
153 memcpy(&ROM_HEADER, rom, sizeof(m64p_rom_header));
154
155 /* Calculate MD5 hash */
156 md5_init(&state);
157 md5_append(&state, (const md5_byte_t*)rom, rom_size);
158 md5_finish(&state, digest);
159 for ( i = 0; i < 16; ++i )
160 sprintf(buffer+i*2, "%02X", digest[i]);
161 buffer[32] = '\0';
162 strcpy(ROM_SETTINGS.MD5, buffer);
163
164 /* add some useful properties to ROM_PARAMS */
165 ROM_PARAMS.systemtype = rom_country_code_to_system_type(ROM_HEADER.Country_code);
166 ROM_PARAMS.vilimit = rom_system_type_to_vi_limit(ROM_PARAMS.systemtype);
167 ROM_PARAMS.aidacrate = rom_system_type_to_ai_dac_rate(ROM_PARAMS.systemtype);
168
169 memcpy(ROM_PARAMS.headername, ROM_HEADER.Name, 20);
170 ROM_PARAMS.headername[20] = '\0';
171 trim(ROM_PARAMS.headername); /* Remove trailing whitespace from ROM name. */
172
173 /* Look up this ROM in the .ini file and fill in goodname, etc */
174 if ((entry=ini_search_by_md5(digest)) != NULL ||
175 (entry=ini_search_by_crc(sl(ROM_HEADER.CRC1),sl(ROM_HEADER.CRC2))) != NULL)
176 {
177 strncpy(ROM_SETTINGS.goodname, entry->goodname, 255);
178 ROM_SETTINGS.goodname[255] = '\0';
179 ROM_SETTINGS.savetype = entry->savetype;
180 ROM_SETTINGS.status = entry->status;
181 ROM_SETTINGS.players = entry->players;
182 ROM_SETTINGS.rumble = entry->rumble;
2d262872 183 delay_si = entry->delay_si;
184 count_per_op = entry->count_per_op;
451ab91e 185 }
186 else
187 {
188 strcpy(ROM_SETTINGS.goodname, ROM_PARAMS.headername);
189 strcat(ROM_SETTINGS.goodname, " (unknown rom)");
190 ROM_SETTINGS.savetype = NONE;
191 ROM_SETTINGS.status = 0;
192 ROM_SETTINGS.players = 0;
193 ROM_SETTINGS.rumble = 0;
2d262872 194 delay_si = -1;
195 count_per_op = -1;
196 }
451ab91e 197
198 /* print out a bunch of info about the ROM */
199 DebugMessage(M64MSG_INFO, "Goodname: %s", ROM_SETTINGS.goodname);
200 DebugMessage(M64MSG_INFO, "Name: %s", ROM_HEADER.Name);
201 imagestring(imagetype, buffer);
202 DebugMessage(M64MSG_INFO, "MD5: %s", ROM_SETTINGS.MD5);
203 DebugMessage(M64MSG_INFO, "CRC: %x %x", sl(ROM_HEADER.CRC1), sl(ROM_HEADER.CRC2));
204 DebugMessage(M64MSG_INFO, "Imagetype: %s", buffer);
205 DebugMessage(M64MSG_INFO, "Rom size: %d bytes (or %d Mb or %d Megabits)", rom_size, rom_size/1024/1024, rom_size/1024/1024*8);
206 DebugMessage(M64MSG_VERBOSE, "ClockRate = %x", sl(ROM_HEADER.ClockRate));
207 DebugMessage(M64MSG_INFO, "Version: %x", sl(ROM_HEADER.Release));
208 if(sl(ROM_HEADER.Manufacturer_ID) == 'N')
209 DebugMessage(M64MSG_INFO, "Manufacturer: Nintendo");
210 else
211 DebugMessage(M64MSG_INFO, "Manufacturer: %x", sl(ROM_HEADER.Manufacturer_ID));
212 DebugMessage(M64MSG_VERBOSE, "Cartridge_ID: %x", ROM_HEADER.Cartridge_ID);
213 countrycodestring(ROM_HEADER.Country_code, buffer);
214 DebugMessage(M64MSG_INFO, "Country: %s", buffer);
215 DebugMessage(M64MSG_VERBOSE, "PC = %x", sl((unsigned int)ROM_HEADER.PC));
216 DebugMessage(M64MSG_VERBOSE, "Save type: %d", ROM_SETTINGS.savetype);
2d262872 217 if (delay_si>=0) DebugMessage(M64MSG_INFO, "Delay SI: %d", delay_si);
218 if (count_per_op>=0) DebugMessage(M64MSG_INFO, "Count Per OP: %d", count_per_op);
451ab91e 219
220 //Prepare Hack for GOLDENEYE
221 isGoldeneyeRom = 0;
222 if(strcmp(ROM_PARAMS.headername, "GOLDENEYE") == 0)
223 isGoldeneyeRom = 1;
224
225 return M64ERR_SUCCESS;
226}
227
228m64p_error close_rom(void)
229{
230 if (rom == NULL)
231 return M64ERR_INVALID_STATE;
232
233 free(rom);
234 rom = NULL;
235
236 /* Clear Byte-swapped flag, since ROM is now deleted. */
237 g_MemHasBeenBSwapped = 0;
238 DebugMessage(M64MSG_STATUS, "Rom closed.");
239
240 return M64ERR_SUCCESS;
241}
242
243/********************************************************************************************/
244/* ROM utility functions */
245
246// Get the system type associated to a ROM country code.
247static m64p_system_type rom_country_code_to_system_type(unsigned short country_code)
248{
249 switch (country_code & 0xFF)
250 {
251 // PAL codes
252 case 0x44:
253 case 0x46:
254 case 0x49:
255 case 0x50:
256 case 0x53:
257 case 0x55:
258 case 0x58:
259 case 0x59:
260 return SYSTEM_PAL;
261
262 // NTSC codes
263 case 0x37:
264 case 0x41:
265 case 0x45:
266 case 0x4a:
267 default: // Fallback for unknown codes
268 return SYSTEM_NTSC;
269 }
270}
271
272// Get the VI (vertical interrupt) limit associated to a ROM system type.
273static int rom_system_type_to_vi_limit(m64p_system_type system_type)
274{
275 switch (system_type)
276 {
277 case SYSTEM_PAL:
278 case SYSTEM_MPAL:
279 return 50;
280
281 case SYSTEM_NTSC:
282 default:
283 return 60;
284 }
285}
286
287static int rom_system_type_to_ai_dac_rate(m64p_system_type system_type)
288{
289 switch (system_type)
290 {
291 case SYSTEM_PAL:
292 return 49656530;
293 case SYSTEM_MPAL:
294 return 48628316;
295 case SYSTEM_NTSC:
296 default:
297 return 48681812;
298 }
299}
300
301/********************************************************************************************/
302/* INI Rom database functions */
303
304void romdatabase_open(void)
305{
306 FILE *fPtr;
307 char buffer[256];
308 romdatabase_search* search = NULL;
309 romdatabase_search** next_search;
310
311 int counter, value, lineno;
312 unsigned char index;
313 const char *pathname = ConfigGetSharedDataFilepath("mupen64plus.ini");
314
315 if(g_romdatabase.have_database)
316 return;
317
318 /* Open romdatabase. */
319 if (pathname == NULL || (fPtr = fopen(pathname, "rb")) == NULL)
320 {
321 DebugMessage(M64MSG_ERROR, "Unable to open rom database file '%s'.", pathname);
322 return;
323 }
324
325 g_romdatabase.have_database = 1;
326
327 /* Clear premade indices. */
328 for(counter = 0; counter < 255; ++counter)
329 g_romdatabase.crc_lists[counter] = NULL;
330 for(counter = 0; counter < 255; ++counter)
331 g_romdatabase.md5_lists[counter] = NULL;
332 g_romdatabase.list = NULL;
333
334 next_search = &g_romdatabase.list;
335
336 /* Parse ROM database file */
337 for (lineno = 1; fgets(buffer, 255, fPtr) != NULL; lineno++)
338 {
339 char *line = buffer;
340 ini_line l = ini_parse_line(&line);
341 switch (l.type)
342 {
343 case INI_SECTION:
344 {
345 md5_byte_t md5[16];
346 if (!parse_hex(l.name, md5, 16))
347 {
348 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid MD5 on line %i", lineno);
349 search = NULL;
350 continue;
351 }
352
353 *next_search = (romdatabase_search*)malloc(sizeof(romdatabase_search));
354 search = *next_search;
355 next_search = &search->next_entry;
356
357 search->entry.goodname = NULL;
358 memcpy(search->entry.md5, md5, 16);
359 search->entry.refmd5 = NULL;
360 search->entry.crc1 = 0;
361 search->entry.crc2 = 0;
362 search->entry.status = 0; /* Set default to 0 stars. */
363 search->entry.savetype = DEFAULT;
364 search->entry.players = DEFAULT;
365 search->entry.rumble = DEFAULT;
2d262872 366 /*SEB*/
367 search->entry.delay_si=-1;
368 search->entry.count_per_op=-1;
451ab91e 369
370 search->next_entry = NULL;
371 search->next_crc = NULL;
372 /* Index MD5s by first 8 bits. */
373 index = search->entry.md5[0];
374 search->next_md5 = g_romdatabase.md5_lists[index];
375 g_romdatabase.md5_lists[index] = search;
376
377 break;
378 }
379 case INI_PROPERTY:
380 // This happens if there's stray properties before any section,
381 // or if some error happened on INI_SECTION (e.g. parsing).
382 if (search == NULL)
383 {
384 DebugMessage(M64MSG_WARNING, "ROM Database: Ignoring property on line %i", lineno);
385 continue;
386 }
387 if(!strcmp(l.name, "GoodName"))
388 {
389 search->entry.goodname = strdup(l.value);
390 }
391 else if(!strcmp(l.name, "CRC"))
392 {
393 char garbage_sweeper;
394 if (sscanf(l.value, "%X %X%c", &search->entry.crc1,
395 &search->entry.crc2, &garbage_sweeper) == 2)
396 {
397 /* Index CRCs by first 8 bits. */
398 index = search->entry.crc1 >> 24;
399 search->next_crc = g_romdatabase.crc_lists[index];
400 g_romdatabase.crc_lists[index] = search;
401 }
402 else
403 {
404 search->entry.crc1 = search->entry.crc2 = 0;
405 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid CRC on line %i", lineno);
406 }
407 }
408 else if(!strcmp(l.name, "RefMD5"))
409 {
410 md5_byte_t md5[16];
411 if (parse_hex(l.value, md5, 16))
412 {
413 search->entry.refmd5 = (md5_byte_t*)malloc(16*sizeof(md5_byte_t));
414 memcpy(search->entry.refmd5, md5, 16);
415 }
416 else
417 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid RefMD5 on line %i", lineno);
418 }
419 else if(!strcmp(l.name, "SaveType"))
420 {
421 if(!strcmp(l.value, "Eeprom 4KB"))
422 search->entry.savetype = EEPROM_4KB;
423 else if(!strcmp(l.value, "Eeprom 16KB"))
424 search->entry.savetype = EEPROM_16KB;
425 else if(!strcmp(l.value, "SRAM"))
426 search->entry.savetype = SRAM;
427 else if(!strcmp(l.value, "Flash RAM"))
428 search->entry.savetype = FLASH_RAM;
429 else if(!strcmp(l.value, "Controller Pack"))
430 search->entry.savetype = CONTROLLER_PACK;
431 else if(!strcmp(l.value, "None"))
432 search->entry.savetype = NONE;
433 else
434 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid save type on line %i", lineno);
435 }
436 else if(!strcmp(l.name, "Status"))
437 {
438 if (string_to_int(l.value, &value) && value >= 0 && value < 6)
439 search->entry.status = value;
440 else
441 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid status on line %i", lineno);
442 }
443 else if(!strcmp(l.name, "Players"))
444 {
445 if (string_to_int(l.value, &value) && value >= 0 && value < 8)
446 search->entry.players = value;
447 else
448 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid player count on line %i", lineno);
449 }
450 else if(!strcmp(l.name, "Rumble"))
451 {
452 if(!strcmp(l.value, "Yes"))
453 search->entry.rumble = 1;
454 else if(!strcmp(l.value, "No"))
455 search->entry.rumble = 0;
456 else
457 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid rumble string on line %i", lineno);
458 }
2d262872 459 else if(!strcmp(l.name, "DelaySI"))
460 {
461 if(!strcmp(l.value, "True"))
462 search->entry.delay_si = 1;
463 else if(!strcmp(l.value, "False"))
464 search->entry.delay_si = 0;
465 else
466 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid DelaySI string on line %i", lineno);
467 }
468 else if(!strcmp(l.name, "CountPerOp"))
469 {
470 if (string_to_int(l.value, &value) && value >= 0 && value < 8)
471 search->entry.count_per_op = value;
472 else
473 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid CountPerOp on line %i", lineno);
474 }
451ab91e 475 else
476 {
477 DebugMessage(M64MSG_WARNING, "ROM Database: Unknown property on line %i", lineno);
478 }
479 break;
480 default:
481 break;
482 }
483 }
484
485 fclose(fPtr);
486
487 /* Resolve RefMD5 references */
488 for (search = g_romdatabase.list; search != NULL; search = search->next_entry)
489 {
490 if (search->entry.refmd5 != NULL)
491 {
492 romdatabase_entry *ref = ini_search_by_md5(search->entry.refmd5);
493 if (ref != NULL)
494 {
495 if(ref->savetype!=DEFAULT)
496 search->entry.savetype = ref->savetype;
497 if(ref->status!=0)
498 search->entry.status = ref->status;
499 if(ref->players!=DEFAULT)
500 search->entry.players = ref->players;
501 if(ref->rumble!=DEFAULT)
502 search->entry.rumble = ref->rumble;
503 }
504 else
505 DebugMessage(M64MSG_WARNING, "ROM Database: Error solving RefMD5s");
506 }
507 }
508}
509
510void romdatabase_close(void)
511{
512 if (!g_romdatabase.have_database)
513 return;
514
515 while (g_romdatabase.list != NULL)
516 {
517 romdatabase_search* search = g_romdatabase.list->next_entry;
518 if(g_romdatabase.list->entry.goodname)
519 free(g_romdatabase.list->entry.goodname);
520 if(g_romdatabase.list->entry.refmd5)
521 free(g_romdatabase.list->entry.refmd5);
522 free(g_romdatabase.list);
523 g_romdatabase.list = search;
524 }
525}
526
527static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5)
528{
529 romdatabase_search* search;
530
531 if(!g_romdatabase.have_database)
532 return NULL;
533
534 search = g_romdatabase.md5_lists[md5[0]];
535
536 while (search != NULL && memcmp(search->entry.md5, md5, 16) != 0)
537 search = search->next_md5;
538
539 if(search==NULL)
540 return NULL;
541
542 return &(search->entry);
543}
544
545romdatabase_entry* ini_search_by_crc(unsigned int crc1, unsigned int crc2)
546{
547 romdatabase_search* search;
548
549 if(!g_romdatabase.have_database)
550 return NULL;
551
552 search = g_romdatabase.crc_lists[((crc1 >> 24) & 0xff)];
553
554 while (search != NULL && search->entry.crc1 != crc1 && search->entry.crc2 != crc2)
555 search = search->next_crc;
556
557 if(search == NULL)
558 return NULL;
559
560 return &(search->entry);
561}
562
563