//////////////////////////////////////////////////////////////////////////////////////// // // 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 "../NstMachine.hpp" #include "../NstStream.hpp" #include "../NstChecksum.hpp" #include "../NstCartridge.hpp" #include "../NstImageDatabase.hpp" #include "../NstCartridgeInes.hpp" #include "NstApiMachine.hpp" namespace Nes { namespace Api { Cartridge::ChooseProfileCaller Cartridge::chooseProfileCallback; #ifdef NST_MSVC_OPTIMIZE #pragma optimize("s", on) #endif Cartridge::Profile::Hash::Hash() throw() { Clear(); } Cartridge::Profile::Hash::Hash(const char* sha1,const char* crc) throw() { Assign( sha1, crc ); } Cartridge::Profile::Hash::Hash(const wchar_t* sha1,const wchar_t* crc) throw() { Assign( sha1, crc ); } Cartridge::Profile::Hash::Hash(const dword* sha1,dword crc) throw() { Assign( sha1, crc ); } void Cartridge::Profile::Hash::Clear() throw() { Assign( NULL, dword(0) ); } template bool Cartridge::Profile::Hash::Set(dword& dst,const T* NST_RESTRICT src) { dword v = 0; uint i = 32; do { i -= 4; const T c = *src++; if (c >= '0' && c <= '9') { v |= dword(c - '0') << i; } else if (c >= 'A' && c <= 'F') { v |= dword(c - 'A' + 0xA) << i; } else if (c >= 'a' && c <= 'f') { v |= dword(c - 'a' + 0xA) << i; } else { return false; } } while (i); dst = v; return true; } template void Cartridge::Profile::Hash::Import(const T* sha1,const T* crc) { Clear(); if (crc && *crc) Set( data[0], crc ); if (sha1 && *sha1) { for (uint i=1; i < 1+SHA1_WORD_LENGTH; ++i, sha1 += 8) { if (!Set( data[i], sha1 )) { for (uint j=1; j < i; ++j) data[j] = 0; break; } } } } void Cartridge::Profile::Hash::Assign(const char* sha1,const char* crc) throw() { Import( sha1, crc ); } void Cartridge::Profile::Hash::Assign(const wchar_t* sha1,const wchar_t* crc) throw() { Import( sha1, crc ); } void Cartridge::Profile::Hash::Assign(const dword* sha1,dword crc) throw() { data[0] = crc & 0xFFFFFFFF; for (uint i=0; i < SHA1_WORD_LENGTH; ++i) data[1+i] = (sha1 ? sha1[i] & 0xFFFFFFFF : 0); } void Cartridge::Profile::Hash::Get(char* sha1,char* crc) const throw() { if (crc) { for (dword v=data[0], j=32; j; ) { j -= 4; uint c = v >> j & 0xF; *crc++ = (c < 0xA ? '0' + c : 'A' + (c - 0xA) ); } } if (sha1) { for (uint i=1; i < 1+SHA1_WORD_LENGTH; ++i) { for (dword v=data[i], j=32; j; ) { j -= 4; uint c = v >> j & 0xF; *sha1++ = (c < 0xA ? '0' + c : 'A' + (c - 0xA) ); } } } } bool Cartridge::Profile::Hash::operator < (const Hash& c) const throw() { for (const dword *a=data, *b=c.data, *end=data+(1+SHA1_WORD_LENGTH); a != end; ++a, ++b) { if (*a < *b) return true; if (*a > *b) return false; } return false; } bool Cartridge::Profile::Hash::operator == (const Hash& c) const throw() { for (const dword *a=data, *b=c.data, *end=data+(1+SHA1_WORD_LENGTH); a != end; ++a, ++b) { if (*a != *b) return false; } return true; } bool Cartridge::Profile::Hash::operator ! () const throw() { for (uint i=0; i < 1+SHA1_WORD_LENGTH; ++i) { if (data[i]) return false; } return true; } void Cartridge::Profile::Hash::Compute(const void* mem,ulong size) throw() { const Core::Checksum checksum( static_cast(mem), size ); Assign( checksum.GetSha1(), checksum.GetCrc() ); } const dword* Cartridge::Profile::Hash::GetSha1() const throw() { return data+1; } dword Cartridge::Profile::Hash::GetCrc32() const throw() { return data[0]; } Cartridge::Profile::System::System() throw() : type(NES_NTSC), cpu(CPU_RP2A03), ppu(PPU_RP2C02) {} Cartridge::Profile::Dump::Dump() throw() : state(UNKNOWN) {} Cartridge::Profile::Game::Game() throw() : adapter(Input::ADAPTER_NES), players(0) { controllers[0] = Input::PAD1; controllers[1] = Input::PAD2; controllers[2] = Input::UNCONNECTED; controllers[3] = Input::UNCONNECTED; controllers[4] = Input::UNCONNECTED; } Cartridge::Profile::Board::Pin::Pin() throw() : number(0) {} Cartridge::Profile::Board::Sample::Sample() throw() : id(0) {} Cartridge::Profile::Board::Rom::Rom() throw() : id(0), size(0) {} Cartridge::Profile::Board::Ram::Ram() throw() : id(0), size(0), battery(false) {} Cartridge::Profile::Board::Chip::Chip() throw() : battery(false) {} Cartridge::Profile::Board::Board() throw() : solderPads(0), mapper(NO_MAPPER), subMapper(0) {} Cartridge::Profile::Board::~Board() throw() { } Cartridge::Profile::Profile() throw() : multiRegion(false), patched(false) { } Cartridge::Profile::~Profile() throw() { } template dword Cartridge::Profile::Board::GetComponentSize(const T& components) const { dword size = 0; for (typename T::const_iterator it(components.begin()), end(components.end()); it != end; ++it) size += it->size; return size; } dword Cartridge::Profile::Board::GetPrg() const throw() { return GetComponentSize( prg ); } dword Cartridge::Profile::Board::GetChr() const throw() { return GetComponentSize( chr ); } dword Cartridge::Profile::Board::GetWram() const throw() { return GetComponentSize( wram ); } dword Cartridge::Profile::Board::GetVram() const throw() { return GetComponentSize( vram ); } template bool Cartridge::Profile::Board::HasComponentBattery(const T& components) const { for (typename T::const_iterator it(components.begin()), end(components.end()); it != end; ++it) { if (it->battery) return true; } return false; } bool Cartridge::Profile::Board::HasWramBattery() const throw() { return HasComponentBattery( wram ); } bool Cartridge::Profile::Board::HasMmcBattery() const throw() { return HasComponentBattery( chips ); } bool Cartridge::Profile::Board::HasBattery() const throw() { return HasWramBattery() || HasMmcBattery(); } Cartridge::NesHeader::NesHeader() throw() { Clear(); } void Cartridge::NesHeader::Clear() throw() { system = SYSTEM_CONSOLE; region = REGION_NTSC; prgRom = 0; prgRam = 0; prgNvRam = 0; chrRom = 0; chrRam = 0; chrNvRam = 0; ppu = PPU_RP2C02; mirroring = MIRRORING_VERTICAL; mapper = 0; subMapper = 0; security = 0; version = 0; trainer = false; inputType = 0; } Result Cartridge::NesHeader::Import(const void* const data,const ulong length) throw() { return Core::Cartridge::Ines::ReadHeader( *this, static_cast(data), length ); } Result Cartridge::NesHeader::Export(void* data,ulong length) const throw() { return Core::Cartridge::Ines::WriteHeader( *this, static_cast(data), length ); } bool Cartridge::Database::IsLoaded() const throw() { return emulator.imageDatabase; } bool Cartridge::Database::IsEnabled() const throw() { return emulator.imageDatabase && emulator.imageDatabase->Enabled(); } bool Cartridge::Database::Create() { if (emulator.imageDatabase == NULL) emulator.imageDatabase = new (std::nothrow) Core::ImageDatabase; return emulator.imageDatabase; } Result Cartridge::Database::Load(std::istream& stream) throw() { return Create() ? emulator.imageDatabase->Load( stream ) : RESULT_ERR_OUT_OF_MEMORY; } Result Cartridge::Database::Load(std::istream& baseStream,std::istream& overloadStream) throw() { return Create() ? emulator.imageDatabase->Load( baseStream, overloadStream ) : RESULT_ERR_OUT_OF_MEMORY; } void Cartridge::Database::Unload() throw() { if (emulator.imageDatabase) emulator.imageDatabase->Unload(); } Result Cartridge::Database::Enable(bool state) throw() { if (Create()) { if (emulator.imageDatabase->Enabled() != state) { emulator.imageDatabase->Enable( state ); return RESULT_OK; } return RESULT_NOP; } return RESULT_ERR_OUT_OF_MEMORY; } Result Cartridge::ReadRomset(std::istream& stream,Machine::FavoredSystem system,bool askProfile,Profile& profile) throw() { try { Core::Cartridge::ReadRomset( stream, static_cast(system), askProfile, profile ); } catch (Result result) { return result; } catch (...) { return RESULT_ERR_GENERIC; } return RESULT_OK; } Result Cartridge::ReadInes(std::istream& stream,Machine::FavoredSystem system,Profile& profile) throw() { try { Core::Cartridge::ReadInes( stream, static_cast(system), profile ); } catch (Result result) { return result; } catch (...) { return RESULT_ERR_GENERIC; } return RESULT_OK; } Result Cartridge::ReadUnif(std::istream& stream,Machine::FavoredSystem system,Profile& profile) throw() { try { Core::Cartridge::ReadUnif( stream, static_cast(system), profile ); } catch (Result result) { return result; } catch (...) { return RESULT_ERR_GENERIC; } return RESULT_OK; } const Cartridge::Profile* Cartridge::GetProfile() const throw() { if (emulator.Is(Machine::CARTRIDGE)) return &static_cast(emulator.image)->GetProfile(); return NULL; } Cartridge::Database::Entry Cartridge::Database::FindEntry(const Profile::Hash& hash,Machine::FavoredSystem system) const throw() { return emulator.imageDatabase ? emulator.imageDatabase->Search( hash, static_cast(system) ).Reference() : NULL; } Cartridge::Database::Entry Cartridge::Database::FindEntry(const void* file,ulong length,Machine::FavoredSystem system) const throw() { if (emulator.imageDatabase) { Profile::Hash hash; hash.Compute( file, length ); return emulator.imageDatabase->Search( hash, static_cast(system) ).Reference(); } else { return NULL; } } Result Cartridge::Database::Entry::GetProfile(Profile& profile) const throw() { Core::ImageDatabase::Entry entry(ref); if (!entry) return RESULT_ERR_NOT_READY; try { entry.Fill(profile); } catch (const std::bad_alloc&) { return RESULT_ERR_OUT_OF_MEMORY; } catch (...) { return RESULT_ERR_GENERIC; } return RESULT_OK; } const wchar_t* Cartridge::Database::Entry::GetTitle() const throw() { return Core::ImageDatabase::Entry(ref).GetTitle(); } const Cartridge::Profile::Hash* Cartridge::Database::Entry::GetHash() const throw() { return Core::ImageDatabase::Entry(ref).GetHash(); } const wchar_t* Cartridge::Database::Entry::GetRegion() const throw() { return Core::ImageDatabase::Entry(ref).GetRegion(); } Cartridge::Profile::System::Type Cartridge::Database::Entry::GetSystem() const throw() { return Core::ImageDatabase::Entry(ref).GetSystem(); } bool Cartridge::Database::Entry::IsMultiRegion() const throw() { return Core::ImageDatabase::Entry(ref).IsMultiRegion(); } dword Cartridge::Database::Entry::GetPrgRom() const throw() { return Core::ImageDatabase::Entry(ref).GetPrg(); } dword Cartridge::Database::Entry::GetChrRom() const throw() { return Core::ImageDatabase::Entry(ref).GetChr(); } uint Cartridge::Database::Entry::GetWram() const throw() { return Core::ImageDatabase::Entry(ref).GetWram(); } uint Cartridge::Database::Entry::GetVram() const throw() { return Core::ImageDatabase::Entry(ref).GetVram(); } bool Cartridge::Database::Entry::HasBattery() const throw() { return Core::ImageDatabase::Entry(ref).HasBattery(); } uint Cartridge::Database::Entry::GetMapper() const throw() { return Core::ImageDatabase::Entry(ref).GetMapper(); } Cartridge::Profile::Dump::State Cartridge::Database::Entry::GetDumpState() const throw() { return Core::ImageDatabase::Entry(ref).GetDumpState(); } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif } }