Files
Nestopia-Core/core/NstCartridgeRomset.cpp

1000 lines
29 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 <cstring>
#include <new>
#include <vector>
#include "NstLog.hpp"
#include "NstPatcher.hpp"
#include "NstXml.hpp"
#include "NstStream.hpp"
#include "NstChecksum.hpp"
#include "NstCartridge.hpp"
#include "NstCartridgeRomset.hpp"
#include "api/NstApiCartridge.hpp"
#include "api/NstApiUser.hpp"
namespace Nes
{
namespace Core
{
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
class Cartridge::Romset::Loader
{
typedef std::vector<Profile> Profiles;
enum
{
DEFAULT_VERSION = 10,
MIN_PLAYERS = 1,
MAX_PLAYERS = 255,
MIN_CHIP_SIZE = 1,
MAX_CHIP_SIZE = SIZE_16384K,
MIN_IC_PINS = 1,
MAX_IC_PINS = 127,
MAX_CHIP_SAMPLES = 255,
MAX_MAPPER = 255
};
std::istream& imageStream;
std::istream* const patchStream;
const FavoredSystem favoredSystem;
Ram& prg;
Ram& chr;
Profile& profile;
Profiles profiles;
Result* const patchResult;
const bool askProfile;
const bool readOnly;
const bool patchBypassChecksum;
public:
Loader
(
std::istream& i,
std::istream* t,
const bool b,
Result* const e,
Ram& p,
Ram& c,
const FavoredSystem f,
const bool a,
Profile& r,
const bool o
)
:
imageStream (i),
patchStream (t),
favoredSystem (f),
prg (p),
chr (c),
profile (r),
patchResult (e),
askProfile (a),
readOnly (o),
patchBypassChecksum (b)
{
NST_ASSERT( prg.Empty() && chr.Empty() );
}
void Load()
{
Collect();
ChooseProfile();
LoadRoms();
PatchRoms();
}
private:
void Collect()
{
Xml xml;
if (!xml.Read( imageStream ))
throw RESULT_ERR_INVALID_FILE;
const Xml::Node romset( xml.GetRoot() );
if (!romset.IsType( L"romset" ))
throw RESULT_ERR_INVALID_FILE;
uint version = DEFAULT_VERSION;
if (const Xml::Attribute attribute=romset.GetAttribute( L"version" ))
{
wcstring const string = attribute.GetValue();
if
(
(string[0] >= L'1' && string[0] <= L'9') &&
(string[1] == L'.') &&
(string[2] >= L'0' && string[2] <= L'9') &&
(string[3] == L'\0')
)
{
version = (string[0] - L'0') * 10 + (string[2] - L'0');
}
else
{
throw RESULT_ERR_INVALID_FILE;
}
}
bool strict = true;
if (const Xml::Attribute attribute=romset.GetAttribute( L"conformance" ))
{
if (attribute.IsValue( L"loose" ))
{
strict = false;
}
else if (version != DEFAULT_VERSION && !attribute.IsValue( L"strict" ))
{
throw RESULT_ERR_INVALID_FILE;
}
}
profiles.reserve( 4 );
for (Xml::Node game(xml.GetRoot().GetFirstChild()); game; game=game.GetNextSibling())
{
for (Xml::Node image(game.GetFirstChild()); image; image=image.GetNextSibling())
{
Profile p;
p.game.title = game.GetAttribute( L"name" ).GetValue();
p.game.altTitle = game.GetAttribute( L"altname" ).GetValue();
p.game.clss = game.GetAttribute( L"class" ).GetValue();
p.game.subClss = game.GetAttribute( L"subclass" ).GetValue();
p.game.catalog = game.GetAttribute( L"catalog" ).GetValue();
p.game.publisher = game.GetAttribute( L"publisher" ).GetValue();
p.game.developer = game.GetAttribute( L"developer" ).GetValue();
p.game.portDeveloper = game.GetAttribute( L"portdeveloper" ).GetValue();
p.game.region = game.GetAttribute( L"region" ).GetValue();
if (const Xml::Attribute attribute=game.GetAttribute( L"players" ))
{
const ulong players = attribute.GetUnsignedValue();
if (players >= MIN_PLAYERS && players <= MAX_PLAYERS)
{
p.game.players = players;
}
else if (strict)
{
throw RESULT_ERR_INVALID_FILE;
}
}
if (image.IsType( L"cartridge" ))
{
if (const Xml::Attribute attribute=image.GetAttribute( L"system" ))
{
if (attribute.IsValue( L"famicom" ))
{
p.system.type = Profile::System::FAMICOM;
}
else if (attribute.IsValue( L"nes-ntsc" ))
{
p.system.type = Profile::System::NES_NTSC;
}
else if (attribute.IsValue( L"nes-pal" ))
{
p.system.type = Profile::System::NES_PAL;
p.system.cpu = Profile::System::CPU_RP2A07;
p.system.ppu = Profile::System::PPU_RP2C07;
}
else if (attribute.IsValue( L"nes-pal-a" ))
{
p.system.type = Profile::System::NES_PAL_A;
p.system.cpu = Profile::System::CPU_RP2A07;
p.system.ppu = Profile::System::PPU_RP2C07;
}
else if (attribute.IsValue( L"nes-pal-b" ))
{
p.system.type = Profile::System::NES_PAL_B;
p.system.cpu = Profile::System::CPU_RP2A07;
p.system.ppu = Profile::System::PPU_RP2C07;
}
else if (attribute.IsValue( L"dendy" ))
{
p.system.type = Profile::System::DENDY;
p.system.cpu = Profile::System::CPU_DENDY;
p.system.ppu = Profile::System::PPU_DENDY;
}
else if (!strict)
{
if (favoredSystem == FAVORED_NES_PAL)
{
p.system.type = Profile::System::NES_PAL;
p.system.cpu = Profile::System::CPU_RP2A07;
p.system.ppu = Profile::System::PPU_RP2C07;
}
else if (favoredSystem == FAVORED_FAMICOM)
{
p.system.type = Profile::System::FAMICOM;
}
else if (favoredSystem == FAVORED_DENDY)
{
p.system.type = Profile::System::DENDY;
p.system.cpu = Profile::System::CPU_DENDY;
p.system.ppu = Profile::System::PPU_DENDY;
}
else
{
p.system.type = Profile::System::NES_NTSC;
}
}
else
{
throw RESULT_ERR_INVALID_FILE;
}
}
else if (strict)
{
throw RESULT_ERR_INVALID_FILE;
}
}
else if (image.IsType( L"arcade" ))
{
p.system.ppu = Profile::System::PPU_RP2C03B;
if (const Xml::Attribute attribute=image.GetAttribute( L"system" ))
{
if (attribute.IsValue( L"vs-unisystem" ))
{
p.system.type = Profile::System::VS_UNISYSTEM;
}
else if (attribute.IsValue( L"vs-dualsystem" ))
{
p.system.type = Profile::System::VS_DUALSYSTEM;
}
else if (attribute.IsValue( L"playchoice-10" ))
{
p.system.type = Profile::System::PLAYCHOICE_10;
}
else
{
throw RESULT_ERR_INVALID_FILE;
}
}
else
{
throw RESULT_ERR_INVALID_FILE;
}
}
else
{
throw RESULT_ERR_INVALID_FILE;
}
p.dump.by = image.GetAttribute( L"dumper" ).GetValue();
p.dump.date = image.GetAttribute( L"datedumped" ).GetValue();
p.dump.state = Profile::Dump::OK;
if (const Xml::Attribute attribute=image.GetAttribute( L"dump" ))
{
if (attribute.IsValue( L"bad" ))
{
p.dump.state = Profile::Dump::BAD;
}
else if (attribute.IsValue( L"unknown" ))
{
p.dump.state = Profile::Dump::UNKNOWN;
}
else if (strict && !attribute.IsValue( L"ok" ))
{
throw RESULT_ERR_INVALID_FILE;
}
}
if (Xml::Node node=image.GetChild( L"properties" ))
{
for (node=node.GetFirstChild(); node.IsType( L"property" ); node=node.GetNextSibling())
{
Profile::Property property;
property.name = node.GetAttribute( L"name" ).GetValue();
property.value = node.GetAttribute( L"value" ).GetValue();
p.properties.push_back( property );
}
}
if (p.system.type == Profile::System::VS_UNISYSTEM || p.system.type == Profile::System::VS_DUALSYSTEM)
{
if (const Xml::Attribute attribute=image.GetAttribute( L"ppu" ))
{
if (attribute.IsValue( L"rp2c03b" )) p.system.ppu = Profile::System::PPU_RP2C03B;
else if (attribute.IsValue( L"rp2c03g" )) p.system.ppu = Profile::System::PPU_RP2C03G;
else if (attribute.IsValue( L"rp2c04-0001" )) p.system.ppu = Profile::System::PPU_RP2C04_0001;
else if (attribute.IsValue( L"rp2c04-0002" )) p.system.ppu = Profile::System::PPU_RP2C04_0002;
else if (attribute.IsValue( L"rp2c04-0003" )) p.system.ppu = Profile::System::PPU_RP2C04_0003;
else if (attribute.IsValue( L"rp2c04-0004" )) p.system.ppu = Profile::System::PPU_RP2C04_0004;
else if (attribute.IsValue( L"rc2c03b" )) p.system.ppu = Profile::System::PPU_RC2C03B;
else if (attribute.IsValue( L"rc2c03c" )) p.system.ppu = Profile::System::PPU_RC2C03C;
else if (attribute.IsValue( L"rc2c05-01" )) p.system.ppu = Profile::System::PPU_RC2C05_01;
else if (attribute.IsValue( L"rc2c05-02" )) p.system.ppu = Profile::System::PPU_RC2C05_02;
else if (attribute.IsValue( L"rc2c05-03" )) p.system.ppu = Profile::System::PPU_RC2C05_03;
else if (attribute.IsValue( L"rc2c05-04" )) p.system.ppu = Profile::System::PPU_RC2C05_04;
else if (attribute.IsValue( L"rc2c05-05" )) p.system.ppu = Profile::System::PPU_RC2C05_05;
}
}
p.game.revision = image.GetAttribute( L"revision" ).GetValue();
p.hash.Assign
(
image.GetAttribute( L"sha1" ).GetValue(),
image.GetAttribute( L"crc" ).GetValue()
);
if (const Xml::Node board=image.GetChild( L"board" ))
{
p.board.type = board.GetAttribute( L"type" ).GetValue();
p.board.pcb = board.GetAttribute( L"pcb" ).GetValue();
p.board.cic = board.GetChild( L"cic" ).GetAttribute( L"type" ).GetValue();
if (const Xml::Attribute attribute=board.GetAttribute( L"mapper" ))
{
const ulong mapper = attribute.GetUnsignedValue();
if (mapper <= MAX_MAPPER)
{
p.board.mapper = mapper;
}
else if (strict)
{
throw RESULT_ERR_INVALID_FILE;
}
}
if (const Xml::Node pad=board.GetChild( L"pad" ))
{
p.board.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)
);
}
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;
}
}
Profile::Board::Pins pins;
Profile::Board::Samples samples;
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)
{
Profile::Board::Pin pin;
pin.number = number;
pin.function = function;
pins.push_back( pin );
}
}
else if (child.IsType(L"sample"))
{
const ulong id = child.GetAttribute(L"id").GetUnsignedValue();
wcstring const file = child.GetAttribute(L"file").GetValue();
if (id <= MAX_CHIP_SAMPLES && *file)
{
Profile::Board::Sample sample;
sample.id = id;
sample.file = file;
samples.push_back( sample );
}
}
}
bool first;
if (true == (first=node.IsType( L"prg" )) || node.IsType( L"chr" ))
{
if (size < MIN_CHIP_SIZE || size > MAX_CHIP_SIZE)
throw RESULT_ERR_INVALID_FILE;
Profile::Board::Roms& roms = (first ? p.board.prg : p.board.chr);
Profile::Board::Rom rom;
rom.id = node.GetAttribute( L"id" ).GetUnsignedValue();
rom.size = size;
rom.name = node.GetAttribute( L"name" ).GetValue();
rom.pins = pins;
rom.file = node.GetAttribute( L"file" ).GetValue();
rom.package = node.GetAttribute( L"package" ).GetValue();
rom.hash.Assign( node.GetAttribute( L"sha1" ).GetValue(), node.GetAttribute( L"crc" ).GetValue() );
for (Profile::Board::Roms::iterator it(roms.begin()), end(roms.end()); ; ++it)
{
if (it == end || it->id > rom.id)
{
roms.insert( it, rom );
break;
}
}
}
else if (true == (first=node.IsType( L"wram" )) || node.IsType( L"vram" ))
{
if (size < MIN_CHIP_SIZE || size > MAX_CHIP_SIZE)
throw RESULT_ERR_INVALID_FILE;
Profile::Board::Rams& rams = (first ? p.board.wram : p.board.vram);
Profile::Board::Ram ram;
ram.id = node.GetAttribute( L"id" ).GetUnsignedValue();
ram.size = size;
ram.file = node.GetAttribute( L"file" ).GetValue();
ram.pins = pins;
ram.package = node.GetAttribute( L"package" ).GetValue();
ram.battery = node.GetAttribute( L"battery" ).IsValue( L"1" );
for (Profile::Board::Rams::iterator it(rams.begin()), end(rams.end()); ; ++it)
{
if (it == end || it->id > ram.id)
{
rams.insert( it, ram );
break;
}
}
}
else if (node.IsType( L"chip" ))
{
Profile::Board::Chip chip;
chip.type = node.GetAttribute( L"type" ).GetValue();
chip.file = node.GetAttribute( L"file" ).GetValue();
chip.pins = pins;
chip.samples = samples;
chip.battery = node.GetAttribute( L"battery" ).IsValue( L"1" );
chip.package = node.GetAttribute( L"package" ).GetValue();
p.board.chips.push_back( chip );
}
}
}
if (Xml::Node device=game.GetChild( L"peripherals" ))
{
for (device=device.GetFirstChild(); device.IsType( L"device" ); device=device.GetNextSibling())
{
if (const Xml::Attribute attribute = device.GetAttribute( L"type" ))
{
if (attribute.IsValue( L"arkanoid" ))
{
if (p.system.type == Profile::System::FAMICOM)
p.game.controllers[4] = Api::Input::PADDLE;
else
p.game.controllers[1] = Api::Input::PADDLE;
}
else if (attribute.IsValue( L"bandaihypershot" ))
{
p.game.controllers[4] = Api::Input::BANDAIHYPERSHOT;
}
else if (attribute.IsValue( L"barcodeworld" ))
{
p.game.controllers[4] = Api::Input::BARCODEWORLD;
}
else if (attribute.IsValue( L"crazyclimber" ))
{
p.game.controllers[4] = Api::Input::CRAZYCLIMBER;
}
else if (attribute.IsValue( L"doremikko" ))
{
p.game.controllers[4] = Api::Input::DOREMIKKOKEYBOARD;
}
else if (attribute.IsValue( L"excitingboxing" ))
{
p.game.controllers[4] = Api::Input::EXCITINGBOXING;
}
else if (attribute.IsValue( L"familykeyboard" ))
{
p.game.controllers[4] = Api::Input::FAMILYKEYBOARD;
}
else if (attribute.IsValue( L"familytrainer" ))
{
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::FAMILYTRAINER;
}
else if (attribute.IsValue( L"fourplayer" ))
{
if (p.system.type == Profile::System::FAMICOM)
p.game.adapter = Api::Input::ADAPTER_FAMICOM;
else
p.game.adapter = Api::Input::ADAPTER_NES;
p.game.controllers[2] = Api::Input::PAD3;
p.game.controllers[3] = Api::Input::PAD4;
}
else if (attribute.IsValue( L"horitrack" ))
{
p.game.controllers[4] = Api::Input::HORITRACK;
}
else if (attribute.IsValue( L"konamihypershot" ))
{
p.game.controllers[0] = Api::Input::UNCONNECTED;
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::KONAMIHYPERSHOT;
}
else if (attribute.IsValue( L"mahjong" ))
{
p.game.controllers[0] = Api::Input::UNCONNECTED;
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::MAHJONG;
}
else if (attribute.IsValue( L"oekakidstablet" ))
{
p.game.controllers[0] = Api::Input::UNCONNECTED;
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::OEKAKIDSTABLET;
}
else if (attribute.IsValue( L"pachinko" ))
{
p.game.controllers[4] = Api::Input::PACHINKO;
}
else if (attribute.IsValue( L"partytap" ))
{
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::PARTYTAP;
}
else if (attribute.IsValue( L"pokkunmoguraa" ))
{
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::POKKUNMOGURAA;
}
else if (attribute.IsValue( L"powerglove" ))
{
p.game.controllers[0] = Api::Input::POWERGLOVE;
}
else if (attribute.IsValue( L"powerpad" ) || attribute.IsValue( L"familyfunfitness" ))
{
if (p.system.type == Profile::System::FAMICOM)
{
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::FAMILYTRAINER;
}
else
{
p.game.controllers[1] = Api::Input::POWERPAD;
}
}
else if (attribute.IsValue( L"rob" ))
{
p.game.controllers[1] = Api::Input::ROB;
}
else if (attribute.IsValue( L"suborkeyboard" ))
{
p.game.controllers[4] = Api::Input::SUBORKEYBOARD;
}
else if (attribute.IsValue( L"subormouse" ))
{
p.game.controllers[1] = Api::Input::MOUSE;
}
else if (attribute.IsValue( L"standard" ))
{
p.game.controllers[0] = Api::Input::PAD1;
p.game.controllers[1] = Api::Input::PAD2;
}
else if (attribute.IsValue( L"topriderbike" ))
{
p.game.controllers[0] = Api::Input::UNCONNECTED;
p.game.controllers[1] = Api::Input::UNCONNECTED;
p.game.controllers[4] = Api::Input::TOPRIDER;
}
else if (attribute.IsValue( L"turbofile" ))
{
p.game.controllers[4] = Api::Input::TURBOFILE;
}
else if (attribute.IsValue( L"zapper" ))
{
if (p.system.type == Profile::System::VS_UNISYSTEM || p.system.type == Profile::System::VS_DUALSYSTEM)
{
p.game.controllers[0] = Api::Input::ZAPPER;
p.game.controllers[1] = Api::Input::UNCONNECTED;
}
else
{
p.game.controllers[1] = Api::Input::ZAPPER;
}
}
}
}
}
profiles.push_back( p );
}
}
}
void ChooseProfile()
{
if (profiles.empty())
throw RESULT_ERR_INVALID_FILE;
Profiles::const_iterator bestMatch( profiles.begin() );
if (profiles.size() > 1)
{
for (Profiles::const_iterator it(profiles.begin()), end(profiles.end()); it != end; ++it)
{
if (it->system.type == Profile::System::NES_NTSC)
{
if (favoredSystem == FAVORED_NES_NTSC)
{
bestMatch = it;
break;
}
}
else if
(
it->system.type == Profile::System::NES_PAL ||
it->system.type == Profile::System::NES_PAL_A ||
it->system.type == Profile::System::NES_PAL_B
)
{
if (favoredSystem == FAVORED_NES_PAL)
{
bestMatch = it;
break;
}
}
else if (it->system.type == Profile::System::FAMICOM)
{
if (favoredSystem == FAVORED_FAMICOM)
{
bestMatch = it;
break;
}
}
else if (it->system.type == Profile::System::DENDY)
{
if (favoredSystem == FAVORED_DENDY)
{
bestMatch = it;
break;
}
}
}
if (askProfile && Api::Cartridge::chooseProfileCallback)
{
std::vector<std::wstring> names( profiles.size() );
std::vector<std::wstring>::iterator dst( names.begin() );
for (Profiles::const_iterator src(profiles.begin()), end(profiles.end()); src != end; ++src, ++dst)
{
*dst = src->game.title;
if (!src->game.revision.empty())
{
*dst += dst->empty() ? L"(Rev. " : L" (Rev. ";
*dst += src->game.revision;
*dst += L')';
}
if (!src->game.region.empty())
{
*dst += dst->empty() ? L"(" : L" (";
*dst += src->game.region;
*dst += L')';
}
if (!dst->empty())
*dst += L' ';
*dst +=
(
src->system.type == Profile::System::NES_PAL ? L"(NES-PAL)" :
src->system.type == Profile::System::NES_PAL_A ? L"(NES-PAL-A)" :
src->system.type == Profile::System::NES_PAL_B ? L"(NES-PAL-B)" :
src->system.type == Profile::System::FAMICOM ? L"(Famicom)" :
src->system.type == Profile::System::DENDY ? L"(Dendy)" :
src->system.type == Profile::System::VS_UNISYSTEM ? L"(VS)" :
src->system.type == Profile::System::VS_DUALSYSTEM ? L"(VS)" :
src->system.type == Profile::System::PLAYCHOICE_10 ? L"(PC10)" :
L"(NES-NTSC)"
);
}
const uint selected = Api::Cartridge::chooseProfileCallback( &profiles.front(), &names.front(), profiles.size() );
if (selected < profiles.size())
bestMatch = profiles.begin() + selected;
}
}
profile = *bestMatch;
if (profiles.size() > 1)
{
uint regions = 0x0;
for (Profiles::const_iterator it(profiles.begin()), end(profiles.end()); it != end; ++it)
{
if (profile.hash == it->hash)
{
switch (it->system.type)
{
case Profile::System::NES_PAL:
case Profile::System::NES_PAL_A:
case Profile::System::NES_PAL_B:
case Profile::System::DENDY:
regions |= 0x1;
break;
default:
regions |= 0x2;
break;
}
}
if (regions == 0x3)
{
profile.multiRegion = true;
break;
}
}
}
}
void LoadRoms()
{
class Loader : public Api::User::File
{
wcstring const filename;
byte* const rom;
const dword size;
bool loaded;
Action GetAction() const throw()
{
return LOAD_ROM;
}
wcstring GetName() const throw()
{
return filename;
}
ulong GetMaxSize() const throw()
{
return size;
}
Result SetContent(const void* filedata,ulong filesize) throw()
{
if (filesize)
{
if (filedata)
{
std::memcpy( rom, filedata, NST_MIN(size,filesize) );
loaded = true;
}
else
{
return RESULT_ERR_INVALID_PARAM;
}
}
return RESULT_OK;
}
Result SetContent(std::istream& stdStream) throw()
{
try
{
Nes::Core::Stream::In stream( &stdStream );
if (const ulong length = stream.Length())
{
stream.Read( rom, NST_MIN(size,length) );
loaded = true;
}
}
catch (Result result)
{
return result;
}
catch (const std::bad_alloc&)
{
return RESULT_ERR_OUT_OF_MEMORY;
}
catch (...)
{
return RESULT_ERR_GENERIC;
}
return RESULT_OK;
}
public:
Loader(wcstring f,byte* r,dword s)
: filename(f), rom(r), size(s), loaded(false) {}
bool Loaded() const
{
return loaded;
}
};
for (uint i=0; i < 2; ++i)
{
Profile::Board::Roms& roms = (i ? profile.board.chr : profile.board.prg);
if (roms.empty())
continue;
Ram& rom = (i ? chr : prg);
dword size = 0;
for (Profile::Board::Roms::iterator it(roms.begin()), end(roms.end()); it != end; ++it)
{
size += it->size;
if (it->size < MIN_CHIP_SIZE || size > MAX_CHIP_SIZE)
throw RESULT_ERR_INVALID_FILE;
}
rom.Set( size );
for (Profile::Board::Pins::const_iterator pin(roms.begin()->pins.begin()), end(roms.begin()->pins.end()); pin != end; ++pin)
rom.Pin(pin->number) = pin->function.c_str();
if (readOnly)
continue;
if (!Api::User::fileIoCallback)
throw RESULT_ERR_NOT_READY;
size = 0;
for (Profile::Board::Roms::iterator it(roms.begin()), end(roms.end()); it != end; ++it)
{
if (it->file.empty())
throw RESULT_ERR_INVALID_FILE;
Loader loader( it->file.c_str(), rom.Mem(size), it->size );
Api::User::fileIoCallback( loader );
if (!loader.Loaded())
throw RESULT_ERR_INVALID_FILE;
size += it->size;
}
}
}
void PatchRoms()
{
if (patchStream)
{
Patcher patcher( patchBypassChecksum );
*patchResult = patcher.Load( *patchStream );
if (NES_SUCCEEDED(*patchResult))
{
const Patcher::Block blocks[] =
{
{ prg.Mem(), prg.Size() },
{ chr.Mem(), chr.Size() }
};
*patchResult = patcher.Test( blocks );
if (NES_SUCCEEDED(*patchResult))
{
if (patcher.Patch( prg.Mem(), prg.Mem(), prg.Size(), 16 ))
{
profile.patched = true;
Log::Flush( "Romset: PRG-ROM was patched" NST_LINEBREAK );
}
if (patcher.Patch( chr.Mem(), chr.Mem(), chr.Size(), 16 + prg.Size() ))
{
profile.patched = true;
Log::Flush( "Romset: CHR-ROM was patched" NST_LINEBREAK );
}
}
}
}
}
};
void Cartridge::Romset::Load
(
std::istream& stdStreamImage,
std::istream* const stdStreamPatch,
const bool patchBypassChecksum,
Result* const patchResult,
Ram& prg,
Ram& chr,
const FavoredSystem favoredSystem,
const bool askProfile,
Profile& profile,
const bool readOnly
)
{
Loader loader
(
stdStreamImage,
stdStreamPatch,
patchBypassChecksum,
patchResult,
prg,
chr,
favoredSystem,
askProfile,
profile,
readOnly
);
loader.Load();
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
}
}