nestopia/source/core/api/NstApiCartridge.cpp
Rupert Carmichael f1e959117e core: Apply latest upstream changes
This adds support for the NES 2.0 Default Expansion Device field, and
better support for MMC3 and VRC2/4 hacks
2024-11-20 20:01:00 -05:00

581 lines
14 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// 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 <new>
#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<typename T>
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<typename T>
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<const byte*>(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<typename T>
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<typename T>
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<const byte*>(data), length );
}
Result Cartridge::NesHeader::Export(void* data,ulong length) const throw()
{
return Core::Cartridge::Ines::WriteHeader( *this, static_cast<byte*>(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<Core::FavoredSystem>(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<Core::FavoredSystem>(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<Core::FavoredSystem>(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<const Core::Cartridge*>(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<Core::FavoredSystem>(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<Core::FavoredSystem>(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
}
}