//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia 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. // // Nestopia 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 Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include "NstLog.hpp" #include "NstImageDatabase.hpp" #include "NstXml.hpp" namespace Nes { namespace Core { class ImageDatabase::Item { public: ~Item(); enum { PERIPHERAL_UNSPECIFIED, PERIPHERAL_STANDARD, PERIPHERAL_ZAPPER, PERIPHERAL_POWERPAD, PERIPHERAL_FAMILYTRAINER, PERIPHERAL_ARKANOID, PERIPHERAL_FOURPLAYER, PERIPHERAL_3DGLASSES, PERIPHERAL_MIRACLEPIANO, PERIPHERAL_SUBORKEYBOARD, PERIPHERAL_SUBORMOUSE, PERIPHERAL_FAMILYKEYBOARD, PERIPHERAL_PARTYTAP, PERIPHERAL_CRAZYCLIMBER, PERIPHERAL_EXCITINGBOXING, PERIPHERAL_KONAMIHYPERSHOT, PERIPHERAL_BANDAIHYPERSHOT, PERIPHERAL_POKKUNMOGURAA, PERIPHERAL_OEKAKIDSTABLET, PERIPHERAL_MAHJONG, PERIPHERAL_ROB, PERIPHERAL_POWERGLOVE, PERIPHERAL_HORITRACK, PERIPHERAL_PACHINKO, PERIPHERAL_TOPRIDERBIKE, PERIPHERAL_DOREMIKKO, PERIPHERAL_TURBOFILE, PERIPHERAL_BARCODEWORLD, MAX_PERIPHERALS = 4 }; private: class String { union { mutable wcstring string; dword id; }; public: explicit String(dword i=0) : id(i) {} bool operator == (const String& s) const { return id == s.id; } bool operator < (const String& s) const { return id < s.id; } void operator << (wcstring s) const { string = s + id; } operator wcstring () const { return string; } }; public: struct Ic { struct Pin { uint number; String function; Pin() {} Pin(uint n,dword s) : number(n), function(s) {} }; typedef std::vector Pins; String package; Pins pins; Ic() {} Ic(dword p,const Pins& i) : package(p), pins(i) {} }; struct Rom : Ic { dword id; String name; dword size; Hash hash; Rom() {} Rom(dword i,dword n,dword s,dword e,const Pins& p,const Hash& c) : Ic(e,p), id(i), name(n), size(s), hash(c) {} }; typedef std::vector Roms; struct Ram : Ic { dword id; dword size; bool battery; Ram() {} Ram(dword i,dword s,bool b,dword e,const Pins& p) : Ic(e,p), id(i), size(s), battery(b) {} }; typedef std::vector Rams; struct Chip : Ic { String type; bool battery; Chip() {} Chip(dword id,bool b,dword e,const Pins& p) : Ic(e,p), type(id), battery(b) {} bool operator == (const Chip& chip) const { return type == chip.type; } bool operator < (const Chip& chip) const { return type < chip.type; } }; typedef std::vector Chips; struct Dump { const String by; const String date; const Profile::Dump::State state; Dump(dword b,dword d,Profile::Dump::State o) : by(b), date(d), state(o) {} }; struct Property { String name; String value; Property() {} Property(dword n,dword v) : name(n), value(v) {} }; typedef std::vector Properties; private: const Hash hash; Item* sibling; const Dump dump; const String title; const String altTitle; const String clss; const String subClss; const String catalog; const String publisher; const String developer; const String portDeveloper; const String region; const String revision; const String pcb; const String board; const String cic; const Roms prg; const Roms chr; const Rams wram; const Rams vram; Chips chips; const Properties properties; byte peripherals[MAX_PERIPHERALS]; const word mapper; const byte solderPads; const byte system; const byte cpu; const byte ppu; const byte players; bool multiRegion; public: Item ( const Hash& hashIn, dword dumpBy, dword dumpDate, Profile::Dump::State dumpState, dword titleIn, dword altTitleIn, dword clssIn, dword subClssIn, dword catalogIn, dword publisherIn, dword developerIn, dword portDeveloperIn, dword regionIn, const Properties& propertiesIn, dword playersIn, const byte (&e)[MAX_PERIPHERALS], Profile::System::Type systemIn, Profile::System::Cpu cpuIn, Profile::System::Ppu ppuIn, dword revisionIn, dword boardIn, dword pcbIn, uint mapperIn, const Roms& prgIn, const Roms& chrIn, const Rams& wramIn, const Rams& vramIn, const Chips& chipsIn, dword cicIn, uint solderPadsIn ) : hash ( hashIn ), sibling ( NULL ), dump ( dumpBy, dumpDate, dumpState ), title ( titleIn ), altTitle ( altTitleIn ), clss ( clssIn ), subClss ( subClssIn ), catalog ( catalogIn ), publisher ( publisherIn ), developer ( developerIn ), portDeveloper ( portDeveloperIn ), region ( regionIn ), revision ( revisionIn ), pcb ( pcbIn ), board ( boardIn ), cic ( cicIn ), prg ( prgIn ), chr ( chrIn ), wram ( wramIn ), vram ( vramIn ), chips ( chipsIn ), properties ( propertiesIn ), mapper ( mapperIn ), solderPads ( solderPadsIn ), system ( systemIn ), cpu ( cpuIn ), ppu ( ppuIn ), players ( playersIn ), multiRegion ( false ) { for (uint i=0; i < MAX_PERIPHERALS; ++i) peripherals[i] = e[i]; std::sort( chips.begin(), chips.end() ); } private: template static dword GetMemSize(const T& t) { dword size = 0; for (typename T::const_iterator it(t.begin()), end(t.end()); it != end; ++it) size += it->size; return size; } template static bool HasBattery(const T& t) { for (typename T::const_iterator it(t.begin()), end(t.end()); it != end; ++it) { if (it->battery) return true; } return false; } public: wcstring GetTitle() const { return title; } wcstring GetPublisher() const { return publisher; } wcstring GetDeveloper() const { return developer; } wcstring GetRegion() const { return region; } wcstring GetRevision() const { return revision; } wcstring GetPcb() const { return pcb; } wcstring GetBoard() const { return board; } wcstring GetCic() const { return cic; } uint NumPlayers() const { return players; } uint GetMapper() const { return mapper; } uint GetSolderPads() const { return solderPads; } Profile::System::Type GetSystem() const { return static_cast(system); } Profile::Dump::State GetDumpState() const { return dump.state; } const Hash& GetHash() const { return hash; } dword GetPrgSize() const { return GetMemSize( prg ); } dword GetChrSize() const { return GetMemSize( chr ); } dword GetWramSize() const { return GetMemSize( wram ); } dword GetVramSize() const { return GetMemSize( vram ); } bool HasVRamBattery() const { return HasBattery( vram ); } bool HasWRamBattery() const { return HasBattery( wram ); } bool HasChipBattery() const { return HasBattery( chips ); } bool HasBattery() const { return HasWRamBattery() || HasVRamBattery() || HasChipBattery(); } const Item* GetNextSibling() const { return sibling; } bool IsMultiRegion() const { return multiRegion; } void Fill(Profile& profile,const bool full) const { if (full) { if (*dump.by) profile.dump.by = dump.by; if (*dump.date) profile.dump.date = dump.date; if (dump.state != Profile::Dump::UNKNOWN) profile.dump.state = dump.state; if (*title) profile.game.title = title; if (*altTitle) profile.game.altTitle = altTitle; if (*clss) profile.game.clss = clss; if (*subClss) profile.game.subClss = subClss; if (*catalog) profile.game.catalog = catalog; if (*publisher) profile.game.publisher = publisher; if (*developer) profile.game.developer = developer; if (*portDeveloper) profile.game.portDeveloper = portDeveloper; if (*region) profile.game.region = region; if (*revision) profile.game.revision = revision; if (players) profile.game.players = players; if (*cic) profile.board.cic = cic; if (*pcb) profile.board.pcb = pcb; if (const dword size = properties.size()) { profile.properties.resize( size ); Profile::Properties::iterator b(profile.properties.begin()); for (Properties::const_iterator a(properties.begin()), end(properties.end()); a != end; ++a, ++b) { b->name = a->name; b->value = a->value; } } } for (uint i=0; i < MAX_PERIPHERALS; ++i) { if (peripherals[i] != PERIPHERAL_UNSPECIFIED) { switch (peripherals[i]) { case PERIPHERAL_STANDARD: profile.game.controllers[0] = Api::Input::PAD1; profile.game.controllers[1] = Api::Input::PAD2; break; case PERIPHERAL_FOURPLAYER: if (system == Profile::System::FAMICOM) profile.game.adapter = Api::Input::ADAPTER_FAMICOM; else profile.game.adapter = Api::Input::ADAPTER_NES; profile.game.controllers[2] = Api::Input::PAD3; profile.game.controllers[3] = Api::Input::PAD4; break; case PERIPHERAL_ZAPPER: if (system == Profile::System::VS_UNISYSTEM || system == Profile::System::VS_DUALSYSTEM) { profile.game.controllers[0] = Api::Input::ZAPPER; profile.game.controllers[1] = Api::Input::UNCONNECTED; } else { profile.game.controllers[1] = Api::Input::ZAPPER; } break; case PERIPHERAL_POWERPAD: case PERIPHERAL_FAMILYTRAINER: if (system == Profile::System::FAMICOM || peripherals[i] == PERIPHERAL_FAMILYTRAINER) { profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::FAMILYTRAINER; } else { profile.game.controllers[1] = Api::Input::POWERPAD; } break; case PERIPHERAL_ARKANOID: if (system == Profile::System::FAMICOM) profile.game.controllers[4] = Api::Input::PADDLE; else profile.game.controllers[1] = Api::Input::PADDLE; break; case PERIPHERAL_SUBORKEYBOARD: profile.game.controllers[4] = Api::Input::SUBORKEYBOARD; break; case PERIPHERAL_SUBORMOUSE: profile.game.controllers[1] = Api::Input::MOUSE; break; case PERIPHERAL_FAMILYKEYBOARD: profile.game.controllers[4] = Api::Input::FAMILYKEYBOARD; break; case PERIPHERAL_PARTYTAP: profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::PARTYTAP; break; case PERIPHERAL_CRAZYCLIMBER: profile.game.controllers[4] = Api::Input::CRAZYCLIMBER; break; case PERIPHERAL_EXCITINGBOXING: profile.game.controllers[4] = Api::Input::EXCITINGBOXING; break; case PERIPHERAL_BANDAIHYPERSHOT: profile.game.controllers[4] = Api::Input::BANDAIHYPERSHOT; break; case PERIPHERAL_KONAMIHYPERSHOT: profile.game.controllers[0] = Api::Input::UNCONNECTED; profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::KONAMIHYPERSHOT; break; case PERIPHERAL_POKKUNMOGURAA: profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::POKKUNMOGURAA; break; case PERIPHERAL_OEKAKIDSTABLET: profile.game.controllers[0] = Api::Input::UNCONNECTED; profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::OEKAKIDSTABLET; break; case PERIPHERAL_MAHJONG: profile.game.controllers[0] = Api::Input::UNCONNECTED; profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::MAHJONG; break; case PERIPHERAL_TOPRIDERBIKE: profile.game.controllers[0] = Api::Input::UNCONNECTED; profile.game.controllers[1] = Api::Input::UNCONNECTED; profile.game.controllers[4] = Api::Input::TOPRIDER; break; case PERIPHERAL_HORITRACK: profile.game.controllers[4] = Api::Input::HORITRACK; break; case PERIPHERAL_PACHINKO: profile.game.controllers[4] = Api::Input::PACHINKO; break; case PERIPHERAL_ROB: profile.game.controllers[1] = Api::Input::ROB; break; case PERIPHERAL_DOREMIKKO: profile.game.controllers[4] = Api::Input::DOREMIKKOKEYBOARD; break; case PERIPHERAL_POWERGLOVE: profile.game.controllers[0] = Api::Input::POWERGLOVE; break; case PERIPHERAL_TURBOFILE: profile.game.controllers[4] = Api::Input::TURBOFILE; break; case PERIPHERAL_BARCODEWORLD: profile.game.controllers[4] = Api::Input::BARCODEWORLD; break; } } } profile.multiRegion = multiRegion; profile.system.type = static_cast(system); profile.system.cpu = static_cast(cpu); profile.system.ppu = static_cast(ppu); if (*board) profile.board.type = board; if (mapper != Profile::Board::NO_MAPPER) profile.board.mapper = mapper; profile.board.solderPads = solderPads; for (uint j=0; j < 2; ++j) { if (full || (j ? profile.board.GetChr() == GetChrSize() : profile.board.GetPrg() == GetPrgSize())) { const Roms& src = (j ? chr : prg); Profile::Board::Roms& dst = (j ? profile.board.chr : profile.board.prg); dst.resize( src.size() ); Profile::Board::Roms::iterator b(dst.begin()); for (Roms::const_iterator a(src.begin()), end(src.end()); a != end; ++a, ++b) { b->size = a->size; if (full) { b->name = a->name; b->package = a->package; b->hash = a->hash; } b->pins.resize( a->pins.size() ); Profile::Board::Pins::iterator d(b->pins.begin()); for (Rom::Pins::const_iterator c(a->pins.begin()), end(a->pins.end()); c != end; ++c, ++d) { d->number = c->number; d->function = c->function; } } } } for (uint j=0; j < 2; ++j) { if (full || (j ? profile.board.GetVram() == GetVramSize() : profile.board.GetWram() == GetWramSize())) { const Rams& src = (j ? vram : wram); Profile::Board::Rams& dst = (j ? profile.board.vram : profile.board.wram); dst.resize( src.size() ); Profile::Board::Rams::iterator b(dst.begin()); for (Rams::const_iterator a(src.begin()), end(src.end()); a != end; ++a, ++b) { b->id = a->id; b->size = a->size; b->battery = a->battery; if (full) b->package = a->package; b->pins.resize( a->pins.size() ); Profile::Board::Pins::iterator d(b->pins.begin()); for (Ram::Pins::const_iterator c(a->pins.begin()), end(a->pins.end()); c != end; ++c, ++d) { d->number = c->number; d->function = c->function; } } } } profile.board.chips.resize( chips.size() ); Profile::Board::Chips::iterator b(profile.board.chips.begin()); for (Chips::const_iterator a(chips.begin()), end(chips.end()); a != end; ++a, ++b) { b->type = a->type; b->package = a->package; b->battery = a->battery; b->pins.resize( a->pins.size() ); Profile::Board::Pins::iterator d(b->pins.begin()); for (Chip::Pins::const_iterator c(a->pins.begin()), end(a->pins.end()); c != end; ++c, ++d) { d->number = c->number; d->function = c->function; } } } private: bool operator == (const Item& item) const { return ( system == item.system && mapper == item.mapper && board == item.board && solderPads == item.solderPads && chips.size() == item.chips.size() && cpu == item.cpu && ppu == item.ppu && GetVramSize() == item.GetVramSize() && GetWramSize() == item.GetWramSize() && HasVRamBattery() == item.HasVRamBattery() && HasWRamBattery() == item.HasWRamBattery() && HasChipBattery() == item.HasChipBattery() && std::equal( chips.begin(), chips.end(), item.chips.begin() ) ); } bool Add(Item* const item) { item->multiRegion = this->multiRegion || ( ( this->system == Profile::System::NES_PAL || this->system == Profile::System::NES_PAL_A || this->system == Profile::System::NES_PAL_B || this->system == Profile::System::DENDY ) != ( item->system == Profile::System::NES_PAL || item->system == Profile::System::NES_PAL_A || item->system == Profile::System::NES_PAL_B || item->system == Profile::System::DENDY ) ); Item* it = this; for (;;) { if (*it == *item) return false; it->multiRegion = item->multiRegion; if (!it->sibling) break; it = it->sibling; } it->sibling = item; return true; } void Finalize(wcstring const lut) const { dump.by << lut; dump.date << lut; title << lut; altTitle << lut; clss << lut; subClss << lut; catalog << lut; developer << lut; portDeveloper << lut; publisher << lut; region << lut; revision << lut; pcb << lut; board << lut; cic << lut; for (uint i=0; i < 2; ++i) { for (Roms::const_iterator j((i ? chr : prg).begin()), end((i ? chr : prg).end()); j != end; ++j) { j->name << lut; j->package << lut; for (Ic::Pins::const_iterator k(j->pins.begin()), end(j->pins.end()); k != end; ++k) k->function << lut; } } for (uint i=0; i < 2; ++i) { for (Rams::const_iterator j((i ? wram : vram).begin()), end((i ? wram : vram).end()); j != end; ++j) j->package << lut; } for (Chips::const_iterator i(chips.begin()), end(chips.end()); i != end; ++i) { i->type << lut; i->package << lut; for (Ic::Pins::const_iterator j(i->pins.begin()), end(i->pins.end()); j != end; ++j) j->function << lut; } for (Properties::const_iterator i(properties.begin()), end(properties.end()); i != end; ++i) { i->name << lut; i->value << lut; } if (sibling) sibling->Finalize( lut ); } public: struct Less { bool operator () (const Item* a,const Item* b) const { return a->hash < b->hash; } bool operator () (const Item* a,const Hash& b) const { return a->hash < b; } bool operator () (const Hash& a,const Item* b) const { return a < b->hash; } }; class Builder { public: ~Builder(); dword operator << (wcstring); void operator << (Item*); private: struct Less { bool operator () (wcstring a,wcstring b) const { return std::wcscmp( a, b ) < 0; } bool operator () (const Item* a,const Item* b) const { return a->hash < b->hash; } }; typedef std::map StringMap; typedef std::set ItemMap; dword stringLength; StringMap stringMap; ItemMap itemMap; public: Builder() : stringLength(0) { (*this) << L""; } void Construct(Strings& strings,const Item**& itemsBegin,const Item**& itemsEnd) { NST_ASSERT( !strings.Size() && itemsBegin == NULL && itemsEnd == NULL ); if (const dword size = itemMap.size()) { strings.Resize( stringLength ); wchar_t* const NST_RESTRICT sdst = strings.Begin(); for (StringMap::const_iterator src(stringMap.begin()), end(stringMap.end()); src != end; ++src) std::wcscpy( sdst + src->second, src->first ); const Item** dst = new const Item* [size]; itemsBegin = dst; itemsEnd = dst + size; for (ItemMap::const_iterator src(itemMap.begin()), end(itemMap.end()); src != end; ++src, ++dst) { (*src)->Finalize( sdst ); *dst = *src; } itemMap.clear(); } } }; }; ImageDatabase::ImageDatabase() : enabled(true) { items.begin = NULL; items.end = NULL; items.hashing = HASHING_DETECT; } ImageDatabase::~ImageDatabase() { Unload(); } ImageDatabase::Entry ImageDatabase::Search(const Hash& hash,const FavoredSystem favoredSystem) const { if (items.begin) { const Hash searchHash ( ( items.hashing & HASHING_SHA1 ) ? hash.GetSha1() : NULL, ( items.hashing & HASHING_CRC ) ? hash.GetCrc32() : 0UL ); const Item** item = std::lower_bound( items.begin, items.end, searchHash, Item::Less() ); if (item != items.end && (*item)->GetHash() == searchHash) { for (const Item* it = *item; it; it = it->GetNextSibling()) { switch (it->GetSystem()) { case Profile::System::NES_NTSC: if (favoredSystem == FAVORED_NES_NTSC) return it; break; case Profile::System::NES_PAL: case Profile::System::NES_PAL_A: case Profile::System::NES_PAL_B: if (favoredSystem == FAVORED_NES_PAL) return it; break; case Profile::System::FAMICOM: if (favoredSystem == FAVORED_FAMICOM) return it; break; case Profile::System::DENDY: if (favoredSystem == FAVORED_DENDY) return it; break; case Profile::System::VS_UNISYSTEM: case Profile::System::VS_DUALSYSTEM: case Profile::System::PLAYCHOICE_10: default: break; } } return *item; } } return NULL; } wcstring ImageDatabase::Entry::GetTitle() const { return item ? item->GetTitle() : L""; } wcstring ImageDatabase::Entry::GetPublisher() const { return item ? item->GetPublisher() : L""; } wcstring ImageDatabase::Entry::GetDeveloper() const { return item ? item->GetDeveloper() : L""; } wcstring ImageDatabase::Entry::GetRegion() const { return item ? item->GetRegion() : L""; } wcstring ImageDatabase::Entry::GetRevision() const { return item ? item->GetRevision() : L""; } wcstring ImageDatabase::Entry::GetPcb() const { return item ? item->GetPcb() : L""; } wcstring ImageDatabase::Entry::GetBoard() const { return item ? item->GetBoard() : L""; } wcstring ImageDatabase::Entry::GetCic() const { return item ? item->GetCic() : L""; } uint ImageDatabase::Entry::NumPlayers() const { return item ? item->NumPlayers() : 0; } uint ImageDatabase::Entry::GetMapper() const { return item ? item->GetMapper() : Profile::Board::NO_MAPPER; } uint ImageDatabase::Entry::GetSolderPads() const { return item ? item->GetSolderPads() : 0; } ImageDatabase::Profile::System::Type ImageDatabase::Entry::GetSystem() const { return item ? item->GetSystem() : Profile::System::NES_NTSC; } bool ImageDatabase::Entry::IsMultiRegion() const { return item && item->IsMultiRegion(); } ImageDatabase::Profile::Dump::State ImageDatabase::Entry::GetDumpState() const { return item ? item->GetDumpState() : Profile::Dump::UNKNOWN; } const ImageDatabase::Hash* ImageDatabase::Entry::GetHash() const { return item ? &item->GetHash() : NULL; } dword ImageDatabase::Entry::GetPrg() const { return item ? item->GetPrgSize() : 0; } dword ImageDatabase::Entry::GetChr() const { return item ? item->GetChrSize() : 0; } dword ImageDatabase::Entry::GetWram() const { return item ? item->GetWramSize() : 0; } dword ImageDatabase::Entry::GetVram() const { return item ? item->GetVramSize() : 0; } bool ImageDatabase::Entry::HasBattery() const { return item && item->HasBattery(); } void ImageDatabase::Entry::Fill(Profile& profile,bool full) const { if (item) item->Fill( profile, full ); } Result ImageDatabase::Load(std::istream& baseStream,std::istream* overrideStream) { Unload(); try { Xml baseXml, overrideXml; Item::Builder builder; for (uint multi=0; multi < (overrideStream ? 2 : 1); ++multi) { Xml& xml = (multi ? overrideXml : baseXml); try { if (!xml.Read( multi ? *overrideStream : baseStream )) return RESULT_ERR_CORRUPT_FILE; } catch (...) { throw RESULT_ERR_CORRUPT_FILE; } if (!xml.GetRoot().IsType( L"database" )) return RESULT_ERR_INVALID_FILE; if (const Xml::Attribute attribute=xml.GetRoot().GetAttribute( L"version" )) { wcstring const version = attribute.GetValue(); if ( (version[0] < L'1' || version[0] > L'9') || (version[1] != L'.') || (version[2] < L'0' || version[2] > L'9') || (version[3] != L'\0') ) throw RESULT_ERR_INVALID_FILE; } const bool strict = !xml.GetRoot().GetAttribute( L"conformance" ).IsValue( L"loose" ); for (Xml::Node game(xml.GetRoot().GetFirstChild()); game.IsType( L"game" ); game=game.GetNextSibling()) { byte peripherals[4] = { Item::PERIPHERAL_UNSPECIFIED, Item::PERIPHERAL_UNSPECIFIED, Item::PERIPHERAL_UNSPECIFIED, Item::PERIPHERAL_UNSPECIFIED }; if (Xml::Node device=game.GetChild( L"peripherals" )) { uint i = 0; for (device=device.GetFirstChild(); i < 4 && device.IsType( L"device" ); device=device.GetNextSibling()) { if (const Xml::Attribute attribute = device.GetAttribute( L"type" )) { if (attribute.IsValue( L"3dglasses" )) peripherals[i++] = Item::PERIPHERAL_3DGLASSES; else if (attribute.IsValue( L"arkanoid" )) peripherals[i++] = Item::PERIPHERAL_ARKANOID; else if (attribute.IsValue( L"bandaihypershot" )) peripherals[i++] = Item::PERIPHERAL_BANDAIHYPERSHOT; else if (attribute.IsValue( L"barcodeworld" )) peripherals[i++] = Item::PERIPHERAL_BARCODEWORLD; else if (attribute.IsValue( L"crazyclimber" )) peripherals[i++] = Item::PERIPHERAL_CRAZYCLIMBER; else if (attribute.IsValue( L"doremikko" )) peripherals[i++] = Item::PERIPHERAL_DOREMIKKO; else if (attribute.IsValue( L"excitingboxing" )) peripherals[i++] = Item::PERIPHERAL_EXCITINGBOXING; else if (attribute.IsValue( L"familykeyboard" )) peripherals[i++] = Item::PERIPHERAL_FAMILYKEYBOARD; else if (attribute.IsValue( L"familyfunfitness" )) peripherals[i++] = Item::PERIPHERAL_POWERPAD; else if (attribute.IsValue( L"familytrainer" )) peripherals[i++] = Item::PERIPHERAL_FAMILYTRAINER; else if (attribute.IsValue( L"fourplayer" )) peripherals[i++] = Item::PERIPHERAL_FOURPLAYER; else if (attribute.IsValue( L"horitrack" )) peripherals[i++] = Item::PERIPHERAL_HORITRACK; else if (attribute.IsValue( L"konamihypershot" )) peripherals[i++] = Item::PERIPHERAL_KONAMIHYPERSHOT; else if (attribute.IsValue( L"mahjong" )) peripherals[i++] = Item::PERIPHERAL_MAHJONG; else if (attribute.IsValue( L"miraclepiano" )) peripherals[i++] = Item::PERIPHERAL_MIRACLEPIANO; else if (attribute.IsValue( L"oekakidstablet" )) peripherals[i++] = Item::PERIPHERAL_OEKAKIDSTABLET; else if (attribute.IsValue( L"pachinko" )) peripherals[i++] = Item::PERIPHERAL_PACHINKO; else if (attribute.IsValue( L"partytap" )) peripherals[i++] = Item::PERIPHERAL_PARTYTAP; else if (attribute.IsValue( L"pokkunmoguraa" )) peripherals[i++] = Item::PERIPHERAL_POKKUNMOGURAA; else if (attribute.IsValue( L"powerglove" )) peripherals[i++] = Item::PERIPHERAL_POWERGLOVE; else if (attribute.IsValue( L"powerpad" )) peripherals[i++] = Item::PERIPHERAL_POWERPAD; else if (attribute.IsValue( L"rob" )) peripherals[i++] = Item::PERIPHERAL_ROB; else if (attribute.IsValue( L"suborkeyboard" )) peripherals[i++] = Item::PERIPHERAL_SUBORKEYBOARD; else if (attribute.IsValue( L"subormouse" )) peripherals[i++] = Item::PERIPHERAL_SUBORMOUSE; else if (attribute.IsValue( L"topriderbike" )) peripherals[i++] = Item::PERIPHERAL_TOPRIDERBIKE; else if (attribute.IsValue( L"turbofile" )) peripherals[i++] = Item::PERIPHERAL_TURBOFILE; else if (attribute.IsValue( L"zapper" )) peripherals[i++] = Item::PERIPHERAL_ZAPPER; } } } for (Xml::Node image(game.GetFirstChild()); image; image=image.GetNextSibling()) { Profile::System::Type system = Profile::System::NES_NTSC; Profile::System::Cpu cpu = Profile::System::CPU_RP2A03; Profile::System::Ppu ppu = Profile::System::PPU_RP2C02; if (image.IsType( L"cartridge" )) { if (const Xml::Attribute attribute=image.GetAttribute( L"system" )) { if (attribute.IsValue( L"famicom" )) { system = Profile::System::FAMICOM; } else if (attribute.IsValue( L"nes-ntsc" )) { system = Profile::System::NES_NTSC; } else if (attribute.IsValue( L"nes-pal" )) { system = Profile::System::NES_PAL; cpu = Profile::System::CPU_RP2A07; ppu = Profile::System::PPU_RP2C07; } else if (attribute.IsValue( L"nes-pal-a" )) { system = Profile::System::NES_PAL_A; cpu = Profile::System::CPU_RP2A07; ppu = Profile::System::PPU_RP2C07; } else if (attribute.IsValue( L"nes-pal-b" )) { system = Profile::System::NES_PAL_B; cpu = Profile::System::CPU_RP2A07; ppu = Profile::System::PPU_RP2C07; } else if (attribute.IsValue( L"dendy" )) { system = Profile::System::DENDY; cpu = Profile::System::CPU_DENDY; ppu = Profile::System::PPU_DENDY; } else if (strict) { continue; } } else if (strict) { continue; } } else if (image.IsType( L"arcade" )) { ppu = Profile::System::PPU_RP2C03B; if (const Xml::Attribute attribute=image.GetAttribute( L"system" )) { if (attribute.IsValue( L"vs-unisystem" )) { system = Profile::System::VS_UNISYSTEM; } else if (attribute.IsValue( L"vs-dualsystem" )) { system = Profile::System::VS_DUALSYSTEM; } else if (attribute.IsValue( L"playchoice-10" )) { system = Profile::System::PLAYCHOICE_10; } else { continue; } } else { continue; } } else { continue; } if (system == Profile::System::VS_UNISYSTEM || system == Profile::System::VS_DUALSYSTEM) { if (const Xml::Attribute attribute=image.GetAttribute( L"ppu" )) { if (attribute.IsValue( L"rp2c03b" )) ppu = Profile::System::PPU_RP2C03B; else if (attribute.IsValue( L"rp2c03g" )) ppu = Profile::System::PPU_RP2C03G; else if (attribute.IsValue( L"rp2c04-0001" )) ppu = Profile::System::PPU_RP2C04_0001; else if (attribute.IsValue( L"rp2c04-0002" )) ppu = Profile::System::PPU_RP2C04_0002; else if (attribute.IsValue( L"rp2c04-0003" )) ppu = Profile::System::PPU_RP2C04_0003; else if (attribute.IsValue( L"rp2c04-0004" )) ppu = Profile::System::PPU_RP2C04_0004; else if (attribute.IsValue( L"rc2c03b" )) ppu = Profile::System::PPU_RC2C03B; else if (attribute.IsValue( L"rc2c03c" )) ppu = Profile::System::PPU_RC2C03C; else if (attribute.IsValue( L"rc2c05-01" )) ppu = Profile::System::PPU_RC2C05_01; else if (attribute.IsValue( L"rc2c05-02" )) ppu = Profile::System::PPU_RC2C05_02; else if (attribute.IsValue( L"rc2c05-03" )) ppu = Profile::System::PPU_RC2C05_03; else if (attribute.IsValue( L"rc2c05-04" )) ppu = Profile::System::PPU_RC2C05_04; else if (attribute.IsValue( L"rc2c05-05" )) ppu = Profile::System::PPU_RC2C05_05; } } Profile::Dump::State dump = Profile::Dump::OK; if (const Xml::Attribute attribute=image.GetAttribute( L"dump" )) { if (attribute.IsValue( L"bad" )) { if (strict) continue; dump = Profile::Dump::BAD; } else if (attribute.IsValue( L"unknown" )) { if (strict) continue; dump = Profile::Dump::UNKNOWN; } } if (items.hashing == HASHING_DETECT) { if (*image.GetAttribute( L"sha1" ).GetValue()) items.hashing |= HASHING_SHA1; if (*image.GetAttribute( L"crc" ).GetValue()) items.hashing |= HASHING_CRC; } const Hash hash ( ( items.hashing & HASHING_SHA1 ) ? image.GetAttribute( L"sha1" ).GetValue() : L"", ( items.hashing & HASHING_CRC ) ? image.GetAttribute( L"crc" ).GetValue() : L"" ); if (!hash) continue; if (const Xml::Node board=image.GetChild( L"board" )) { uint players = 0; if (const Xml::Attribute attribute=game.GetAttribute( L"players" )) { ulong value = attribute.GetUnsignedValue(); if (value >= MIN_PLAYERS && value <= MAX_PLAYERS) players = value; } uint mapper = Profile::Board::NO_MAPPER; if (const Xml::Attribute attribute=board.GetAttribute( L"mapper" )) { ulong value = attribute.GetUnsignedValue(); if (value <= MAX_MAPPER) mapper = value; } uint solderPads = 0; if (const Xml::Node pad=board.GetChild( L"pad" )) { solderPads = ( (pad.GetAttribute( L"h" ).IsValue( L"1" ) ? Profile::Board::SOLDERPAD_H : 0U) | (pad.GetAttribute( L"v" ).IsValue( L"1" ) ? Profile::Board::SOLDERPAD_V : 0U) ); } Item::Properties properties; if (Xml::Node node=image.GetChild( L"properties" )) { for (node=node.GetFirstChild(); node.IsType( L"property" ); node=node.GetNextSibling()) { properties.push_back ( Item::Property ( builder << node.GetAttribute(L"name").GetValue(), builder << node.GetAttribute(L"value").GetValue() ) ); } } Item::Roms prg, chr; Item::Rams wram, vram; Item::Chips chips; for (Xml::Node node=board.GetFirstChild(); node; node=node.GetNextSibling()) { dword size = 0; if (const Xml::Attribute attribute=node.GetAttribute( L"size" )) { wcstring end; const ulong value = attribute.GetUnsignedValue( end, 10 ); if (end[0] == L'\0') { size = value; } else if ((end[0] == L'k' || end[0] == L'K') && end[1] == L'\0' && value <= MAX_CHIP_SIZE/SIZE_1K) { size = value * SIZE_1K; } } Item::Ic::Pins pins; for (Xml::Node child(node.GetFirstChild()); child; child=child.GetNextSibling()) { if (child.IsType(L"pin")) { const ulong number = child.GetAttribute(L"number").GetUnsignedValue(); wcstring const function = child.GetAttribute(L"function").GetValue(); if (number >= MIN_IC_PINS && number <= MAX_IC_PINS && *function) pins.push_back( Item::Ic::Pin(number,builder << function) ); } } bool first; if (true == (first=node.IsType( L"prg" )) || node.IsType( L"chr" )) { if (size >= MIN_CHIP_SIZE && size <= MAX_CHIP_SIZE) { (first ? prg : chr).push_back ( Item::Rom ( node.GetAttribute( L"id" ).GetUnsignedValue(), builder << node.GetAttribute( L"name" ).GetValue(), size, builder << node.GetAttribute( L"package" ).GetValue(), pins, Hash(node.GetAttribute( L"sha1" ).GetValue(),node.GetAttribute( L"crc" ).GetValue()) ) ); } } else if (true == (first=node.IsType( L"wram" )) || node.IsType( L"vram" )) { if (size >= MIN_CHIP_SIZE && size <= MAX_CHIP_SIZE) { (first ? wram : vram).push_back ( Item::Ram ( node.GetAttribute( L"id" ).GetUnsignedValue(), size, node.GetAttribute( L"battery" ).IsValue( L"1" ), builder << node.GetAttribute( L"package" ).GetValue(), pins ) ); } } else if (node.IsType( L"chip" )) { chips.push_back ( Item::Chip ( builder << node.GetAttribute( L"type" ).GetValue(), node.GetAttribute( L"battery" ).IsValue( L"1" ), builder << node.GetAttribute( L"package" ).GetValue(), pins ) ); } } builder << new Item ( hash, builder << image.GetAttribute( L"dumper" ).GetValue(), builder << image.GetAttribute( L"datedumped" ).GetValue(), dump, builder << game.GetAttribute( L"name" ).GetValue(), builder << game.GetAttribute( L"altname" ).GetValue(), builder << game.GetAttribute( L"class" ).GetValue(), builder << game.GetAttribute( L"subclass" ).GetValue(), builder << game.GetAttribute( L"catalog" ).GetValue(), builder << game.GetAttribute( L"publisher" ).GetValue(), builder << game.GetAttribute( L"developer" ).GetValue(), builder << game.GetAttribute( L"portdeveloper" ).GetValue(), builder << game.GetAttribute( L"region" ).GetValue(), properties, players, peripherals, system, cpu, ppu, builder << image.GetAttribute( L"revision" ).GetValue(), builder << board.GetAttribute( L"type" ).GetValue(), builder << board.GetAttribute( L"pcb" ).GetValue(), mapper, prg, chr, wram, vram, chips, builder << board.GetChild( L"cic" ).GetAttribute( L"type" ).GetValue(), solderPads ); } } } } builder.Construct( strings, items.begin, items.end ); } catch (Result result) { Unload( true ); return result; } catch (const std::bad_alloc&) { Unload( true ); return RESULT_ERR_OUT_OF_MEMORY; } catch (...) { Unload( true ); return RESULT_ERR_GENERIC; } Log() << "Database: " << (items.end - items.begin) << " items imported from " << (overrideStream ? "internal & external" : "internal") << " DB" NST_LINEBREAK; return RESULT_OK; } void ImageDatabase::Unload(const bool error) { if (const Item** it=items.begin) { do { delete *it; } while (++it != items.end); delete [] items.begin; items.begin = NULL; items.end = NULL; } items.hashing = HASHING_DETECT; strings.Destroy(); if (error) Log::Flush( "Database: error, aborting.." NST_LINEBREAK ); } ImageDatabase::Item::Builder::~Builder() { for (ItemMap::const_iterator it(itemMap.begin()), end(itemMap.end()); it != end; ++it) delete *it; } dword ImageDatabase::Item::Builder::operator << (wcstring string) { const std::pair entry ( stringMap.insert( std::pair(string,stringLength) ) ); if (entry.second) stringLength += std::wcslen(string) + 1; return entry.first->second; } void ImageDatabase::Item::Builder::operator << (Item* item) { std::pair entry; try { entry = itemMap.insert( item ); } catch (...) { delete item; throw; } if (!entry.second && !(*entry.first)->Add(item)) delete item; } ImageDatabase::Item::~Item() { if (const Item* item=sibling) { sibling = NULL; delete item; } } } }