Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / rom.c
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
47 static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5);
48
49 static _romdatabase g_romdatabase;
50
51 /* Global loaded rom memory space. */
52 unsigned char* rom = NULL;
53 /* Global loaded rom size. */
54 int rom_size = 0;
55
56 unsigned char isGoldeneyeRom = 0;
57
58 m64p_rom_header   ROM_HEADER;
59 rom_params        ROM_PARAMS;
60 m64p_rom_settings ROM_SETTINGS;
61
62 static m64p_system_type rom_country_code_to_system_type(unsigned short country_code);
63 static int rom_system_type_to_ai_dac_rate(m64p_system_type system_type);
64 static 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. */
67 static 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  */
86 static 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
120 m64p_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
220 m64p_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.
239 static 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.
265 static 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
279 static 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
296 void 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
483 void 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
500 static 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
518 romdatabase_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