ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[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 extern int count_per_op;
59
60 m64p_rom_header   ROM_HEADER;
61 rom_params        ROM_PARAMS;
62 m64p_rom_settings ROM_SETTINGS;
63
64 static m64p_system_type rom_country_code_to_system_type(unsigned short country_code);
65 static int rom_system_type_to_ai_dac_rate(m64p_system_type system_type);
66 static 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. */
69 static 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  */
88 static 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
122 m64p_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;
183         delay_si = entry->delay_si;
184         count_per_op = entry->count_per_op;
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;
194         delay_si = -1;
195         count_per_op = -1;
196    }
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);
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);
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
228 m64p_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.
247 static 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.
273 static 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
287 static 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
304 void 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; 
366                         /*SEB*/
367                         search->entry.delay_si=-1;
368                         search->entry.count_per_op=-1;
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             }
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             }
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
510 void 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
527 static 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
545 romdatabase_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