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