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