1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - rom.c *
3 * Mupen64Plus homepage: https://mupen64plus.org/ *
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 <ctype.h>
24#include <stddef.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#define __STDC_FORMAT_MACROS
31#include <inttypes.h>
32
33#define M64P_CORE_PROTOTYPES 1
34#include "api/callbacks.h"
35#include "api/config.h"
36#include "api/m64p_config.h"
37#include "api/m64p_types.h"
38#include "device/device.h"
39#include "main.h"
40#include "md5.h"
41#include "osal/preproc.h"
42#include "osd/osd.h"
43#include "rom.h"
44#include "util.h"
45
46#define CHUNKSIZE 1024*128 /* Read files 128KB at a time. */
47
48/* Number of cpu cycles per instruction */
49enum { DEFAULT_COUNT_PER_OP = 2 };
50/* by default, extra mem is enabled */
51enum { DEFAULT_DISABLE_EXTRA_MEM = 0 };
52/* Default SI DMA duration */
53enum { DEFAULT_SI_DMA_DURATION = 0x900 };
54
55static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5);
56
57static _romdatabase g_romdatabase;
58
59/* Global loaded rom size. */
60int g_rom_size = 0;
61
62m64p_rom_header ROM_HEADER;
63rom_params ROM_PARAMS;
64m64p_rom_settings ROM_SETTINGS;
65
66static m64p_system_type rom_country_code_to_system_type(uint16_t country_code);
67
68static const uint8_t Z64_SIGNATURE[4] = { 0x80, 0x37, 0x12, 0x40 };
69static const uint8_t V64_SIGNATURE[4] = { 0x37, 0x80, 0x40, 0x12 };
70static const uint8_t N64_SIGNATURE[4] = { 0x40, 0x12, 0x37, 0x80 };
71
72/* Tests if a file is a valid N64 rom by checking the first 4 bytes. */
73static int is_valid_rom(const unsigned char *buffer)
74{
75 if (memcmp(buffer, Z64_SIGNATURE, sizeof(Z64_SIGNATURE)) == 0
76 || memcmp(buffer, V64_SIGNATURE, sizeof(V64_SIGNATURE)) == 0
77 || memcmp(buffer, N64_SIGNATURE, sizeof(N64_SIGNATURE)) == 0)
78 return 1;
79 else
80 return 0;
81}
82
83/* Copies the source block of memory to the destination block of memory while
84 * switching the endianness of .v64 and .n64 images to the .z64 format, which
85 * is native to the Nintendo 64. The data extraction routines and MD5 hashing
86 * function may only act on the .z64 big-endian format.
87 *
88 * IN: src: The source block of memory. This must be a valid Nintendo 64 ROM
89 * image of 'len' bytes.
90 * len: The length of the source and destination, in bytes.
91 * OUT: dst: The destination block of memory. This must be a valid buffer for
92 * at least 'len' bytes.
93 * imagetype: A pointer to a byte that gets updated with the value of
94 * V64IMAGE, N64IMAGE or Z64IMAGE according to the format of
95 * the source block. The value is undefined if 'src' does not
96 * represent a valid Nintendo 64 ROM image.
97 */
98static void swap_copy_rom(void* dst, const void* src, size_t len, unsigned char* imagetype)
99{
100 if (memcmp(src, V64_SIGNATURE, sizeof(V64_SIGNATURE)) == 0)
101 {
102 size_t i;
103 const uint16_t* src16 = (const uint16_t*) src;
104 uint16_t* dst16 = (uint16_t*) dst;
105
106 *imagetype = V64IMAGE;
107 /* .v64 images have byte-swapped half-words (16-bit). */
108 for (i = 0; i < len; i += 2)
109 {
110 *dst16++ = m64p_swap16(*src16++);
111 }
112 }
113 else if (memcmp(src, N64_SIGNATURE, sizeof(N64_SIGNATURE)) == 0)
114 {
115 size_t i;
116 const uint32_t* src32 = (const uint32_t*) src;
117 uint32_t* dst32 = (uint32_t*) dst;
118
119 *imagetype = N64IMAGE;
120 /* .n64 images have byte-swapped words (32-bit). */
121 for (i = 0; i < len; i += 4)
122 {
123 *dst32++ = m64p_swap32(*src32++);
124 }
125 }
126 else {
127 *imagetype = Z64IMAGE;
128 memcpy(dst, src, len);
129 }
130}
131
132m64p_error open_rom(const unsigned char* romimage, unsigned int size)
133{
134 md5_state_t state;
135 md5_byte_t digest[16];
136 romdatabase_entry* entry;
137 char buffer[256];
138 unsigned char imagetype;
139 int i;
140
141 /* check input requirements */
142 if (romimage == NULL || !is_valid_rom(romimage))
143 {
144 DebugMessage(M64MSG_ERROR, "open_rom(): not a valid ROM image");
145 return M64ERR_INPUT_INVALID;
146 }
147
148 /* Clear Byte-swapped flag, since ROM is now deleted. */
149 g_RomWordsLittleEndian = 0;
150 /* allocate new buffer for ROM and copy into this buffer */
151 g_rom_size = size;
152 swap_copy_rom((uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM), romimage, size, &imagetype);
153 /* ROM is now in N64 native (big endian) byte order */
154
155 memcpy(&ROM_HEADER, (uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM), sizeof(m64p_rom_header));
156
157 /* Calculate MD5 hash */
158 md5_init(&state);
159 md5_append(&state, (const md5_byte_t*)((uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM)), g_rom_size);
160 md5_finish(&state, digest);
161 for ( i = 0; i < 16; ++i )
162 sprintf(buffer+i*2, "%02X", digest[i]);
163 buffer[32] = '\0';
164 strcpy(ROM_SETTINGS.MD5, buffer);
165
166 /* add some useful properties to ROM_PARAMS */
167 ROM_PARAMS.systemtype = rom_country_code_to_system_type(ROM_HEADER.Country_code);
168 ROM_PARAMS.countperop = DEFAULT_COUNT_PER_OP;
169 ROM_PARAMS.disableextramem = DEFAULT_DISABLE_EXTRA_MEM;
170 ROM_PARAMS.sidmaduration = DEFAULT_SI_DMA_DURATION;
171 ROM_PARAMS.cheats = NULL;
172
173 memcpy(ROM_PARAMS.headername, ROM_HEADER.Name, 20);
174 ROM_PARAMS.headername[20] = '\0';
175 trim(ROM_PARAMS.headername); /* Remove trailing whitespace from ROM name. */
176
177 /* Look up this ROM in the .ini file and fill in goodname, etc */
178 if ((entry=ini_search_by_md5(digest)) != NULL ||
179 (entry=ini_search_by_crc(tohl(ROM_HEADER.CRC1),tohl(ROM_HEADER.CRC2))) != NULL)
180 {
181 strncpy(ROM_SETTINGS.goodname, entry->goodname, 255);
182 ROM_SETTINGS.goodname[255] = '\0';
183 ROM_SETTINGS.savetype = entry->savetype;
184 ROM_SETTINGS.status = entry->status;
185 ROM_SETTINGS.players = entry->players;
186 ROM_SETTINGS.rumble = entry->rumble;
187 ROM_SETTINGS.transferpak = entry->transferpak;
188 ROM_SETTINGS.mempak = entry->mempak;
189 ROM_SETTINGS.biopak = entry->biopak;
190 ROM_PARAMS.countperop = entry->countperop;
191 ROM_PARAMS.disableextramem = entry->disableextramem;
192 ROM_PARAMS.sidmaduration = entry->sidmaduration;
193 ROM_PARAMS.cheats = entry->cheats;
194 }
195 else
196 {
197 strcpy(ROM_SETTINGS.goodname, ROM_PARAMS.headername);
198 strcat(ROM_SETTINGS.goodname, " (unknown rom)");
199 ROM_SETTINGS.savetype = NONE;
200 ROM_SETTINGS.status = 0;
201 ROM_SETTINGS.players = 4;
202 ROM_SETTINGS.rumble = 1;
203 ROM_SETTINGS.transferpak = 0;
204 ROM_SETTINGS.mempak = 1;
205 ROM_SETTINGS.biopak = 0;
206 ROM_PARAMS.countperop = DEFAULT_COUNT_PER_OP;
207 ROM_PARAMS.disableextramem = DEFAULT_DISABLE_EXTRA_MEM;
208 ROM_PARAMS.sidmaduration = DEFAULT_SI_DMA_DURATION;
209 ROM_PARAMS.cheats = NULL;
210 }
211
212 /* print out a bunch of info about the ROM */
213 DebugMessage(M64MSG_INFO, "Goodname: %s", ROM_SETTINGS.goodname);
214 DebugMessage(M64MSG_INFO, "Name: %s", ROM_HEADER.Name);
215 imagestring(imagetype, buffer);
216 DebugMessage(M64MSG_INFO, "MD5: %s", ROM_SETTINGS.MD5);
217 DebugMessage(M64MSG_INFO, "CRC: %08" PRIX32 " %08" PRIX32, tohl(ROM_HEADER.CRC1), tohl(ROM_HEADER.CRC2));
218 DebugMessage(M64MSG_INFO, "Imagetype: %s", buffer);
219 DebugMessage(M64MSG_INFO, "Rom size: %d bytes (or %d Mb or %d Megabits)", g_rom_size, g_rom_size/1024/1024, g_rom_size/1024/1024*8);
220 DebugMessage(M64MSG_VERBOSE, "ClockRate = %" PRIX32, tohl(ROM_HEADER.ClockRate));
221 DebugMessage(M64MSG_INFO, "Version: %" PRIX32, tohl(ROM_HEADER.Release));
222 if(tohl(ROM_HEADER.Manufacturer_ID) == 'N')
223 DebugMessage(M64MSG_INFO, "Manufacturer: Nintendo");
224 else
225 DebugMessage(M64MSG_INFO, "Manufacturer: %" PRIX32, tohl(ROM_HEADER.Manufacturer_ID));
226 DebugMessage(M64MSG_VERBOSE, "Cartridge_ID: %" PRIX16, ROM_HEADER.Cartridge_ID);
227 countrycodestring(ROM_HEADER.Country_code, buffer);
228 DebugMessage(M64MSG_INFO, "Country: %s", buffer);
229 DebugMessage(M64MSG_VERBOSE, "PC = %" PRIX32, tohl(ROM_HEADER.PC));
230 DebugMessage(M64MSG_VERBOSE, "Save type: %d", ROM_SETTINGS.savetype);
231
232 return M64ERR_SUCCESS;
233}
234
235m64p_error close_rom(void)
236{
237 /* Clear Byte-swapped flag, since ROM is now deleted. */
238 g_RomWordsLittleEndian = 0;
239 DebugMessage(M64MSG_STATUS, "Rom closed.");
240
241 return M64ERR_SUCCESS;
242}
243
244/********************************************************************************************/
245/* ROM utility functions */
246
247// Get the system type associated to a ROM country code.
248static m64p_system_type rom_country_code_to_system_type(uint16_t country_code)
249{
250 switch (country_code & UINT16_C(0xFF))
251 {
252 // PAL codes
253 case 0x44:
254 case 0x46:
255 case 0x49:
256 case 0x50:
257 case 0x53:
258 case 0x55:
259 case 0x58:
260 case 0x59:
261 return SYSTEM_PAL;
262
263 // NTSC codes
264 case 0x37:
265 case 0x41:
266 case 0x45:
267 case 0x4a:
268 default: // Fallback for unknown codes
269 return SYSTEM_NTSC;
270 }
271}
272
273static size_t romdatabase_resolve_round(void)
274{
275 romdatabase_search *entry;
276 romdatabase_entry *ref;
277 size_t skipped = 0;
278
279 /* Resolve RefMD5 references */
280 for (entry = g_romdatabase.list; entry; entry = entry->next_entry) {
281 if (!entry->entry.refmd5)
282 continue;
283
284 ref = ini_search_by_md5(entry->entry.refmd5);
285 if (!ref) {
286 DebugMessage(M64MSG_WARNING, "ROM Database: Error solving RefMD5s");
287 continue;
288 }
289
290 /* entry is not yet resolved, skip for now */
291 if (ref->refmd5) {
292 skipped++;
293 continue;
294 }
295
296 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_GOODNAME) &&
297 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_GOODNAME)) {
298 entry->entry.goodname = strdup(ref->goodname);
299 if (entry->entry.goodname)
300 entry->entry.set_flags |= ROMDATABASE_ENTRY_GOODNAME;
301 }
302
303 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_CRC) &&
304 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_CRC)) {
305 entry->entry.crc1 = ref->crc1;
306 entry->entry.crc2 = ref->crc2;
307 entry->entry.set_flags |= ROMDATABASE_ENTRY_CRC;
308 }
309
310 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_STATUS) &&
311 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_STATUS)) {
312 entry->entry.status = ref->status;
313 entry->entry.set_flags |= ROMDATABASE_ENTRY_STATUS;
314 }
315
316 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_SAVETYPE) &&
317 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_SAVETYPE)) {
318 entry->entry.savetype = ref->savetype;
319 entry->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
320 }
321
322 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_PLAYERS) &&
323 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_PLAYERS)) {
324 entry->entry.players = ref->players;
325 entry->entry.set_flags |= ROMDATABASE_ENTRY_PLAYERS;
326 }
327
328 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_RUMBLE) &&
329 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_RUMBLE)) {
330 entry->entry.rumble = ref->rumble;
331 entry->entry.set_flags |= ROMDATABASE_ENTRY_RUMBLE;
332 }
333
334 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_COUNTEROP) &&
335 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_COUNTEROP)) {
336 entry->entry.countperop = ref->countperop;
337 entry->entry.set_flags |= ROMDATABASE_ENTRY_COUNTEROP;
338 }
339
340 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_CHEATS) &&
341 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_CHEATS)) {
342 if (ref->cheats)
343 entry->entry.cheats = strdup(ref->cheats);
344 entry->entry.set_flags |= ROMDATABASE_ENTRY_CHEATS;
345 }
346
347 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_EXTRAMEM) &&
348 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_EXTRAMEM)) {
349 entry->entry.disableextramem = ref->disableextramem;
350 entry->entry.set_flags |= ROMDATABASE_ENTRY_EXTRAMEM;
351 }
352
353 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_TRANSFERPAK) &&
354 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_TRANSFERPAK)) {
355 entry->entry.transferpak = ref->transferpak;
356 entry->entry.set_flags |= ROMDATABASE_ENTRY_TRANSFERPAK;
357 }
358
359 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_MEMPAK) &&
360 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_MEMPAK)) {
361 entry->entry.mempak = ref->mempak;
362 entry->entry.set_flags |= ROMDATABASE_ENTRY_MEMPAK;
363 }
364
365 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_BIOPAK) &&
366 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_BIOPAK)) {
367 entry->entry.biopak = ref->biopak;
368 entry->entry.set_flags |= ROMDATABASE_ENTRY_BIOPAK;
369 }
370
371 if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_SIDMADURATION) &&
372 isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_SIDMADURATION)) {
373 entry->entry.sidmaduration = ref->sidmaduration;
374 entry->entry.set_flags |= ROMDATABASE_ENTRY_SIDMADURATION;
375 }
376
377 free(entry->entry.refmd5);
378 entry->entry.refmd5 = NULL;
379 }
380
381 return skipped;
382}
383
384static void romdatabase_resolve(void)
385{
386 size_t last_skipped = (size_t)~0ULL;
387 size_t skipped;
388
389 do {
390 skipped = romdatabase_resolve_round();
391 if (skipped == last_skipped) {
392 DebugMessage(M64MSG_ERROR, "Unable to resolve rom database entries (loop)");
393 break;
394 }
395 last_skipped = skipped;
396 } while (skipped > 0);
397}
398
399/********************************************************************************************/
400/* INI Rom database functions */
401
402void romdatabase_open(void)
403{
404 FILE *fPtr;
405 char buffer[256];
406 romdatabase_search* search = NULL;
407 romdatabase_search** next_search;
408
409 int counter, value, lineno;
410 unsigned char index;
411 const char *pathname = ConfigGetSharedDataFilepath("mupen64plus.ini");
412
413 if(g_romdatabase.have_database)
414 return;
415
416 /* Open romdatabase. */
417 if (pathname == NULL || (fPtr = fopen(pathname, "rb")) == NULL)
418 {
419 DebugMessage(M64MSG_ERROR, "Unable to open rom database file '%s'.", pathname);
420 return;
421 }
422
423 g_romdatabase.have_database = 1;
424
425 /* Clear premade indices. */
426 for(counter = 0; counter < 255; ++counter)
427 g_romdatabase.crc_lists[counter] = NULL;
428 for(counter = 0; counter < 255; ++counter)
429 g_romdatabase.md5_lists[counter] = NULL;
430 g_romdatabase.list = NULL;
431
432 next_search = &g_romdatabase.list;
433
434 /* Parse ROM database file */
435 for (lineno = 1; fgets(buffer, 255, fPtr) != NULL; lineno++)
436 {
437 char *line = buffer;
438 ini_line l = ini_parse_line(&line);
439 switch (l.type)
440 {
441 case INI_SECTION:
442 {
443 md5_byte_t md5[16];
444 if (!parse_hex(l.name, md5, 16))
445 {
446 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid MD5 on line %i", lineno);
447 search = NULL;
448 continue;
449 }
450
451 *next_search = (romdatabase_search*) malloc(sizeof(romdatabase_search));
452 search = *next_search;
453 next_search = &search->next_entry;
454
455 memset(search, 0, sizeof(romdatabase_search));
456
457 search->entry.goodname = NULL;
458 memcpy(search->entry.md5, md5, 16);
459 search->entry.refmd5 = NULL;
460 search->entry.crc1 = 0;
461 search->entry.crc2 = 0;
462 search->entry.status = 0; /* Set default to 0 stars. */
463 search->entry.savetype = 0;
464 search->entry.players = 4;
465 search->entry.rumble = 1;
466 search->entry.countperop = DEFAULT_COUNT_PER_OP;
467 search->entry.disableextramem = DEFAULT_DISABLE_EXTRA_MEM;
468 search->entry.cheats = NULL;
469 search->entry.transferpak = 0;
470 search->entry.mempak = 1;
471 search->entry.biopak = 0;
472 search->entry.sidmaduration = DEFAULT_SI_DMA_DURATION;
473 search->entry.set_flags = ROMDATABASE_ENTRY_NONE;
474
475 search->next_entry = NULL;
476 search->next_crc = NULL;
477 /* Index MD5s by first 8 bits. */
478 index = search->entry.md5[0];
479 search->next_md5 = g_romdatabase.md5_lists[index];
480 g_romdatabase.md5_lists[index] = search;
481
482 break;
483 }
484 case INI_PROPERTY:
485 // This happens if there's stray properties before any section,
486 // or if some error happened on INI_SECTION (e.g. parsing).
487 if (search == NULL)
488 {
489 DebugMessage(M64MSG_WARNING, "ROM Database: Ignoring property on line %i", lineno);
490 continue;
491 }
492 if(!strcmp(l.name, "GoodName"))
493 {
494 search->entry.goodname = strdup(l.value);
495 search->entry.set_flags |= ROMDATABASE_ENTRY_GOODNAME;
496 }
497 else if(!strcmp(l.name, "CRC"))
498 {
499 char garbage_sweeper;
500 if (sscanf(l.value, "%X %X%c", &search->entry.crc1,
501 &search->entry.crc2, &garbage_sweeper) == 2)
502 {
503 /* Index CRCs by first 8 bits. */
504 index = search->entry.crc1 >> 24;
505 search->next_crc = g_romdatabase.crc_lists[index];
506 g_romdatabase.crc_lists[index] = search;
507 search->entry.set_flags |= ROMDATABASE_ENTRY_CRC;
508 }
509 else
510 {
511 search->entry.crc1 = search->entry.crc2 = 0;
512 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid CRC on line %i", lineno);
513 }
514 }
515 else if(!strcmp(l.name, "RefMD5"))
516 {
517 md5_byte_t md5[16];
518 if (parse_hex(l.value, md5, 16))
519 {
520 search->entry.refmd5 = (md5_byte_t*)malloc(16*sizeof(md5_byte_t));
521 memcpy(search->entry.refmd5, md5, 16);
522 }
523 else
524 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid RefMD5 on line %i", lineno);
525 }
526 else if(!strcmp(l.name, "SaveType"))
527 {
528 if(!strcmp(l.value, "Eeprom 4KB")) {
529 search->entry.savetype = EEPROM_4KB;
530 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
531 } else if(!strcmp(l.value, "Eeprom 16KB")) {
532 search->entry.savetype = EEPROM_16KB;
533 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
534 } else if(!strcmp(l.value, "SRAM")) {
535 search->entry.savetype = SRAM;
536 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
537 } else if(!strcmp(l.value, "Flash RAM")) {
538 search->entry.savetype = FLASH_RAM;
539 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
540 } else if(!strcmp(l.value, "Controller Pack")) {
541 search->entry.savetype = CONTROLLER_PACK;
542 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
543 } else if(!strcmp(l.value, "None")) {
544 search->entry.savetype = NONE;
545 search->entry.set_flags |= ROMDATABASE_ENTRY_SAVETYPE;
546 } else {
547 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid save type on line %i", lineno);
548 }
549 }
550 else if(!strcmp(l.name, "Status"))
551 {
552 if (string_to_int(l.value, &value) && value >= 0 && value < 6) {
553 search->entry.status = value;
554 search->entry.set_flags |= ROMDATABASE_ENTRY_STATUS;
555 } else {
556 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid status on line %i", lineno);
557 }
558 }
559 else if(!strcmp(l.name, "Players"))
560 {
561 if (string_to_int(l.value, &value) && value >= 0 && value < 8) {
562 search->entry.players = value;
563 search->entry.set_flags |= ROMDATABASE_ENTRY_PLAYERS;
564 } else {
565 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid player count on line %i", lineno);
566 }
567 }
568 else if(!strcmp(l.name, "Rumble"))
569 {
570 if(!strcmp(l.value, "Yes")) {
571 search->entry.rumble = 1;
572 search->entry.set_flags |= ROMDATABASE_ENTRY_RUMBLE;
573 } else if(!strcmp(l.value, "No")) {
574 search->entry.rumble = 0;
575 search->entry.set_flags |= ROMDATABASE_ENTRY_RUMBLE;
576 } else {
577 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid rumble string on line %i", lineno);
578 }
579 }
580 else if(!strcmp(l.name, "CountPerOp"))
581 {
582 if (string_to_int(l.value, &value) && value > 0 && value <= 4) {
583 search->entry.countperop = value;
584 search->entry.set_flags |= ROMDATABASE_ENTRY_COUNTEROP;
585 } else {
586 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid CountPerOp on line %i", lineno);
587 }
588 }
589 else if (!strcmp(l.name, "DisableExtraMem"))
590 {
591 search->entry.disableextramem = atoi(l.value);
592 search->entry.set_flags |= ROMDATABASE_ENTRY_EXTRAMEM;
593 }
594 else if(!strncmp(l.name, "Cheat", 5))
595 {
596 size_t len1 = 0, len2 = 0;
597 char *newcheat;
598
599 if (search->entry.cheats)
600 len1 = strlen(search->entry.cheats);
601 if (l.value)
602 len2 = strlen(l.value);
603
604 /* initial cheat */
605 if (len1 == 0 && len2 > 0)
606 search->entry.cheats = strdup(l.value);
607
608 /* append cheat */
609 if (len1 != 0 && len2 > 0) {
610 newcheat = malloc(len1 + 1 + len2 + 1);
611 if (!newcheat) {
612 DebugMessage(M64MSG_WARNING, "ROM Database: Failed to append cheat");
613 } else {
614 strcpy(newcheat, search->entry.cheats);
615 strcat(newcheat, ";");
616 strcat(newcheat, l.value);
617 free(search->entry.cheats);
618 search->entry.cheats = newcheat;
619 }
620 }
621
622 search->entry.set_flags |= ROMDATABASE_ENTRY_CHEATS;
623 }
624 else if(!strcmp(l.name, "Transferpak"))
625 {
626 if(!strcmp(l.value, "Yes")) {
627 search->entry.transferpak = 1;
628 search->entry.set_flags |= ROMDATABASE_ENTRY_TRANSFERPAK;
629 } else if(!strcmp(l.value, "No")) {
630 search->entry.transferpak = 0;
631 search->entry.set_flags |= ROMDATABASE_ENTRY_TRANSFERPAK;
632 } else {
633 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid transferpak string on line %i", lineno);
634 }
635 }
636 else if(!strcmp(l.name, "Mempak"))
637 {
638 if(!strcmp(l.value, "Yes")) {
639 search->entry.mempak = 1;
640 search->entry.set_flags |= ROMDATABASE_ENTRY_MEMPAK;
641 } else if(!strcmp(l.value, "No")) {
642 search->entry.mempak = 0;
643 search->entry.set_flags |= ROMDATABASE_ENTRY_MEMPAK;
644 } else {
645 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid mempak string on line %i", lineno);
646 }
647 }
648 else if(!strcmp(l.name, "Biopak"))
649 {
650 if(!strcmp(l.value, "Yes")) {
651 search->entry.biopak = 1;
652 search->entry.set_flags |= ROMDATABASE_ENTRY_BIOPAK;
653 } else if(!strcmp(l.value, "No")) {
654 search->entry.biopak = 0;
655 search->entry.set_flags |= ROMDATABASE_ENTRY_BIOPAK;
656 } else {
657 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid biopak string on line %i", lineno);
658 }
659 }
660 else if(!strcmp(l.name, "SiDmaDuration"))
661 {
662 if (string_to_int(l.value, &value) && value >= 0 && value <= 0x10000) {
663 search->entry.sidmaduration = value;
664 search->entry.set_flags |= ROMDATABASE_ENTRY_SIDMADURATION;
665 } else {
666 DebugMessage(M64MSG_WARNING, "ROM Database: Invalid SiDmaDuration on line %i", lineno);
667 }
668 }
669 else
670 {
671 DebugMessage(M64MSG_WARNING, "ROM Database: Unknown property on line %i", lineno);
672 }
673 break;
674 default:
675 break;
676 }
677 }
678
679 fclose(fPtr);
680 romdatabase_resolve();
681}
682
683void romdatabase_close(void)
684{
685 if (!g_romdatabase.have_database)
686 return;
687
688 while (g_romdatabase.list != NULL)
689 {
690 romdatabase_search* search = g_romdatabase.list->next_entry;
691 if(g_romdatabase.list->entry.goodname)
692 free(g_romdatabase.list->entry.goodname);
693 if(g_romdatabase.list->entry.refmd5)
694 free(g_romdatabase.list->entry.refmd5);
695 free(g_romdatabase.list->entry.cheats);
696 free(g_romdatabase.list);
697 g_romdatabase.list = search;
698 }
699}
700
701static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5)
702{
703 romdatabase_search* search;
704
705 if(!g_romdatabase.have_database)
706 return NULL;
707
708 search = g_romdatabase.md5_lists[md5[0]];
709
710 while (search != NULL && memcmp(search->entry.md5, md5, 16) != 0)
711 search = search->next_md5;
712
713 if(search==NULL)
714 return NULL;
715
716 return &(search->entry);
717}
718
719romdatabase_entry* ini_search_by_crc(unsigned int crc1, unsigned int crc2)
720{
721 romdatabase_search* search;
722
723 if(!g_romdatabase.have_database)
724 return NULL;
725
726 search = g_romdatabase.crc_lists[((crc1 >> 24) & 0xff)];
727
728 while (search != NULL && search->entry.crc1 != crc1 && search->entry.crc2 != crc2)
729 search = search->next_crc;
730
731 if(search == NULL)
732 return NULL;
733
734 return &(search->entry);
735}
736
737
738