GameDatabase / Patches:

* Made all database key comparisons case-insensitive, so that "patches" and "Patches" both work as expected, etc.
 * Applied patches should be remembered properly now when using suspend/resume and savestates (hopefully -- didn't test savestates yet).

DevNotes:
 * Reorganized all game database code into a generic interface used by the emulation core (IGameDatabase), and app-side implementation (AppGameDatabase) that loads the files and provides info in a thread-safe manner.


git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3223 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-06-18 14:27:13 +00:00
parent b5a0a7186e
commit 914474ebc9
22 changed files with 815 additions and 627 deletions

View File

@ -27,7 +27,7 @@
#include "GS.h" // for gsRegionMode #include "GS.h" // for gsRegionMode
#include "Elfheader.h" #include "Elfheader.h"
#include "ps2/BiosTools.h" #include "ps2/BiosTools.h"
#include "DataBase_Loader.h" #include "GameDatabase.h"
wxString DiscID; wxString DiscID;
@ -354,7 +354,7 @@ static __forceinline void _reloadElfInfo(wxString elfpath)
elfptr.Delete(); elfptr.Delete();
// Set the Game DataBase to the correct game based on Game Serial Code... // Set the Game DataBase to the correct game based on Game Serial Code...
if (DataBase_Loader* GameDB = AppHost_GetGameDatabase()) { if (IGameDatabase* GameDB = AppHost_GetGameDatabase()) {
wxString gameSerial = DiscID; wxString gameSerial = DiscID;
if (DiscID.IsEmpty()) { // Search for crc if no Serial Code if (DiscID.IsEmpty()) { // Search for crc if no Serial Code
gameSerial = wxString(wxsFormat( L"%8.8x", ElfCRC )); gameSerial = wxString(wxsFormat( L"%8.8x", ElfCRC ));

View File

@ -121,7 +121,7 @@ set(pcsx2Sources
COP0.cpp COP0.cpp
COP2.cpp COP2.cpp
Counters.cpp Counters.cpp
DataBase_Loader.cpp GameDatabase.cpp
Dump.cpp Dump.cpp
Elfheader.cpp Elfheader.cpp
FiFo.cpp FiFo.cpp
@ -194,7 +194,7 @@ set(pcsx2Headers
Counters.h Counters.h
Dmac.h Dmac.h
Dump.h Dump.h
DataBase_Loader.h GameDatabase.h
Elfheader.h Elfheader.h
Gif.h Gif.h
GS.h GS.h
@ -290,6 +290,7 @@ set(pcsx2GuiSources
gui/AppCorePlugins.cpp gui/AppCorePlugins.cpp
gui/AppCoreThread.cpp gui/AppCoreThread.cpp
gui/AppEventSources.cpp gui/AppEventSources.cpp
gui/AppGameDatabase.cpp
gui/AppInit.cpp gui/AppInit.cpp
gui/AppMain.cpp gui/AppMain.cpp
gui/AppRes.cpp gui/AppRes.cpp
@ -355,6 +356,7 @@ set(pcsx2GuiHeaders
gui/AppCorePlugins.h gui/AppCorePlugins.h
gui/AppEventListeners.h gui/AppEventListeners.h
gui/AppForwardDefs.h gui/AppForwardDefs.h
gui/AppGameDatabase.h
gui/ConsoleLogger.h gui/ConsoleLogger.h
gui/CpuUsageProvider.h gui/CpuUsageProvider.h
gui/Dialogs/ConfigurationDialog.h gui/Dialogs/ConfigurationDialog.h

View File

@ -1,58 +0,0 @@
#include "PrecompiledHeader.h"
#include "DataBase_Loader.h"
//------------------------------------------------------------------
// DataBase_Loader - Private Methods
//------------------------------------------------------------------
void DataBase_Loader::doError(const wxString& line, key_pair& keyPair, bool doMsg) {
if (doMsg) Console.Error("DataBase_Loader: Bad file data [%s]", line.c_str());
keyPair.Clear();
}
// Multiline Sections are in the form of:
//
// [section=value]
// content
// content
// [/section]
//
// ... where the =value part is OPTIONAL.
bool DataBase_Loader::extractMultiLine(const wxString& line, key_pair& keyPair, wxInputStream& ffile) {
if (line[0] != L'[') return false; // All multiline sections begin with a '['!
if (!line.EndsWith(L"]")) {
doError(line, keyPair, true);
return false;
}
keyPair.key = line;
// Use Mid() to strip off the left and right side brackets.
wxString midLine(line.Mid(1, line.Length()-2));
wxString lvalue(midLine.BeforeFirst(L'=').Trim(true).Trim(false));
//wxString rvalue(midLine.AfterFirst(L'=').Trim(true).Trim(false));
wxString endString;
endString.Printf( L"[/%s]", lvalue.c_str() );
while(!ffile.Eof()) {
pxReadLine( ffile, m_dest, m_intermediate );
if (m_dest == endString) break;
keyPair.value += m_dest + L"\n";
}
return true;
}
void DataBase_Loader::extract(const wxString& line, key_pair& keyPair, wxInputStream& reader) {
keyPair.Clear();
if( line.IsEmpty() ) return;
if( extractMultiLine(line, keyPair, reader) ) return;
if( !pxParseAssignmentString( line, keyPair.key, keyPair.value ) ) return;
if( keyPair.value.IsEmpty() )
doError(line, keyPair, true);
}

View File

@ -1,395 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Common.h"
#include "AppConfig.h"
#include <wx/wfstream.h>
struct key_pair;
class Game_Data;
// This was originally configured to use deque, but there appear to be no uses of deque's sole
// advantage: adding and removing items from the head of the list. So I changed it to vector
// since it is a slightly lighter weight class. --air
typedef std::vector<Game_Data> GameDataArray;
typedef std::vector<key_pair> KeyPairArray;
struct key_pair {
wxString key;
wxString value;
key_pair() {}
key_pair(const wxString& _key, const wxString& _value)
: key(_key) , value(_value) {}
void Clear() {
key.clear();
value.clear();
}
// Performs case-insensitive compare against the key value.
bool CompareKey( const wxString& cmpto ) const {
return key.CmpNoCase(cmpto) == 0;
}
bool IsOk() const {
return !key.IsEmpty();
}
wxString toString() const {
if (key[0] == '[') {
pxAssumeDev( key.EndsWith(L"]"), "Malformed multiline key detected: missing end bracket!" );
// Terminating tag must be written without the "rvalue" -- in the form of:
// [/patches]
// Use Mid() to strip off the left and right side brackets.
wxString midLine(key.Mid(1, key.Length()-2));
wxString keyLvalue(midLine.BeforeFirst(L'=').Trim(true).Trim(false));
return wxsFormat( L"%s\n%s[/%s]\n",
key.c_str(), value.c_str(), keyLvalue.c_str()
);
}
else {
// Note: 6 char padding on the l-value makes things look nicer.
return wxsFormat(L"%-6s = %s\n", key.c_str(), value.c_str() );
}
}
};
class Game_Data {
public:
wxString id; // Serial Identification Code
KeyPairArray kList; // List of all (key, value) pairs for game data
public:
Game_Data(const wxString& _id = wxEmptyString)
: id(_id) {}
void NewSerial( const wxString& _id ) {
id = _id;
kList.clear();
}
bool IsOk() const {
return !id.IsEmpty();
}
};
// DataBase_Loader:
// Give the starting Key and Value you're looking for,
// and it will extract the necessary data from the database.
// Example:
// ---------------------------------------------
// Serial = SLUS-20486
// Name = Marvel vs. Capcom 2
// Region = NTSC-U
// ---------------------------------------------
// To Load this game data, use "Serial" as the initial Key
// then specify "SLUS-20486" as the value in the constructor.
// After the constructor loads the game data, you can use the
// DataBase_Loader class's methods to get the other key's values.
// Such as dbLoader.getString("Region") returns "NTSC-U"
class DataBase_Loader {
protected:
bool isComment(const wxString& s);
void doError(const wxString& line, key_pair& keyPair, bool doMsg = false);
bool extractMultiLine(const wxString& line, key_pair& keyPair, wxInputStream& reader);
void extract(const wxString& line, key_pair& keyPair, wxInputStream& reader);
// temp areas used as buffers for accelerated loading of database content. These strings are
// allocated and grown only once, and then reused for the duration of the database loading
// process; saving thousands of heapp allocation operations.
wxString m_dest;
std::string m_intermediate;
public:
GameDataArray gList; // List of all game data
Game_Data* curGame; // Current game data (index into gList)
wxString header; // Header of the database
wxString baseKey; // Key to separate games by ("Serial")
DataBase_Loader(const wxString& file = L"GameIndex.dbf", const wxString& key = L"Serial", const wxString& value = wxEmptyString )
: baseKey( key )
{
curGame = NULL;
if (!wxFileExists(file)) {
Console.Error(L"DataBase_Loader: DataBase Not Found! [%s]", file.c_str());
}
wxFFileInputStream reader( file );
key_pair keyPair;
wxString s0;
Game_Data game;
while(!reader.Eof()) {
while(!reader.Eof()) { // Find first game
pxReadLine(reader, s0, m_intermediate);
extract(s0.Trim(true).Trim(false), keyPair, reader);
if (keyPair.CompareKey(key)) break;
header += s0 + L'\n';
}
game.NewSerial( keyPair.value );
game.kList.push_back(keyPair);
while(!reader.Eof()) { // Fill game data, find new game, repeat...
pxReadLine(reader, s0, m_intermediate);
extract(s0.Trim(true).Trim(false), keyPair, reader);
if (!keyPair.IsOk()) continue;
if (keyPair.CompareKey(key)) {
gList.push_back(game);
game.NewSerial(keyPair.value);
}
game.kList.push_back(keyPair);
}
}
if (game.IsOk()) gList.push_back(game);
if (value.IsEmpty()) return;
if (setGame(value)) Console.WriteLn(L"DataBase_Loader: Found Game! [%s]", value.c_str());
else Console.Warning(L"DataBase_Loader: Game Not Found! [%s]", value.c_str());
}
virtual ~DataBase_Loader() throw() {
// deque deletes its contents automatically.
Console.WriteLn( "(GameDB) Destroying..." );
}
// Sets the current game to the one matching the serial id given
// Returns true if game found, false if not found...
bool setGame(const wxString& id) {
GameDataArray::iterator it( gList.begin() );
for ( ; it != gList.end(); ++it) {
if (it[0].id == id) {
curGame = &it[0];
return true;
}
}
curGame = NULL;
return false;
}
// Returns true if a game is currently loaded into the database
// Returns false if otherwise (this means you need to call setGame()
// or it could mean the game was not found in the database at all...)
bool gameLoaded() {
return !!curGame;
}
// Saves changes to the database
void saveToFile(const wxString& file = L"GameIndex.dbf") {
wxFFileOutputStream writer( file );
pxWriteMultiline(writer, header);
GameDataArray::iterator it( gList.begin() );
for ( ; it != gList.end(); ++it) {
KeyPairArray::iterator i = it[0].kList.begin();
for ( ; i != it[0].kList.end(); ++i) {
pxWriteMultiline(writer, i[0].toString() );
}
pxWriteLine(writer, L"---------------------------------------------");
}
}
// Adds new game data to the database, and sets curGame to the new game...
// If searchDB is true, it searches the database to see if game already exists.
void addGame(const wxString& id, bool searchDB = true) {
if (searchDB && setGame(id)) return;
Game_Data game(id);
key_pair kp(baseKey, id);
game.kList.push_back(kp);
gList.push_back(game);
curGame = &(gList.end()-1)[0];
}
// Searches the current game's data to see if the given key exists
bool keyExists(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
return true;
}
}
}
else Console.Error("DataBase_Loader: Game not set!");
return false;
}
// Totally Deletes the specified key/pair value from the current game's data
void deleteKey(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
curGame->kList.erase(it);
return;
}
}
}
else Console.Error("DataBase_Loader: Game not set!");
}
// Gets a string representation of the 'value' for the given key
wxString getString(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
return it[0].value;
}
}
}
else Console.Error("DataBase_Loader: Game not set!");
return wxString();
}
bool sectionExists(const wxChar* key, const wxString& value) {
return keyExists( wxsFormat(L"[%s = %s]", key, value.c_str()) );
}
wxString getSection(const wxChar* key, const wxString& value) {
return getString( wxsFormat(L"[%s = %s]", key, value.c_str()) );
}
// Gets an integer representation of the 'value' for the given key
int getInt(const wxChar* key) {
return wxStrtoul(getString(key), NULL, 0);
}
// Gets a u8 representation of the 'value' for the given key
u8 getU8(const wxChar* key) {
return (u8)wxAtoi(getString(key));
}
// Gets a bool representation of the 'value' for the given key
bool getBool(const wxChar* key) {
return !!wxAtoi(getString(key));
}
wxString getString(const char* key) {
return getString(fromUTF8(key));
}
bool keyExists(const char* key) {
return keyExists(fromUTF8(key));
}
int getInt(const char* key) {
return getInt(fromUTF8(key));
}
u8 getU8(const char* key) {
return getU8(fromUTF8(key));
}
bool getBool(const char* key) {
return getBool(fromUTF8(key));
}
// Write a string value to the specified key
void writeString(const wxString& key, const wxString& value) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
it[0].value = value;
return;
}
}
key_pair tKey(key, value);
curGame->kList.push_back(tKey);
}
else Console.Error("DataBase_Loader: Game not set!");
}
// Write a bool value to the specified key
void writeBool(const wxString& key, bool value) {
writeString(key, value ? L"1" : L"0");
}
};
static wxString compatToStringWX(int compat) {
switch (compat) {
case 6: return L"Perfect";
case 5: return L"Playable";
case 4: return L"In-Game";
case 3: return L"Menu";
case 2: return L"Intro";
case 1: return L"Nothing";
default: return L"Unknown";
}
}
#define checkGamefix(gFix) { \
if (gameDB->keyExists(#gFix)) { \
SetGameFixConfig().gFix = gameDB->getBool(#gFix); \
Console.WriteLn("Loading Gamefix: " #gFix); \
gf++; \
} \
}
// Load Game Settings found in database
// (game fixes, round modes, clamp modes, etc...)
// Returns number of gamefixes set
static int loadGameSettings(DataBase_Loader* gameDB) {
if (gameDB && gameDB->gameLoaded()) {
SSE_MXCSR eeMX = EmuConfig.Cpu.sseMXCSR;
SSE_MXCSR vuMX = EmuConfig.Cpu.sseVUMXCSR;
int eeRM = eeMX.GetRoundMode();
int vuRM = vuMX.GetRoundMode();
bool rm = 0;
int gf = 0;
if (gameDB->keyExists("eeRoundMode")) { eeRM = gameDB->getInt("eeRoundMode"); rm=1; gf++; }
if (gameDB->keyExists("vuRoundMode")) { vuRM = gameDB->getInt("vuRoundMode"); rm=1; gf++; }
if (rm && eeRM<4 && vuRM<4) {
Console.WriteLn("Game DataBase: Changing roundmodes! [ee=%d] [vu=%d]", eeRM, vuRM);
SetCPUState(eeMX.SetRoundMode((SSE_RoundMode)eeRM), vuMX.SetRoundMode((SSE_RoundMode)vuRM));
}
if (gameDB->keyExists("eeClampMode")) {
int clampMode = gameDB->getInt("eeClampMode");
Console.WriteLn("Game DataBase: Changing EE/FPU clamp mode [mode=%d]", clampMode);
SetRecompilerConfig().fpuOverflow = clampMode >= 1;
SetRecompilerConfig().fpuExtraOverflow = clampMode >= 2;
SetRecompilerConfig().fpuFullMode = clampMode >= 3;
gf++;
}
if (gameDB->keyExists("vuClampMode")) {
int clampMode = gameDB->getInt("vuClampMode");
Console.WriteLn("Game DataBase: Changing VU0/VU1 clamp mode [mode=%d]", clampMode);
SetRecompilerConfig().vuOverflow = clampMode >= 1;
SetRecompilerConfig().vuExtraOverflow = clampMode >= 2;
SetRecompilerConfig().vuSignOverflow = clampMode >= 3;
gf++;
}
checkGamefix(VuAddSubHack);
checkGamefix(VuClipFlagHack);
checkGamefix(FpuCompareHack);
checkGamefix(FpuMulHack);
checkGamefix(FpuNegDivHack);
checkGamefix(XgKickHack);
checkGamefix(IPUWaitHack);
checkGamefix(EETimingHack);
checkGamefix(SkipMPEGHack);
return gf;
}
return 0;
}
extern DataBase_Loader* AppHost_GetGameDatabase();

107
pcsx2/GameDatabase.cpp Normal file
View File

@ -0,0 +1,107 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "GameDatabase.h"
// Sets the current game to the one matching the serial id given
// Returns true if game found, false if not found...
bool BaseGameDatabaseVector::setGame(const wxString& id) {
GameDataArray::iterator it( gList.begin() );
for ( ; it != gList.end(); ++it) {
if (it[0].CompareId(id)) {
curGame = &it[0];
return true;
}
}
curGame = NULL;
return false;
}
Game_Data* BaseGameDatabaseVector::createNewGame(const wxString& id) {
gList.push_back(Game_Data(id));
curGame = &(gList.end()-1)[0];
return curGame;
}
// Searches the current game's data to see if the given key exists
bool BaseGameDatabaseVector::keyExists(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
return true;
}
}
}
else Console.Error("(GameDB) Game not set!");
return false;
}
// Totally Deletes the specified key/pair value from the current game's data
void BaseGameDatabaseVector::deleteKey(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
curGame->kList.erase(it);
return;
}
}
}
else Console.Error("(GameDB) Game not set!");
}
// Gets a string representation of the 'value' for the given key
wxString BaseGameDatabaseVector::getString(const wxChar* key) {
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
return it[0].value;
}
}
}
else Console.Error("(GameDB) Game not set!");
return wxString();
}
void BaseGameDatabaseVector::writeString(const wxString& key, const wxString& value) {
if (key.CmpNoCase(m_baseKey) == 0)
curGame = createNewGame(value);
if (curGame) {
KeyPairArray::iterator it( curGame->kList.begin() );
for ( ; it != curGame->kList.end(); ++it) {
if (it[0].CompareKey(key)) {
if( value.IsEmpty() )
curGame->kList.erase(it);
else
it[0].value = value;
return;
}
}
if( !value.IsEmpty() ) {
key_pair tKey(key, value);
curGame->kList.push_back(tKey);
}
}
else Console.Error("(GameDB) Game not set!");
}
// Write a bool value to the specified key
void BaseGameDatabaseVector::writeBool(const wxString& key, bool value) {
writeString(key, value ? L"1" : L"0");
}

206
pcsx2/GameDatabase.h Normal file
View File

@ -0,0 +1,206 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Common.h"
#include "AppConfig.h"
#include <wx/wfstream.h>
struct key_pair;
class Game_Data;
typedef std::vector<key_pair> KeyPairArray;
struct key_pair {
wxString key;
wxString value;
key_pair() {}
key_pair(const wxString& _key, const wxString& _value)
: key(_key) , value(_value) {}
void Clear() {
key.clear();
value.clear();
}
// Performs case-insensitive compare against the key value.
bool CompareKey( const wxString& cmpto ) const {
return key.CmpNoCase(cmpto) == 0;
}
bool IsOk() const {
return !key.IsEmpty();
}
wxString toString() const {
if (key[0] == '[') {
pxAssumeDev( key.EndsWith(L"]"), "Malformed multiline key detected: missing end bracket!" );
// Terminating tag must be written without the "rvalue" -- in the form of:
// [/patches]
// Use Mid() to strip off the left and right side brackets.
wxString midLine(key.Mid(1, key.Length()-2));
wxString keyLvalue(midLine.BeforeFirst(L'=').Trim(true).Trim(false));
return wxsFormat( L"%s\n%s[/%s]\n",
key.c_str(), value.c_str(), keyLvalue.c_str()
);
}
else {
// Note: 6 char padding on the l-value makes things look nicer.
return wxsFormat(L"%-6s = %s\n", key.c_str(), value.c_str() );
}
}
};
class Game_Data {
public:
wxString id; // Serial Identification Code
KeyPairArray kList; // List of all (key, value) pairs for game data
public:
Game_Data(const wxString& _id = wxEmptyString)
: id(_id) {}
void NewSerial( const wxString& _id ) {
id = _id;
kList.clear();
}
bool IsOk() const {
return !id.IsEmpty();
}
// Performs a case-insensitive compare of two IDs, returns TRUE if the IDs match
// or FALSE if the ids differ in a case-insensitive way.
bool CompareId( const wxString& _id ) const
{
return id.CmpNoCase(_id) == 0;
}
};
// --------------------------------------------------------------------------------------
// IGameDatabase
// --------------------------------------------------------------------------------------
class IGameDatabase
{
public:
virtual ~IGameDatabase() throw() {}
virtual wxString getBaseKey() const=0;
virtual bool gameLoaded()=0;
virtual bool setGame(const wxString& id)=0;
virtual Game_Data* createNewGame(const wxString& id=wxEmptyString)=0;
virtual bool keyExists(const wxChar* key)=0;
virtual void deleteKey(const wxChar* key)=0;
virtual wxString getString(const wxChar* key)=0;
bool sectionExists(const wxChar* key, const wxString& value) {
return keyExists(wxsFormat(L"[%s%s%s]", key, value.IsEmpty() ? L"" : L" = ", value.c_str()));
}
wxString getSection(const wxChar* key, const wxString& value) {
return getString(wxsFormat(L"[%s%s%s]", key, value.IsEmpty() ? L"" : L" = ", value.c_str()));
}
// Gets an integer representation of the 'value' for the given key
int getInt(const wxChar* key) {
return wxStrtoul(getString(key), NULL, 0);
}
// Gets a u8 representation of the 'value' for the given key
u8 getU8(const wxChar* key) {
return (u8)wxAtoi(getString(key));
}
// Gets a bool representation of the 'value' for the given key
bool getBool(const wxChar* key) {
return !!wxAtoi(getString(key));
}
bool keyExists(const char* key) {
return keyExists(fromUTF8(key));
}
wxString getString(const char* key) {
return getString(fromUTF8(key));
}
int getInt(const char* key) {
return getInt(fromUTF8(key));
}
u8 getU8(const char* key) {
return getU8(fromUTF8(key));
}
bool getBool(const char* key) {
return getBool(fromUTF8(key));
}
// Writes a string of data associated with the specified key to the current
// game database. If the key being written is the baseKey, then a new slot is
// created to account for it, or the existing slot is set as the current game.
//
virtual void writeString(const wxString& key, const wxString& value)=0;
virtual void writeBool(const wxString& key, bool value)=0;
};
typedef std::vector<Game_Data> GameDataArray;
// --------------------------------------------------------------------------------------
// BaseGameDatabaseVector
// --------------------------------------------------------------------------------------
// [TODO] Create a version of this that uses google hashsets; should be several magnitudes
// faster that way.
class BaseGameDatabaseVector : public IGameDatabase
{
public:
GameDataArray gList; // List of all game data
Game_Data* curGame; // Current game data (index into gList)
wxString m_baseKey;
public:
BaseGameDatabaseVector()
{
curGame = NULL;
m_baseKey = L"Serial";
}
virtual ~BaseGameDatabaseVector() throw() {}
wxString getBaseKey() const { return m_baseKey; }
void setBaseKey( const wxString& key ) { m_baseKey = key; }
// Returns true if a game is currently loaded into the database
// Returns false if otherwise (this means you need to call setGame()
// or it could mean the game was not found in the database at all...)
bool gameLoaded() {
return curGame != NULL;
}
bool setGame(const wxString& id);
Game_Data* createNewGame(const wxString& id=wxEmptyString);
bool keyExists(const wxChar* key);
void deleteKey(const wxChar* key);
wxString getString(const wxChar* key);
void writeString(const wxString& key, const wxString& value);
void writeBool(const wxString& key, bool value);
};
extern IGameDatabase* AppHost_GetGameDatabase();

View File

@ -204,8 +204,8 @@
<Unit filename="../Config.h" /> <Unit filename="../Config.h" />
<Unit filename="../Counters.cpp" /> <Unit filename="../Counters.cpp" />
<Unit filename="../Counters.h" /> <Unit filename="../Counters.h" />
<Unit filename="../DataBase_Loader.cpp" /> <Unit filename="../GameDatabase.cpp" />
<Unit filename="../DataBase_Loader.h" /> <Unit filename="../GameDatabase.h" />
<Unit filename="../DebugTools/Debug.h" /> <Unit filename="../DebugTools/Debug.h" />
<Unit filename="../DebugTools/DisASM.h" /> <Unit filename="../DebugTools/DisASM.h" />
<Unit filename="../DebugTools/DisR3000A.cpp" /> <Unit filename="../DebugTools/DisR3000A.cpp" />
@ -359,6 +359,8 @@
<Unit filename="../gui/AppEventListeners.h" /> <Unit filename="../gui/AppEventListeners.h" />
<Unit filename="../gui/AppEventSources.cpp" /> <Unit filename="../gui/AppEventSources.cpp" />
<Unit filename="../gui/AppForwardDefs.h" /> <Unit filename="../gui/AppForwardDefs.h" />
<Unit filename="../gui/AppGameDatabase.cpp" />
<Unit filename="../gui/AppGameDatabase.h" />
<Unit filename="../gui/AppInit.cpp" /> <Unit filename="../gui/AppInit.cpp" />
<Unit filename="../gui/AppMain.cpp" /> <Unit filename="../gui/AppMain.cpp" />
<Unit filename="../gui/AppRes.cpp" /> <Unit filename="../gui/AppRes.cpp" />

View File

@ -19,7 +19,7 @@
#include "IopCommon.h" #include "IopCommon.h"
#include "Patch.h" #include "Patch.h"
#include "DataBase_Loader.h" #include "GameDatabase.h"
#include <wx/textfile.h> #include <wx/textfile.h>
IniPatch Patch[ MAX_PATCH ]; IniPatch Patch[ MAX_PATCH ];
@ -135,10 +135,9 @@ int InitPatches(const wxString& name)
{ {
bool patchFound = false; bool patchFound = false;
wxString patch; wxString patch;
const wxString crc( L"[patches = " + name + L"]" );
patchnumber = 0; patchnumber = 0;
if (DataBase_Loader* GameDB = AppHost_GetGameDatabase() ) if (IGameDatabase* GameDB = AppHost_GetGameDatabase() )
{ {
if(GameDB->gameLoaded()) { if(GameDB->gameLoaded()) {
if (GameDB->sectionExists(L"patches", name)) { if (GameDB->sectionExists(L"patches", name)) {

View File

@ -30,7 +30,7 @@
#include "Elfheader.h" #include "Elfheader.h"
#include "CDVD/CDVD.h" #include "CDVD/CDVD.h"
#include "Patch.h" #include "Patch.h"
#include "DataBase_Loader.h" #include "GameDatabase.h"
#include "SamplProf.h" #include "SamplProf.h"
using namespace R5900; // for R5900 disasm tools using namespace R5900; // for R5900 disasm tools
@ -574,60 +574,22 @@ __forceinline void CPU_INT( u32 n, s32 ecycle)
cpuSetNextBranchDelta( cpuRegs.eCycle[n] ); cpuSetNextBranchDelta( cpuRegs.eCycle[n] );
} }
// Called from recompilers; __fastcall define is mandatory.
void __fastcall eeGameStarting() void __fastcall eeGameStarting()
{ {
if (!g_GameStarted && ElfCRC) { if (!g_GameStarted)
wxString gameCRC( wxsFormat( L"%8.8x", ElfCRC ) );
wxString gameName = L"Unknown Game (\?\?\?)";
wxString gameSerial = L" [" + DiscID + L"]";
wxString gameCompat = L" [Status = Unknown]";
wxString gamePatch = L"";
wxString gameFixes = L"";
wxString gameCheats = L"";
if (DataBase_Loader* GameDB = AppHost_GetGameDatabase() )
{ {
if (GameDB->gameLoaded()) { Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry );
int compat = GameDB->getInt("Compat");
gameName = GameDB->getString("Name");
gameName += L" (" + GameDB->getString("Region") + L")";
gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
}
if (EmuConfig.EnablePatches) {
int patches = InitPatches(gameCRC);
if (patches) {
wxString pString( wxsFormat( L"%d", patches ) );
gamePatch = L" [Patches = " + pString + L"]";
}
int fixes = loadGameSettings(GameDB);
if (fixes) {
wxString pString( wxsFormat( L"%d", fixes ) );
gameFixes = L" [Fixes = " + pString + L"]";
}
}
}
if (EmuConfig.EnableCheats) {
int cheats = InitCheats(gameCRC);
if (cheats) {
wxString cString( wxsFormat( L"%d", cheats ) );
gameCheats = L" [Cheats = " + cString + L"]";
}
}
Console.SetTitle(gameName+gameSerial+gameCompat+gameFixes+gamePatch+gameCheats);
GetMTGS().SendGameCRC(ElfCRC);
g_GameStarted = true; g_GameStarted = true;
GetCoreThread().GameStartingInThread();
if (0) ProfilerSetEnabled(true); }
else
{
Console.WriteLn( Color_Green, "(R5900) Re-executed ELF Entry point (ignored) [addr=0x%08X]", ElfEntry );
} }
if (EmuConfig.EnablePatches) ApplyPatch(0);
if (EmuConfig.EnableCheats) ApplyCheat(0);
} }
// Called from recompilers; __fastcall define is mandatory.
void __fastcall eeloadReplaceOSDSYS() void __fastcall eeloadReplaceOSDSYS()
{ {
g_SkipBiosHack = false; g_SkipBiosHack = false;

View File

@ -262,12 +262,12 @@ const u32 EENULL_START = 0x81FC0;
const u32 EELOAD_START = 0x82000; const u32 EELOAD_START = 0x82000;
const u32 EELOAD_SIZE = 0x20000; // overestimate for searching const u32 EELOAD_SIZE = 0x20000; // overestimate for searching
void __fastcall eeGameStarting(); extern void __fastcall eeGameStarting();
void __fastcall eeloadReplaceOSDSYS(); extern void __fastcall eeloadReplaceOSDSYS();
//////////////////////////////////////////////////////////////////// // --------------------------------------------------------------------------------------
// R5900 Public Interface / API // R5900cpu
// // --------------------------------------------------------------------------------------
// [TODO] : This is on the list to get converted to a proper C++ class. I'm putting it // [TODO] : This is on the list to get converted to a proper C++ class. I'm putting it
// off until I get my new IOPint and IOPrec re-merged. --air // off until I get my new IOPint and IOPrec re-merged. --air
// //

View File

@ -23,7 +23,7 @@
// Includes needed for cleanup, since we don't have a good system (yet) for // Includes needed for cleanup, since we don't have a good system (yet) for
// cleaning up these things. // cleaning up these things.
#include "sVU_zerorec.h" #include "sVU_zerorec.h"
#include "DataBase_Loader.h" #include "GameDatabase.h"
extern void closeNewVif(int idx); extern void closeNewVif(int idx);
extern void resetNewVif(int idx); extern void resetNewVif(int idx);

View File

@ -182,10 +182,6 @@ void SysCoreThread::DoCpuReset()
cpuReset(); cpuReset();
} }
void SysCoreThread::PostVsyncToUI()
{
}
// This is called from the PS2 VM at the start of every vsync (either 59.94 or 50 hz by PS2 // This is called from the PS2 VM at the start of every vsync (either 59.94 or 50 hz by PS2
// clock scale, which does not correlate to the actual host machine vsync). // clock scale, which does not correlate to the actual host machine vsync).
// //
@ -197,11 +193,18 @@ void SysCoreThread::PostVsyncToUI()
// //
void SysCoreThread::VsyncInThread() void SysCoreThread::VsyncInThread()
{ {
PostVsyncToUI();
if (EmuConfig.EnablePatches) ApplyPatch(); if (EmuConfig.EnablePatches) ApplyPatch();
if (EmuConfig.EnableCheats) ApplyCheat(); if (EmuConfig.EnableCheats) ApplyCheat();
} }
void SysCoreThread::GameStartingInThread()
{
GetMTGS().SendGameCRC(ElfCRC);
if (EmuConfig.EnablePatches) ApplyPatch(0);
if (EmuConfig.EnableCheats) ApplyCheat(0);
}
void SysCoreThread::StateCheckInThread() void SysCoreThread::StateCheckInThread()
{ {
GetMTGS().RethrowException(); GetMTGS().RethrowException();

View File

@ -186,7 +186,7 @@ public:
virtual void StateCheckInThread(); virtual void StateCheckInThread();
virtual void VsyncInThread(); virtual void VsyncInThread();
virtual void PostVsyncToUI()=0; virtual void GameStartingInThread();
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void UploadStateCopy( const VmStateBuffer& copy ); virtual void UploadStateCopy( const VmStateBuffer& copy );

View File

@ -26,7 +26,7 @@
#include "AppCommon.h" #include "AppCommon.h"
#include "AppCoreThread.h" #include "AppCoreThread.h"
#include "RecentIsoList.h" #include "RecentIsoList.h"
#include "DataBase_Loader.h" #include "AppGameDatabase.h"
#include "System.h" #include "System.h"
#include "System/SysThreads.h" #include "System/SysThreads.h"
@ -318,7 +318,7 @@ struct pxAppResources
ScopedPtr<wxImageList> ToolbarImages; ScopedPtr<wxImageList> ToolbarImages;
ScopedPtr<wxIconBundle> IconBundle; ScopedPtr<wxIconBundle> IconBundle;
ScopedPtr<wxBitmap> Bitmap_Logo; ScopedPtr<wxBitmap> Bitmap_Logo;
ScopedPtr<DataBase_Loader> GameDB; ScopedPtr<AppGameDatabase> GameDB;
pxAppResources(); pxAppResources();
virtual ~pxAppResources() throw() { } virtual ~pxAppResources() throw() { }
@ -568,6 +568,7 @@ public:
void SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override=wxEmptyString ); void SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override=wxEmptyString );
void SysShutdown(); void SysShutdown();
void LogicalVsync(); void LogicalVsync();
void GameStarting();
GSFrame& GetGsFrame() const; GSFrame& GetGsFrame() const;
MainEmuFrame& GetMainFrame() const; MainEmuFrame& GetMainFrame() const;
@ -618,7 +619,7 @@ public:
return m_Resources->ImageId; return m_Resources->ImageId;
} }
DataBase_Loader* GetGameDatabase(); AppGameDatabase* GetGameDatabase();
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Overrides of wxApp virtuals: // Overrides of wxApp virtuals:

View File

@ -144,9 +144,12 @@ void AppCoreThread::ChangeCdvdSource()
// TODO: Add a listener for CDVDsource changes? Or should we bother? // TODO: Add a listener for CDVDsource changes? Or should we bother?
} }
extern int loadGameSettings(IGameDatabase* gameDB=NULL);
void AppCoreThread::OnResumeReady() void AppCoreThread::OnResumeReady()
{ {
ApplySettings( g_Conf->EmuOptions ); ApplySettings( g_Conf->EmuOptions );
loadGameSettings();
CDVD_SourceType cdvdsrc( g_Conf->CdvdSource ); CDVD_SourceType cdvdsrc( g_Conf->CdvdSource );
if( cdvdsrc != CDVDsys_GetSourceType() || (cdvdsrc==CDVDsrc_Iso && (CDVDsys_GetFile(cdvdsrc) != g_Conf->CurrentIso)) ) if( cdvdsrc != CDVDsys_GetSourceType() || (cdvdsrc==CDVDsrc_Iso && (CDVDsys_GetFile(cdvdsrc) != g_Conf->CurrentIso)) )
@ -230,9 +233,16 @@ void AppCoreThread::OnCleanupInThread()
_parent::OnCleanupInThread(); _parent::OnCleanupInThread();
} }
void AppCoreThread::PostVsyncToUI() void AppCoreThread::VsyncInThread()
{ {
wxGetApp().LogicalVsync(); wxGetApp().LogicalVsync();
_parent::VsyncInThread();
}
void AppCoreThread::GameStartingInThread()
{
wxGetApp().GameStarting();
_parent::GameStartingInThread();
} }
void AppCoreThread::StateCheckInThread() void AppCoreThread::StateCheckInThread()

View File

@ -144,7 +144,8 @@ protected:
virtual void OnResumeInThread( bool IsSuspended ); virtual void OnResumeInThread( bool IsSuspended );
virtual void OnSuspendInThread(); virtual void OnSuspendInThread();
virtual void OnCleanupInThread(); virtual void OnCleanupInThread();
virtual void PostVsyncToUI(); virtual void VsyncInThread();
virtual void GameStartingInThread();
virtual void ExecuteTaskInThread(); virtual void ExecuteTaskInThread();
virtual void DoCpuReset(); virtual void DoCpuReset();
}; };

View File

@ -0,0 +1,186 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "AppGameDatabase.h"
class DBLoaderHelper
{
DeclareNoncopyableObject( DBLoaderHelper );
protected:
IGameDatabase& m_gamedb;
wxInputStream& m_reader;
// temp areas used as buffers for accelerated loading of database content. These strings are
// allocated and grown only once, and then reused for the duration of the database loading
// process; saving thousands of heapp allocation operations.
wxString m_dest;
std::string m_intermediate;
key_pair m_keyPair;
public:
DBLoaderHelper( wxInputStream& reader, IGameDatabase& db )
: m_gamedb(db)
, m_reader(reader)
{
}
wxString ReadHeader();
void ReadGames();
protected:
void doError(bool doMsg = false);
bool extractMultiLine();
void extract();
};
void DBLoaderHelper::doError(bool doMsg) {
if (doMsg) Console.Error("GameDatabase: Bad file data [%s]", m_dest.c_str());
m_keyPair.Clear();
}
// Multiline Sections are in the form of:
//
// [section=value]
// content
// content
// [/section]
//
// ... where the =value part is OPTIONAL.
bool DBLoaderHelper::extractMultiLine() {
if (m_dest[0] != L'[') return false; // All multiline sections begin with a '['!
if (!m_dest.EndsWith(L"]")) {
doError(true);
return false;
}
m_keyPair.key = m_dest;
// Use Mid() to strip off the left and right side brackets.
wxString midLine(m_dest.Mid(1, m_dest.Length()-2));
wxString lvalue(midLine.BeforeFirst(L'=').Trim(true).Trim(false));
//wxString rvalue(midLine.AfterFirst(L'=').Trim(true).Trim(false));
wxString endString;
endString.Printf( L"[/%s]", lvalue.c_str() );
while(!m_reader.Eof()) {
pxReadLine( m_reader, m_dest, m_intermediate );
if (m_dest.CmpNoCase(endString) == 0) break;
m_keyPair.value += m_dest + L"\n";
}
return true;
}
void DBLoaderHelper::extract() {
if( !pxParseAssignmentString( m_dest, m_keyPair.key, m_keyPair.value ) ) return;
if( m_keyPair.value.IsEmpty() ) doError(true);
}
wxString DBLoaderHelper::ReadHeader()
{
wxString header;
header.reserve(2048);
while(!m_reader.Eof()) {
pxReadLine(m_reader, m_dest, m_intermediate);
m_dest.Trim(false).Trim(true);
if( !(m_dest.IsEmpty() || m_dest.StartsWith(L"--") || m_dest.StartsWith( L"//" ) || m_dest.StartsWith( L";" )) ) break;
header += m_dest + L'\n';
}
if( !m_dest.IsEmpty() )
{
m_keyPair.Clear();
if( !extractMultiLine() ) extract();
}
return header;
}
void DBLoaderHelper::ReadGames()
{
Game_Data* game = m_gamedb.createNewGame();
if (m_keyPair.IsOk())
m_gamedb.writeString( m_keyPair.key, m_keyPair.value );
while(!m_reader.Eof()) { // Fill game data, find new game, repeat...
pxReadLine(m_reader, m_dest, m_intermediate);
m_dest.Trim(true).Trim(false);
if( m_dest.IsEmpty() ) continue;
m_keyPair.Clear();
if (!extractMultiLine()) extract();
if (!m_keyPair.IsOk()) continue;
m_gamedb.writeString( m_keyPair.key, m_keyPair.value );
}
}
// --------------------------------------------------------------------------------------
// AppGameDatabase (implementations)
// --------------------------------------------------------------------------------------
AppGameDatabase& AppGameDatabase::LoadFromFile(const wxString& file, const wxString& key )
{
if (!wxFileExists(file))
{
Console.Error(L"GameDatabase: DataBase Not Found! [%s]", file.c_str());
return *this;
}
wxFFileInputStream reader( file );
if (!reader.IsOk())
{
//throw Exception::FileNotFound( file );
Console.Error(L"GameDatabase: Could not access file (permission denied?) [%s]", file.c_str());
}
DBLoaderHelper loader( reader, *this );
header = loader.ReadHeader();
loader.ReadGames();
//if (setGame(value)) Console.WriteLn(L"GameDatabase: Found Game! [%s]", value.c_str());
//else Console.Warning(L"GameDatabase: Game Not Found! [%s]", value.c_str());
// Clear the current key after loading.
curGame = NULL;
return *this;
}
// Saves changes to the database
void AppGameDatabase::SaveToFile(const wxString& file) {
wxFFileOutputStream writer( file );
pxWriteMultiline(writer, header);
GameDataArray::iterator it( gList.begin() );
for ( ; it != gList.end(); ++it) {
KeyPairArray::iterator i = it[0].kList.begin();
for ( ; i != it[0].kList.end(); ++i) {
pxWriteMultiline(writer, i[0].toString() );
}
pxWriteLine(writer, L"---------------------------------------------");
}
}

View File

@ -0,0 +1,69 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "GameDatabase.h"
// --------------------------------------------------------------------------------------
// AppGameDatabase
// --------------------------------------------------------------------------------------
// This class extends BaseGameDatabase_Impl and provides interfaces for loading and saving
// the text-formatted game database.
//
// Example:
// ---------------------------------------------
// Serial = SLUS-20486
// Name = Marvel vs. Capcom 2
// Region = NTSC-U
// ---------------------------------------------
//
// [-- separators are a standard part of the formatting]
//
// To Load this game data, use "Serial" as the initial Key
// then specify "SLUS-20486" as the value in the constructor.
// After the constructor loads the game data, you can use the
// GameDatabase class's methods to get the other key's values.
// Such as dbLoader.getString("Region") returns "NTSC-U"
class AppGameDatabase : public BaseGameDatabaseVector
{
protected:
wxString header; // Header of the database
wxString baseKey; // Key to separate games by ("Serial")
public:
AppGameDatabase() {}
virtual ~AppGameDatabase() throw() {
// deque deletes its contents automatically.
Console.WriteLn( "(GameDB) Destroying..." );
}
AppGameDatabase& LoadFromFile(const wxString& file = L"GameIndex.dbf", const wxString& key = L"Serial" );
void SaveToFile(const wxString& file = L"GameIndex.dbf");
};
static wxString compatToStringWX(int compat) {
switch (compat) {
case 6: return L"Perfect";
case 5: return L"Playable";
case 4: return L"In-Game";
case 3: return L"Menu";
case 2: return L"Intro";
case 1: return L"Nothing";
default: return L"Unknown";
}
}

View File

@ -332,6 +332,127 @@ void Pcsx2App::LogicalVsync()
} }
} }
// Load Game Settings found in database
// (game fixes, round modes, clamp modes, etc...)
// Returns number of gamefixes set
int loadGameSettings(IGameDatabase* gameDB) {
if (!gameDB)
gameDB = wxGetApp().GetGameDatabase();
if(!gameDB->gameLoaded()) return 0;
Pcsx2Config sysLocal( EmuConfig );
int gf = 0;
bool rm = false;
if (gameDB->keyExists("eeRoundMode"))
{
SSE_RoundMode eeRM = (SSE_RoundMode)gameDB->getInt("eeRoundMode");
if (EnumIsValid(eeRM))
{
Console.WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
sysLocal.Cpu.sseMXCSR.SetRoundMode(eeRM);
rm = true;
++gf;
}
}
if (gameDB->keyExists("vuRoundMode"))
{
SSE_RoundMode vuRM = (SSE_RoundMode)gameDB->getInt("vuRoundMode");
if (EnumIsValid(vuRM))
{
Console.WriteLn("(GameDB) Changing VU0/VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
sysLocal.Cpu.sseVUMXCSR.SetRoundMode(vuRM);
rm = true;
++gf;
}
}
if (gameDB->keyExists("eeClampMode")) {
int clampMode = gameDB->getInt("eeClampMode");
Console.WriteLn("(GameDB) Changing EE/FPU clamp mode [mode=%d]", clampMode);
sysLocal.Recompiler.fpuOverflow = (clampMode >= 1);
sysLocal.Recompiler.fpuExtraOverflow = (clampMode >= 2);
sysLocal.Recompiler.fpuFullMode = (clampMode >= 3);
gf++;
}
if (gameDB->keyExists("vuClampMode")) {
int clampMode = gameDB->getInt("vuClampMode");
Console.WriteLn("(GameDB) Changing VU0/VU1 clamp mode [mode=%d]", clampMode);
sysLocal.Recompiler.vuOverflow = (clampMode >= 1);
sysLocal.Recompiler.vuExtraOverflow = (clampMode >= 2);
sysLocal.Recompiler.vuSignOverflow = (clampMode >= 3);
gf++;
}
for( GamefixId id=GamefixId_FIRST; id<pxEnumEnd; ++id )
{
wxString key( EnumToString(id) );
key += L"Hack";
if (gameDB->keyExists(key))
{
bool enableIt = gameDB->getBool(key);
sysLocal.Gamefixes.Set(id, enableIt );
Console.WriteLn(L"(GameDB) %s Gamefix: " + key, enableIt ? L"Enabled" : L"Disabled" );
gf++;
}
}
GetCoreThread().ApplySettings( sysLocal );
return gf;
}
#include "cdvd/CDVD.h"
#include "Elfheader.h"
#include "Patch.h"
void Pcsx2App::GameStarting()
{
if( PostAppMethodMyself( &Pcsx2App::GameStarting ) ) return;
wxString gameCRC;
wxString gameName (L"Unknown Game (\?\?\?)");
wxString gameSerial (L" [" + DiscID + L"]");
wxString gameCompat (L" [Status = Unknown]");
wxString gamePatch;
wxString gameFixes;
wxString gameCheats;
if( ElfCRC ) gameCRC.Printf( L"%8.8x", ElfCRC );
if (IGameDatabase* GameDB = AppHost_GetGameDatabase() )
{
if (GameDB->gameLoaded()) {
int compat = GameDB->getInt("Compat");
gameName = GameDB->getString("Name");
gameName += L" (" + GameDB->getString("Region") + L")";
gameCompat = L" [Status = "+compatToStringWX(compat)+L"]";
}
if (EmuConfig.EnablePatches) {
if (int patches = InitPatches(gameCRC)) {
gamePatch.Printf(L" [%d Patches]", patches);
}
if (int fixes = loadGameSettings(GameDB)) {
gameFixes.Printf(L" [%d Fixes]", fixes);
}
}
}
if (EmuConfig.EnableCheats) {
if (int cheats = InitCheats(gameCRC)) {
gameCheats.Printf(L" [Cheats = %d]", cheats);
}
}
Console.SetTitle(gameName+gameSerial+gameCompat+gameFixes+gamePatch+gameCheats);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Pcsx2App Event Handlers // Pcsx2App Event Handlers
@ -987,16 +1108,20 @@ SysCoreAllocations& GetSysCoreAlloc()
return *wxGetApp().m_CoreAllocs; return *wxGetApp().m_CoreAllocs;
} }
DataBase_Loader* Pcsx2App::GetGameDatabase() AppGameDatabase* Pcsx2App::GetGameDatabase()
{ {
pxAppResources& res( GetResourceCache() ); pxAppResources& res( GetResourceCache() );
ScopedLock lock( m_mtx_LoadingGameDB ); ScopedLock lock( m_mtx_LoadingGameDB );
if( !res.GameDB ) res.GameDB = new DataBase_Loader(); if( !res.GameDB )
{
res.GameDB = new AppGameDatabase();
res.GameDB->LoadFromFile();
}
return res.GameDB; return res.GameDB;
} }
DataBase_Loader* AppHost_GetGameDatabase() IGameDatabase* AppHost_GetGameDatabase()
{ {
return wxGetApp().GetGameDatabase(); return wxGetApp().GetGameDatabase();
} }

View File

@ -14,7 +14,7 @@
*/ */
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "DataBase_Loader.h" #include "App.h"
#include "ConfigurationPanels.h" #include "ConfigurationPanels.h"
extern wxString DiscID; extern wxString DiscID;
@ -42,11 +42,11 @@ wxTextCtrl* CreateMultiLineTextCtrl( wxWindow* parent, int digits, long flags =
Panels::GameDatabasePanel::GameDatabasePanel( wxWindow* parent ) Panels::GameDatabasePanel::GameDatabasePanel( wxWindow* parent )
: BaseApplicableConfigPanel( parent ) : BaseApplicableConfigPanel( parent )
{ {
//if (!GameDB) GameDB = new DataBase_Loader(); //if (!GameDB) GameDB = new GameDatabase();
DataBase_Loader* GameDB = AppHost_GetGameDatabase(); IGameDatabase* GameDB = AppHost_GetGameDatabase();
pxAssume( GameDB != NULL ); pxAssume( GameDB != NULL );
searchBtn = new wxButton (this, wxID_DEFAULT, L"Search"); searchBtn = new wxButton (this, wxID_ANY, _("Search"));
serialBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT); serialBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
nameBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT); nameBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
@ -56,7 +56,7 @@ Panels::GameDatabasePanel::GameDatabasePanel( wxWindow* parent )
patchesBox = CreateMultiLineTextCtrl(this, 40, wxTE_LEFT); patchesBox = CreateMultiLineTextCtrl(this, 40, wxTE_LEFT);
for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i)
gameFixes[i] = new pxCheckBox(this, EnumToString(i)); gameFixes[i] = new pxCheckBox(this, EnumToString(i), wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER );
*this += Heading(_("Game Database Editor")).Bold() | StdExpand(); *this += Heading(_("Game Database Editor")).Bold() | StdExpand();
*this += Heading(_("This panel lets you add and edit game titles, game fixes, and game patches.")) | StdExpand(); *this += Heading(_("This panel lets you add and edit game titles, game fixes, and game patches.")) | StdExpand();
@ -96,7 +96,7 @@ Panels::GameDatabasePanel::GameDatabasePanel( wxWindow* parent )
} }
void Panels::GameDatabasePanel::PopulateFields() { void Panels::GameDatabasePanel::PopulateFields() {
DataBase_Loader* GameDB = AppHost_GetGameDatabase(); IGameDatabase* GameDB = AppHost_GetGameDatabase();
if (GameDB->gameLoaded()) { if (GameDB->gameLoaded()) {
serialBox ->SetLabel(GameDB->getString("Serial")); serialBox ->SetLabel(GameDB->getString("Serial"));
@ -107,7 +107,13 @@ void Panels::GameDatabasePanel::PopulateFields() {
patchesBox->SetLabel(GameDB->getString("[patches]")); patchesBox->SetLabel(GameDB->getString("[patches]"));
for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i)
gameFixes[i]->SetValue(GameDB->getBool(EnumToString(i)+wxString(L"Hack"))); {
wxString keyName (EnumToString(i)); keyName += L"Hack";
if( GameDB->keyExists(keyName) )
gameFixes[i]->SetValue(GameDB->getBool(keyName));
else
gameFixes[i]->SetIndeterminate();
}
} }
else { else {
serialBox ->SetLabel(L""); serialBox ->SetLabel(L"");
@ -129,31 +135,33 @@ void Panels::GameDatabasePanel::PopulateFields() {
// returns True if the database is modified, or FALSE if no changes to save. // returns True if the database is modified, or FALSE if no changes to save.
bool Panels::GameDatabasePanel::WriteFieldsToDB() { bool Panels::GameDatabasePanel::WriteFieldsToDB() {
DataBase_Loader* GameDB = AppHost_GetGameDatabase(); IGameDatabase* GameDB = AppHost_GetGameDatabase();
wxString wxStr( serialBox->GetValue() );
if (wxStr.IsEmpty()) return false; if (serialBox->GetValue().IsEmpty()) return false;
if (wxStr != GameDB->getString("Serial")) {
GameDB->addGame(wxStr);
}
writeTextBoxToDB("Name", nameBox->GetValue()); // Only write the serial if its been changed
writeTextBoxToDB("Region", regionBox->GetValue()); if( !GameDB->setGame(serialBox->GetValue()) )
writeTextBoxToDB("Compat", compatBox->GetValue()); GameDB->writeString(L"Serial", serialBox->GetValue());
writeTextBoxToDB("[comments]", commentBox->GetValue());
writeTextBoxToDB("[patches]", patchesBox->GetValue()); GameDB->writeString(L"Name", nameBox->GetValue());
GameDB->writeString(L"Region", regionBox->GetValue());
GameDB->writeString(L"Compat", compatBox->GetValue());
GameDB->writeString(L"[comments]", commentBox->GetValue());
GameDB->writeString(L"[patches]", patchesBox->GetValue());
for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) { for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) {
const bool val = gameFixes[i]->GetValue(); wxString keyName (EnumToString(i)); keyName += L"Hack";
wxString keyName( EnumToString(i) ); keyName += L"Hack";
if (!val) GameDB->deleteKey(keyName); if (gameFixes[i]->IsIndeterminate())
else GameDB->writeBool(keyName, val); GameDB->deleteKey(keyName);
else
GameDB->writeBool(keyName, gameFixes[i]->GetValue());
} }
return true; return true;
} }
void Panels::GameDatabasePanel::Search_Click(wxCommandEvent& evt) { void Panels::GameDatabasePanel::Search_Click(wxCommandEvent& evt) {
DataBase_Loader* GameDB = AppHost_GetGameDatabase(); IGameDatabase* GameDB = AppHost_GetGameDatabase();
wxString wxStr = serialBox->GetValue(); wxString wxStr = serialBox->GetValue();
if( wxStr.IsEmpty() ) wxStr = DiscID; if( wxStr.IsEmpty() ) wxStr = DiscID;
@ -166,49 +174,14 @@ void Panels::GameDatabasePanel::Search_Click(wxCommandEvent& evt) {
} }
void Panels::GameDatabasePanel::Apply() { void Panels::GameDatabasePanel::Apply() {
DataBase_Loader* GameDB = AppHost_GetGameDatabase(); AppGameDatabase* GameDB = wxGetApp().GetGameDatabase();
if( WriteFieldsToDB() ) if( WriteFieldsToDB() )
{ {
Console.WriteLn("Saving changes to Game Database..."); Console.WriteLn("Saving changes to Game Database...");
GameDB->saveToFile(); GameDB->SaveToFile();
} }
} }
void Panels::GameDatabasePanel::AppStatusEvent_OnSettingsApplied() void Panels::GameDatabasePanel::AppStatusEvent_OnSettingsApplied()
{ {
} }
/*
//#define lineIndent(_wxSizer, txt) {_wxSizer+=5;_wxSizer+=5;_wxSizer+=Heading(txt);_wxSizer+=5;_wxSizer+=5;}
//searchBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
//searchType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
//searchList = new wxListBox (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT | wxLB_NEEDED_SB);
//searchList->SetFont (wxFont(searchList->GetFont().GetPointSize()+1, wxFONTFAMILY_MODERN, wxNORMAL, wxNORMAL, false, L"Lucida Console"));
//searchList->SetMinSize(wxSize(wxDefaultCoord, std::max(searchList->GetMinSize().GetHeight(), 96)));
searchType->Append(L"Game Serial", (void*)0);
searchType->Append(L"Game Name" , (void*)0);
searchType->SetSelection(0);
sizer1 += searchType;
sizer1 += 5;
sizer1 += searchBox | pxCenter;
sizer1 += 5;
sizer1 += searchBtn;
sizer1 += 5;
sizer1 += 5;
sizer1 += searchList | StdExpand();// pxCenter;
sizer1 += 5;
sizer1 += 5;
lineIndent(sizer1, L"");
sizer1 += 5;
sizer1 += 5;
sizer1 += new wxStaticLine(this) | StdExpand();
sizer1 += 5;
sizer1 += 5;
lineIndent(sizer1, L"");
lineIndent(sizer1, L"Game Info");
*/

View File

@ -260,6 +260,14 @@
RelativePath="..\..\Utilities\folderdesc.txt" RelativePath="..\..\Utilities\folderdesc.txt"
> >
</File> </File>
<File
RelativePath="..\..\GameDatabase.cpp"
>
</File>
<File
RelativePath="..\..\GameDatabase.h"
>
</File>
<File <File
RelativePath="..\..\PrecompiledHeader.cpp" RelativePath="..\..\PrecompiledHeader.cpp"
> >
@ -312,22 +320,6 @@
> >
</File> </File>
</Filter> </Filter>
<Filter
Name="Game DataBase"
>
<File
RelativePath="..\..\DataBase_Loader.cpp"
>
</File>
<File
RelativePath="..\..\DataBase_Loader.h"
>
</File>
<File
RelativePath="..\..\File_Reader.h"
>
</File>
</Filter>
<Filter <Filter
Name="Patch" Name="Patch"
> >
@ -467,10 +459,6 @@
RelativePath="..\..\System\PageFaultSource.h" RelativePath="..\..\System\PageFaultSource.h"
> >
</File> </File>
<File
RelativePath="..\..\Paths.h"
>
</File>
<File <File
RelativePath="..\..\Plugins.h" RelativePath="..\..\Plugins.h"
> >
@ -1896,6 +1884,10 @@
RelativePath="..\..\gui\AppEventSources.cpp" RelativePath="..\..\gui\AppEventSources.cpp"
> >
</File> </File>
<File
RelativePath="..\..\gui\AppGameDatabase.cpp"
>
</File>
<File <File
RelativePath="..\..\gui\AppInit.cpp" RelativePath="..\..\gui\AppInit.cpp"
> >
@ -2657,6 +2649,10 @@
RelativePath="..\..\gui\AppForwardDefs.h" RelativePath="..\..\gui\AppForwardDefs.h"
> >
</File> </File>
<File
RelativePath="..\..\gui\AppGameDatabase.h"
>
</File>
<File <File
RelativePath="..\..\gui\ApplyState.h" RelativePath="..\..\gui\ApplyState.h"
> >

View File

@ -20,7 +20,6 @@
SSE_MXCSR g_sseMXCSR = { DEFAULT_sseMXCSR }; SSE_MXCSR g_sseMXCSR = { DEFAULT_sseMXCSR };
SSE_MXCSR g_sseVUMXCSR = { DEFAULT_sseVUMXCSR }; SSE_MXCSR g_sseVUMXCSR = { DEFAULT_sseVUMXCSR };
//////////////////////////////////////////////////////////////////////////////////////////
// SetCPUState -- for assignment of SSE roundmodes and clampmodes. // SetCPUState -- for assignment of SSE roundmodes and clampmodes.
// //
void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVUMXCSR) void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVUMXCSR)