/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus - romchache.c * * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * * Copyright (C) 2008 Tillin9 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include #include "winlnxdefs.h" #include "romcache.h" #include "config.h" #include "rom.h" #include "main.h" #include "translate.h" #include "../opengl/osd.h" #define DEFAULT 16 /* Rom database necessary even in GUI=NONE due to Eeprom issue for ROM loading. * Possible SoftHack or other necessary compatibility settings as well. */ void romdatabase_open(void); void romdatabase_close(void); _romdatabase g_romdatabase; romdatabase_entry empty_entry; /* All RCS thread and caching functions aren't needed if compiling GUI=NONE */ #ifndef NO_GUI #include #include #include /* PATH_MAX */ #include /* Directory support. */ #include /* Includes for POSIX file status.*/ #include #include "7zip/Types.h" #include "7zip/7zCrc.h" #include "../memory/memory.h" /* sl() macro for CRCs. */ #include "md5.h" #include "gui.h" #include "util.h" #include #include #define UPDATE_FREQUENCY 12 #define RCS_VERSION "RCS1.1" rom_cache g_romcache; static char cache_filename[PATH_MAX]; static const char* romextensions[] = { ".v64", ".z64", ".n64", ".gz", ".zip", ".bz2", ".lzma", ".7z", NULL }; static void scan_dir(const char* dirname, int* romcounter); int load_initial_cache(void); static void clear_cache(void) { cache_entry *entry, *entrynext; if(g_romcache.length!=0) { entry = g_romcache.top; do { entrynext = entry->next; free(entry); entry = entrynext; --g_romcache.length; } while (entry!=NULL); g_romcache.last = NULL; } } static int write_cache_file(void) { gzFile* gzfile; if((gzfile = gzopen(cache_filename, "wb"))==NULL) { printf("Could not create cache file %s\n", cache_filename); return 0; } int i = 0; gzwrite(gzfile, &i, sizeof(int)); gzwrite(gzfile, RCS_VERSION, 6*sizeof(char)); gzwrite(gzfile, &g_romcache.length, sizeof(unsigned int)); if(g_romcache.length!=0) { cache_entry* entry; entry = g_romcache.top; do { gzwrite(gzfile, entry->md5, 16*sizeof(md5_byte_t)); gzwrite(gzfile, &entry->timestamp, sizeof(time_t)); gzwrite(gzfile, entry->filename, PATH_MAX*sizeof(char)); gzwrite(gzfile, entry->usercomments, COMMENT_MAXLENGTH*sizeof(char)); gzwrite(gzfile, &entry->internalname, 80*sizeof(char)); gzwrite(gzfile, &entry->compressiontype, sizeof(unsigned char)); gzwrite(gzfile, &entry->imagetype, sizeof(unsigned char)); gzwrite(gzfile, &entry->cic, sizeof(unsigned char)); gzwrite(gzfile, &entry->countrycode, sizeof(unsigned short)); gzwrite(gzfile, &entry->archivefile, sizeof(unsigned int)); gzwrite(gzfile, &entry->crc1, sizeof(unsigned int)); gzwrite(gzfile, &entry->crc2, sizeof(unsigned int)); gzwrite(gzfile, &entry->romsize, sizeof(int)); entry = entry->next; } while (entry!=NULL); } gzclose(gzfile); return 1; } static void rebuild_cache_file(void) { char path[PATH_MAX], buffer[PATH_MAX]; char* file; unsigned char match; int counter, oldlength; struct stat filestatus; /* Scrub cache, if user removed rom directories remove roms. */ cache_entry *entry, *entryprevious, *entrynext; if(g_romcache.length!=0) { oldlength = g_romcache.length; entryprevious = entry = g_romcache.top; while (entry != NULL) { for(match = counter = 0; counter < config_get_number("NumRomDirs", 0); ++counter) { snprintf(buffer, sizeof(buffer), "RomDirectory[%d]", counter); snprintf(path, sizeof(path), "%s", config_get_string(buffer, "/")); if(config_get_bool("RomDirsScanRecursive", FALSE)) { if(strncmp(entry->filename, path, strlen(path))==0) { match = 1; break; } } else { file = dirfrompath(entry->filename); if(strlen(file)==strlen(path)&&strncmp(file, path, strlen(path))==0) { free(file); match = 1; break; } free(file); } } /* Check rom is valid. */ if(!match||stat(entry->filename, &filestatus)==-1) { if(entry==g_romcache.top) g_romcache.top = entry->next; if(entryprevious) entryprevious->next = entry->next; entrynext = entry->next; entry = NULL; free(entry); --g_romcache.length; } else { entrynext = entry->next; entryprevious = entry; } entry = entrynext; }; if(g_romcache.length!=oldlength) g_romcache.clear = 1; g_romcache.last = entryprevious; } int romcounter = 0; /* Re-scan directories. */ for(counter = 0; counter < config_get_number("NumRomDirs", 0); ++counter) { snprintf(buffer, sizeof(buffer), "RomDirectory[%d]", counter); snprintf(path, sizeof(path), "%s", config_get_string(buffer, "")); if(path[strlen(path)-1]!='/') { strncat(path, "/", sizeof(path)); path[sizeof(path)-1] = '\0'; } printf("Scanning... %s\n", path); scan_dir(path, &romcounter); } write_cache_file(); } /* Entry point for the RCS thread. Sets up local variables and enters task loop. * After initially loading and rescanning, thread sleeps unless asked to refresh * cache (generally by user action in the GUI). */ int rom_cache_system(void* _arg) { char* buffer; g_romcache.rcspause = 0; g_romcache.clear = 0; /* Setup job parser. */ while (g_romcache.rcstask != RCS_SHUTDOWN) { /* Uncomment to debug RCS task. */ /* printf("Task: %d\n", g_romcache.rcstask); */ switch(g_romcache.rcstask) { case RCS_INIT: g_romcache.rcstask = RCS_BUSY; buffer = (char*)config_get_string("RomCacheFile", NULL); if(buffer==NULL) { buffer = (char*)malloc(PATH_MAX*sizeof(char)); snprintf(buffer, PATH_MAX, "%s%s", get_configpath(), "rombrowser.cache"); config_put_string("RomCacheFile", buffer); } snprintf(cache_filename, sizeof(cache_filename), "%s", buffer); if(load_initial_cache()) gui_update_rombrowser(g_romcache.length, g_romcache.clear); remove(cache_filename); main_message(1, 1, 0, OSD_BOTTOM_LEFT, tr("Rescanning rom cache.")); rebuild_cache_file(); gui_update_rombrowser(g_romcache.length, g_romcache.clear); main_message(1, 1, 0, OSD_BOTTOM_LEFT, tr("Rom cache up to date. %d ROM%s."), g_romcache.length, (g_romcache.length==1) ? "" : "s"); if(g_romcache.rcstask==RCS_BUSY) g_romcache.rcstask = RCS_SLEEP; break; case RCS_WRITE_CACHE: g_romcache.rcstask = RCS_BUSY; remove(cache_filename); write_cache_file(); if(g_romcache.rcstask==RCS_BUSY) g_romcache.rcstask = RCS_SLEEP; break; case RCS_RESCAN: g_romcache.rcstask = RCS_BUSY; main_message(1, 1, 0, OSD_BOTTOM_LEFT, tr("Rescanning rom cache.")); rebuild_cache_file(); gui_update_rombrowser(g_romcache.length, g_romcache.clear); main_message(1, 1, 0, OSD_BOTTOM_LEFT, tr("Rom cache up to date. %d ROM%s."), g_romcache.length, (g_romcache.length==1) ? "" : "s"); g_romcache.clear = 0; if(g_romcache.rcstask==RCS_BUSY) g_romcache.rcstask = RCS_SLEEP; break; case RCS_SLEEP: SDL_Delay(100); /* Sleep to not use any CPU power. */ break; } } printf("Rom cache system terminated!\n"); clear_cache(); SDL_WaitThread(g_RomCacheThread, NULL); return 1; } /* Given a pointer to the memory space of a valid ROM, and its size in entry->romsize * fill md5, inientry, countrycode, crc1, crc2, internalname, and cic properties of entry. */ void fill_entry(cache_entry* entry, unsigned char* localrom) { /* Compute md5. */ md5_state_t state; md5_init(&state); md5_append(&state, (const md5_byte_t *)localrom, entry->romsize); md5_finish(&state, entry->md5); entry->inientry = ini_search_by_md5(entry->md5); /* See rom.h for header layout. */ entry->countrycode = (unsigned short)*(localrom+0x3E); entry->crc1 = sl(*(unsigned int*)(localrom+0x10)); entry->crc2 = sl(*(unsigned int*)(localrom+0x14)); /* Internal name is encoded in SHIFT-JIS. Attempt to convert to UTF-8 so that GUIs and (ideally) Rice can use this for Japanese titles in a moderm environment. */ iconv_t conversion = iconv_open ("UTF-8", "SHIFT-JIS"); if(conversion==(iconv_t)-1) snprintf(entry->internalname, 20, "%s", (char*)localrom+0x20); else { char *shiftjis, *shiftjisstart, *utf8, *utf8start; size_t shiftjislength = 20; size_t utf8length = 80; shiftjisstart = shiftjis = (char*)calloc(20,sizeof(char)); utf8start = utf8 = (char*)calloc(81,sizeof(char)); snprintf(shiftjis, 20, "%s", (char*)localrom+0x20); iconv(conversion, &shiftjis, &shiftjislength, &utf8, &utf8length); iconv_close(conversion); snprintf(entry->internalname, sizeof(entry->internalname), "%s", utf8start); free(shiftjisstart); free(utf8start); } /* Detect CIC copy protection boot chip by CRCing the boot code. */ long long CRC = 0; int counter; for(counter = 0x40/4; counter < 0x1000/4; ++counter) CRC += ((unsigned int*)localrom)[counter]; switch(CRC) { case 0x000000A0F26F62FELL: entry->cic = CIC_NUS_6101; break; case 0x000000A316ADC55ALL: entry->cic = CIC_NUS_6102; break; case 0x000000A9229D7C45LL: entry->cic = CIC_NUS_6103; break; case 0x000000F8B860ED00LL: entry->cic = CIC_NUS_6105; break; case 0x000000BA5BA4B8CDLL: entry->cic = CIC_NUS_6106; break; default: entry->cic = CIC_NUS_6102; } } static void scan_dir(const char* directoryname, int* romcounter) { char filename[PATH_MAX]; char fullpath[PATH_MAX]; char* extension; DIR* directory; struct dirent* directoryentry; struct stat filestatus; cache_entry* entry; int found; directory = opendir(directoryname); if(!directory) { printf("Could not open directory '%s': %s\n", directoryname, strerror(errno)); return; } int counter; while ((directoryentry = readdir(directory))) { if(directoryentry->d_name[0]=='.') /* Skip .,.., or hidden files. */ continue; snprintf(filename, PATH_MAX-1, "%s%s", directoryname, directoryentry->d_name); #ifdef __WIN32__ snprintf(fullpath, PATH_MAX-1, "%s", filename); #else /* Use real path (maybe it's a link) */ if(realpath(filename, fullpath)) snprintf(fullpath, PATH_MAX-1, "%s", filename); #endif /* If we can't get information, move to next file. */ if(stat(fullpath, &filestatus)==-1) { printf("Could not open file '%s': %s\n", fullpath, strerror(errno)); continue; } /* Handle recursive scanning. */ if(config_get_bool("RomDirsScanRecursive", 0)) { if(S_ISDIR(filestatus.st_mode)) { strncat(filename, "/", sizeof(filename)); filename[sizeof(filename)-1] = '\0'; scan_dir(filename, romcounter); continue; } } /* Check if file has a supported extension. */ extension = strrchr(filename, '.'); if(!extension) continue; for(counter = 0; romextensions[counter]; ++counter) { if(!strcasecmp(extension,romextensions[counter])) break; } if(!romextensions[counter]) continue; found = 0; /* Found controls whether its found in the cache. */ if(g_romcache.length!=0) { entry = g_romcache.top; do { if(strncmp(entry->filename, filename, PATH_MAX)==0) { found = 1; break; } entry = entry->next; } while (entry != NULL); } entry = (cache_entry*)calloc(1,sizeof(cache_entry)); if(entry==NULL) { fprintf(stderr, "%s, %d: Out of memory!\n", __FILE__, __LINE__); continue; } if(found==0) { snprintf(entry->filename, PATH_MAX, "%s", filename); unsigned char* localrom; /* Uncomment to debug file handling. */ /* printf("File: %s\n", filename); */ UInt32 blockIndex = 0xFFFFFFFF; Byte* outBuffer = NULL; size_t outBufferSize = 0; CFileInStream archiveStream; archiveStream.File=NULL; archiveStream.InStream.Read = SzFileReadImp; archiveStream.InStream.Seek = SzFileSeekImp; CArchiveDatabaseEx db; SzArDbExInit(&db); CrcGenerateTable(); unsigned int archivefile = 0; /* Testload to see if we're a valid rom. Functions in rom.c. */ if((localrom=load_single_rom(filename, &entry->romsize, &entry->compressiontype, &entry->romsize))==NULL) { if((localrom=load_archive_rom(filename, &entry->romsize, &entry->compressiontype, &entry->romsize, &archivefile, &blockIndex, &outBuffer, &outBufferSize, &archiveStream, &db))==NULL) { free(entry); continue; } } /* Multi-rom archive? */ int multi; do { multi = 0; swap_rom(localrom, &entry->imagetype, entry->romsize); fill_entry(entry, localrom); free(localrom); entry->timestamp = filestatus.st_mtime; entry->archivefile = archivefile; ++archivefile; /* Actually add rom to cache. */ if(g_romcache.length==0) { g_romcache.top = entry; g_romcache.last = entry; ++g_romcache.length; } else { g_romcache.last->next = entry; g_romcache.last = entry; ++g_romcache.length; } printf("Added ROM: %s\n", entry->inientry->goodname); ++*romcounter; if(*romcounter%UPDATE_FREQUENCY==0) { gui_update_rombrowser(g_romcache.length, g_romcache.clear); main_message(1, 1, 0, OSD_BOTTOM_LEFT, tr("Added ROMs %d-%d."), g_romcache.length-UPDATE_FREQUENCY+1, g_romcache.length); write_cache_file(); } if(entry->compressiontype==ZIP_COMPRESSION||entry->compressiontype==SZIP_COMPRESSION) { entry = (cache_entry*)calloc(1, sizeof(cache_entry)); snprintf(entry->filename, PATH_MAX, "%s", filename); if((localrom=load_archive_rom(filename, &entry->romsize, &entry->compressiontype, &entry->romsize, &archivefile, &blockIndex, &outBuffer, &outBufferSize, &archiveStream, &db))!=NULL) { multi = 1; if(entry==NULL) { fprintf(stderr, "%s, %d: Out of memory!\n", __FILE__, __LINE__); continue; } } } } while (multi); if(outBuffer) free(outBuffer); if(archiveStream.File) fclose(archiveStream.File); SzArDbExFree(&db, free); } /* Pause if user open a ROM and starts emulation. Toggled in rom.c. */ while (g_romcache.rcspause) SDL_Delay(100); } closedir(directory); } int load_initial_cache(void) { int counter; char header[6]; cache_entry *entry; struct stat filestatus; gzFile* gzfile; if((gzfile = gzopen(cache_filename,"rb"))==NULL) { printf("Could not open cachefile %s\n", cache_filename); return 0; } gzread(gzfile, &header, sizeof(int)); if(!gzread(gzfile, &header, 6*sizeof(char))||strncmp(header, RCS_VERSION,6)!=0) { printf("Rom cache corrupt or from previous version.\n"); return 0; } gzread(gzfile, &g_romcache.length, sizeof(unsigned int)); for(counter = 0; counter < g_romcache.length; ++counter) { entry = (cache_entry*)calloc(1, sizeof(cache_entry)); if(!entry) { fprintf(stderr, "%s, %d: Out of memory!\n", __FILE__, __LINE__); return 0; } gzread(gzfile, entry->md5, 16*sizeof(md5_byte_t)); gzread(gzfile, &entry->timestamp, sizeof(time_t)); gzread(gzfile, entry->filename, PATH_MAX*sizeof(char)); gzread(gzfile, entry->usercomments, COMMENT_MAXLENGTH*sizeof(char)); gzread(gzfile, entry->internalname, 80*sizeof(char)); gzread(gzfile, &entry->compressiontype, sizeof(unsigned char)); gzread(gzfile, &entry->imagetype, sizeof(unsigned char)); gzread(gzfile, &entry->cic, sizeof(unsigned char)); gzread(gzfile, &entry->countrycode, sizeof(unsigned short)); gzread(gzfile, &entry->archivefile, sizeof(unsigned int)); gzread(gzfile, &entry->crc1, sizeof(unsigned int)); gzread(gzfile, &entry->crc2, sizeof(unsigned int)); gzread(gzfile, &entry->romsize, sizeof(int)); /* Check rom is valid. */ if(stat(entry->filename, &filestatus)==-1) { free(entry); /* If we can't get into make sure to */ --counter; /* Update counters before continuing. */ --g_romcache.length; continue; } entry->next = NULL; if(entry->timestamp==filestatus.st_mtime) { /* Actually add rom to cache. */ entry->inientry = ini_search_by_md5(entry->md5); if(counter==0) { g_romcache.top = entry; g_romcache.last = entry; } else { g_romcache.last->next = entry; g_romcache.last = entry; } /* Uncomment to debug which roms are loaded from cache. */ /* printf("Added from cache: %d %s\n", counter, entry->inientry->goodname); */ } } gzclose(gzfile); return 1; } #endif /* Convert two letters representing hexidecimal to the appropriate value: 00->0 - FF->255. */ unsigned char hexconvert(const char* bigraph) { unsigned char returnvalue = 0; char character; int digits; for(digits = 2; digits > 0; --digits) { returnvalue = returnvalue << 4; character = *bigraph++ | 0x20; if(character>='0'&&character<='9') returnvalue += character - '0'; else if(character>='a'&&character<='f') returnvalue += character - 'a' + 10; } return returnvalue; } /* Helper function, identify the space of a line before an = sign. */ static int split_property(char* string) { int counter = 0; while (string[counter] != '=' && string[counter] != '\0') ++counter; if(string[counter]=='\0') return -1; string[counter] = '\0'; return counter; } void romdatabase_open(void) { gzFile gzfile; char buffer[256]; romdatabase_search* search = NULL; romdatabase_entry* entry = NULL; int stringlength, totallength, namelength, index, counter, value; char hashtemp[3]; hashtemp[2] = '\0'; if(g_romdatabase.comment!=NULL) return; /* Setup empty_entry. */ empty_entry.goodname = ""; for(counter=0; counter<16; ++counter) empty_entry.md5[counter]=0; empty_entry.refmd5 = NULL; empty_entry.crc1 = 0; empty_entry.crc2 = 0; empty_entry.status = 0; empty_entry.savetype = DEFAULT; empty_entry.players = DEFAULT; empty_entry.rumble = DEFAULT; /* Open romdatabase. */ char* pathname = (char*)malloc(PATH_MAX*sizeof(char)); snprintf(pathname, PATH_MAX, "%s%s", get_installpath(), "mupen64plus.ini"); /* printf("Database file: %s \n", pathname); */ gzfile = gzopen(pathname, "rb"); if(gzfile==NULL) { printf("Unable to open rom database.\n"); free(pathname); return; } free(pathname); /* Move through opening comments, set romdatabase.comment to non-NULL to signal we have a database. */ totallength = 0; do { gzgets(gzfile, buffer, 255); if(buffer[0]!='[') { stringlength=strlen(buffer); totallength+=stringlength; if(g_romdatabase.comment==NULL) { g_romdatabase.comment = (char*)malloc(stringlength+2); snprintf(g_romdatabase.comment, stringlength, "%s", buffer); } else { g_romdatabase.comment = (char*)realloc(g_romdatabase.comment, totallength+2); snprintf(g_romdatabase.comment, totallength+1, "%s%s", g_romdatabase.comment, buffer); } } } while (buffer[0] != '[' && !gzeof(gzfile)); /* Clear premade indices. */ for(counter = 0; counter < 255; ++counter) g_romdatabase.crc_lists[counter] = NULL; for(counter = 0; counter < 255; ++counter) g_romdatabase.md5_lists[counter] = NULL; g_romdatabase.list = NULL; do { if(buffer[0]=='[') { if(g_romdatabase.list==NULL) { g_romdatabase.list = (romdatabase_search*)malloc(sizeof(romdatabase_search)); g_romdatabase.list->next_entry = NULL; g_romdatabase.list->next_crc = NULL; g_romdatabase.list->next_md5 = NULL; search = g_romdatabase.list; } else { search->next_entry = (romdatabase_search*)malloc(sizeof(romdatabase_search)); search = search->next_entry; search->next_entry = NULL; search->next_crc = NULL; search->next_md5 = NULL; } for (counter=0; counter < 16; ++counter) { hashtemp[0] = buffer[counter*2+1]; hashtemp[1] = buffer[counter*2+2]; search->entry.md5[counter] = hexconvert(hashtemp); } /* Index MD5s by first 8 bits. */ index = search->entry.md5[0]; if(g_romdatabase.md5_lists[index]==NULL) g_romdatabase.md5_lists[index] = search; else { romdatabase_search* aux = g_romdatabase.md5_lists[index]; search->next_md5 = aux; g_romdatabase.md5_lists[index] = search; } search->entry.goodname = NULL; search->entry.refmd5 = NULL; search->entry.crc1 = 0; search->entry.crc2 = 0; search->entry.status = 0; /* Set default to 0 stars. */ search->entry.savetype = DEFAULT; search->entry.rumble = DEFAULT; search->entry.players = DEFAULT; } else { stringlength = split_property(buffer); if(stringlength!=-1) { if(!strcmp(buffer, "GoodName")) { /* Get length of GoodName since we dynamically allocate. */ namelength = strlen(buffer+stringlength+1); search->entry.goodname = (char*)malloc(namelength*sizeof(char)); /* Make sure we're null terminated. */ if(buffer[stringlength+namelength]=='\n'||buffer[stringlength+namelength]=='\r') buffer[stringlength+namelength] = '\0'; snprintf(search->entry.goodname, namelength, "%s", buffer+stringlength+1); /* printf("Name: %s, Length: %d\n", search->entry.goodname, namelength-1); */ } else if(!strcmp(buffer, "CRC")) { buffer[stringlength+19] = '\0'; sscanf(buffer+stringlength+10, "%X", &search->entry.crc2); buffer[stringlength+9] = '\0'; sscanf(buffer+stringlength+1, "%X", &search->entry.crc1); buffer[stringlength+3] = '\0'; index = hexconvert(buffer+stringlength+1); /* Index CRCs by first 8 bits. */ if(g_romdatabase.crc_lists[index]==NULL) g_romdatabase.crc_lists[index] = search; else { romdatabase_search* aux = g_romdatabase.crc_lists[index]; search->next_crc = aux; g_romdatabase.crc_lists[index] = search; } } else if(!strcmp(buffer, "RefMD5")) { /* If we have a refernce MD5, dynamically allocate. */ search->entry.refmd5 = (md5_byte_t*)malloc(16*sizeof(md5_byte_t)); for (counter=0; counter < 16; ++counter) { hashtemp[0] = buffer[stringlength+1+counter*2]; hashtemp[1] = buffer[stringlength+2+counter*2]; search->entry.refmd5[counter] = hexconvert(hashtemp); } /* Lookup reference MD5 and replace non-default entries. */ if((entry = ini_search_by_md5(search->entry.refmd5))!=&empty_entry) { /* printf("RefMD5: %s\n", aux->goodname); */ if(entry->savetype!=DEFAULT) search->entry.savetype = entry->savetype; if(entry->status!=0) search->entry.status = entry->status; if(entry->players!=DEFAULT) search->entry.players = entry->players; if(entry->rumble!=DEFAULT) search->entry.rumble = entry->rumble; } } else if(!strcmp(buffer, "SaveType")) { if(!strncmp(buffer+stringlength+1, "Eeprom 4KB", 10)) search->entry.savetype = EEPROM_4KB; else if(!strncmp(buffer+stringlength+1, "Eeprom 16KB", 10)) search->entry.savetype = EEPROM_16KB; else if(!strncmp(buffer+stringlength+1, "SRAM", 4)) search->entry.savetype = SRAM; else if(!strncmp(buffer+stringlength+1, "Flash RAM", 9)) search->entry.savetype = FLASH_RAM; else if(!strncmp(buffer+stringlength+1, "Controller Pack", 15)) search->entry.savetype = CONTROLLER_PACK; else if(!strncmp(buffer+stringlength+1, "None", 4)) search->entry.savetype = NONE; } else if(!strcmp(buffer, "Status")) { value = (unsigned char)atoi(buffer+stringlength+1); if(value>-1&&value<6) search->entry.status = value; } else if(!strcmp(buffer, "Players")) { value = (unsigned char)atoi(buffer+stringlength+1); if(value>-1&&value<8) search->entry.players = value; } else if(!strcmp(buffer, "Rumble")) { if(!strncmp(buffer+stringlength+1, "Yes", 3)) search->entry.rumble = 1; if(!strncmp(buffer+stringlength+1, "No", 2)) search->entry.rumble = 0; } } } gzgets(gzfile, buffer, 255); } while (!gzeof(gzfile)); gzclose(gzfile); } void romdatabase_close(void) { if (g_romdatabase.comment == NULL) return; free(g_romdatabase.comment); while (g_romdatabase.list != NULL) { romdatabase_search* search = g_romdatabase.list->next_entry; if(g_romdatabase.list->entry.goodname) free(g_romdatabase.list->entry.goodname); if(g_romdatabase.list->entry.refmd5) free(g_romdatabase.list->entry.refmd5); free(g_romdatabase.list); g_romdatabase.list = search; } } romdatabase_entry* ini_search_by_md5(md5_byte_t* md5) { if(g_romdatabase.comment==NULL) return &empty_entry; romdatabase_search* search; search = g_romdatabase.md5_lists[md5[0]]; while (search != NULL && memcmp(search->entry.md5, md5, 16) != 0) search = search->next_md5; if(search==NULL) return &empty_entry; else return &(search->entry); } romdatabase_entry* ini_search_by_crc(unsigned int crc1, unsigned int crc2) { if(g_romdatabase.comment==NULL) return &empty_entry; romdatabase_search* search; search = g_romdatabase.crc_lists[((crc1 >> 24) & 0xff)]; while (search != NULL && search->entry.crc1 != crc1 && search->entry.crc2 != crc2) search = search->next_crc; if(search == NULL) return &empty_entry; else return &(search->entry); }