pcsx2/pcsx2/gui/AppGameDatabase.cpp

242 lines
6.6 KiB
C++

/* 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"
#include <wx/stdpaths.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]", WX_STR(m_dest));
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 = NULL;
if (m_keyPair.IsOk())
{
game = m_gamedb.createNewGame(m_keyPair.value);
game->writeString(m_keyPair.key, m_keyPair.value);
//if( m_keyPair.CompareKey(m_gamedb.getBaseKey()) )
// game.id = m_keyPair.value;
}
while(!m_reader.Eof()) { // Fill game data, find new game, repeat...
pthread_testcancel();
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;
if (m_keyPair.CompareKey(m_gamedb.getBaseKey()))
game = m_gamedb.createNewGame(m_keyPair.value);
game->writeString( m_keyPair.key, m_keyPair.value );
}
}
// --------------------------------------------------------------------------------------
// AppGameDatabase (implementations)
// --------------------------------------------------------------------------------------
AppGameDatabase& AppGameDatabase::LoadFromFile(const wxString& _file, const wxString& key )
{
wxString file(_file);
if( wxFileName(file).IsRelative() )
{
// InstallFolder is the preferred base directory for the DB file, but the registry can point to previous
// installs if uninstall wasn't done properly.
// Since the games DB file is considered part of pcsx2.exe itself, look for it at the exe folder
// regardless of any other settings.
// Note 1: Portable setup didn't suffer from this as install folder pointed already to the exe folder in portable.
// Note 2: Other folders are either configurable (plugins, memcards, etc) or create their content automatically (inis)
// So the games DB was really the only one that suffers from residues of prior installs.
//wxDirName dir = InstallFolder;
wxDirName dir = (wxDirName)wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath();
file = ( dir + file ).GetFullPath();
}
if (!wxFileExists(file))
{
Console.Error(L"(GameDB) Database Not Found! [%s]", WX_STR(file));
return *this;
}
wxFFileInputStream reader( file );
if (!reader.IsOk())
{
//throw Exception::FileNotFound( file );
Console.Error(L"(GameDB) Could not access file (permission denied?) [%s]", WX_STR(file));
}
DBLoaderHelper loader( reader, *this );
u64 qpc_Start = GetCPUTicks();
header = loader.ReadHeader();
loader.ReadGames();
u64 qpc_end = GetCPUTicks();
Console.WriteLn( "(GameDB) %d games on record (loaded in %ums)",
gHash.size(), (u32)(((qpc_end-qpc_Start)*1000) / GetTickFrequency()) );
return *this;
}
// Saves changes to the database
void AppGameDatabase::SaveToFile(const wxString& file) {
wxFFileOutputStream writer( file );
pxWriteMultiline(writer, header);
for(uint blockidx=0; blockidx<=m_BlockTableWritePos; ++blockidx)
{
if( !m_BlockTable[blockidx] ) continue;
const uint endidx = (blockidx == m_BlockTableWritePos) ? m_CurBlockWritePos : m_GamesPerBlock;
for( uint gameidx=0; gameidx<=endidx; ++gameidx )
{
const Game_Data& game( m_BlockTable[blockidx][gameidx] );
for (auto i = game.kList.begin(); i != game.kList.end(); ++i) {
pxWriteMultiline(writer, i->toString() );
}
pxWriteLine(writer, L"---------------------------------------------");
}
}
}
AppGameDatabase* Pcsx2App::GetGameDatabase()
{
pxAppResources& res( GetResourceCache() );
ScopedLock lock( m_mtx_LoadingGameDB );
if( !res.GameDB )
{
res.GameDB = new AppGameDatabase();
res.GameDB->LoadFromFile();
}
return res.GameDB;
}
IGameDatabase* AppHost_GetGameDatabase()
{
return wxGetApp().GetGameDatabase();
}