mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
This adds support for the NES 2.0 Default Expansion Device field, and better support for MMC3 and VRC2/4 hacks
581 lines
14 KiB
C++
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
|
|
}
|
|
}
|