775 lines
19 KiB
C++
775 lines
19 KiB
C++
//
|
|
// Copyright (C) 2007-2010 by sinamas <sinamas at users.sourceforge.net>
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2 as
|
|
// published by the Free Software Foundation.
|
|
//
|
|
// This program 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 version 2 for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// version 2 along with this program; if not, write to the
|
|
// Free Software Foundation, Inc.,
|
|
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
//
|
|
|
|
#include "cartridge.h"
|
|
#include "file/file.h"
|
|
#include "../savestate.h"
|
|
#include "pakinfo_internal.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
|
|
using namespace gambatte;
|
|
|
|
namespace {
|
|
|
|
unsigned toMulti64Rombank(unsigned rombank) {
|
|
return (rombank >> 1 & 0x30) | (rombank & 0xF);
|
|
}
|
|
|
|
class DefaultMbc : public Mbc {
|
|
public:
|
|
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
|
|
return (addr < 0x4000) == (bank == 0);
|
|
}
|
|
};
|
|
|
|
class Mbc0 : public DefaultMbc {
|
|
public:
|
|
explicit Mbc0(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, enableRam_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
if (p < 0x2000) {
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.enableRam = enableRam_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
enableRam_ = ss.enableRam;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
bool enableRam_;
|
|
};
|
|
|
|
inline unsigned rambanks(MemPtrs const &memptrs) {
|
|
return (memptrs.rambankdataend() - memptrs.rambankdata()) / rambank_size();
|
|
}
|
|
|
|
inline unsigned rombanks(MemPtrs const &memptrs) {
|
|
return (memptrs.romdataend() - memptrs.romdata()) / rombank_size();
|
|
}
|
|
|
|
class Mbc1 : public DefaultMbc {
|
|
public:
|
|
explicit Mbc1(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, rombank_(1)
|
|
, rambank_(0)
|
|
, enableRam_(false)
|
|
, rambankMode_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p >> 13 & 3) {
|
|
case 0:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
setRambank();
|
|
break;
|
|
case 1:
|
|
rombank_ = rambankMode_ ? data & 0x1F : (rombank_ & 0x60) | (data & 0x1F);
|
|
setRombank();
|
|
break;
|
|
case 2:
|
|
if (rambankMode_) {
|
|
rambank_ = data & 3;
|
|
setRambank();
|
|
} else {
|
|
rombank_ = (data << 5 & 0x60) | (rombank_ & 0x1F);
|
|
setRombank();
|
|
}
|
|
|
|
break;
|
|
case 3:
|
|
// Should this take effect immediately rather?
|
|
rambankMode_ = data & 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.rambank = rambank_;
|
|
ss.enableRam = enableRam_;
|
|
ss.rambankMode = rambankMode_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
rambank_ = ss.rambank;
|
|
enableRam_ = ss.enableRam;
|
|
rambankMode_ = ss.rambankMode;
|
|
setRambank();
|
|
setRombank();
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
unsigned char rombank_;
|
|
unsigned char rambank_;
|
|
bool enableRam_;
|
|
bool rambankMode_;
|
|
|
|
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
|
|
|
|
void setRambank() const {
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0,
|
|
rambank_ & (rambanks(memptrs_) - 1));
|
|
}
|
|
|
|
void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); }
|
|
};
|
|
|
|
class Mbc1Multi64 : public Mbc {
|
|
public:
|
|
explicit Mbc1Multi64(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, rombank_(1)
|
|
, enableRam_(false)
|
|
, rombank0Mode_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p >> 13 & 3) {
|
|
case 0:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
break;
|
|
case 1:
|
|
rombank_ = (rombank_ & 0x60) | (data & 0x1F);
|
|
memptrs_.setRombank(rombank0Mode_
|
|
? adjustedRombank(toMulti64Rombank(rombank_))
|
|
: adjustedRombank(rombank_) & (rombanks(memptrs_) - 1));
|
|
break;
|
|
case 2:
|
|
rombank_ = (data << 5 & 0x60) | (rombank_ & 0x1F);
|
|
setRombank();
|
|
break;
|
|
case 3:
|
|
rombank0Mode_ = data & 1;
|
|
setRombank();
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.enableRam = enableRam_;
|
|
ss.rambankMode = rombank0Mode_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
enableRam_ = ss.enableRam;
|
|
rombank0Mode_ = ss.rambankMode;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
setRombank();
|
|
}
|
|
|
|
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
|
|
return (addr < 0x4000) == ((bank & 0xF) == 0);
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
unsigned char rombank_;
|
|
bool enableRam_;
|
|
bool rombank0Mode_;
|
|
|
|
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
|
|
|
|
void setRombank() const {
|
|
if (rombank0Mode_) {
|
|
unsigned const rb = toMulti64Rombank(rombank_);
|
|
memptrs_.setRombank0(rb & 0x30);
|
|
memptrs_.setRombank(adjustedRombank(rb));
|
|
} else {
|
|
memptrs_.setRombank0(0);
|
|
memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1));
|
|
}
|
|
}
|
|
};
|
|
|
|
class Mbc2 : public DefaultMbc {
|
|
public:
|
|
explicit Mbc2(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, rombank_(1)
|
|
, enableRam_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p & 0x6100) {
|
|
case 0x0000:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
break;
|
|
case 0x2100:
|
|
rombank_ = data & 0xF;
|
|
memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1));
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.enableRam = enableRam_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
enableRam_ = ss.enableRam;
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
|
|
memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1));
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
unsigned char rombank_;
|
|
bool enableRam_;
|
|
};
|
|
|
|
class Mbc3 : public DefaultMbc {
|
|
public:
|
|
Mbc3(MemPtrs &memptrs, Rtc *const rtc)
|
|
: memptrs_(memptrs)
|
|
, rtc_(rtc)
|
|
, rombank_(1)
|
|
, rambank_(0)
|
|
, enableRam_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p >> 13 & 3) {
|
|
case 0:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
setRambank();
|
|
break;
|
|
case 1:
|
|
rombank_ = data & 0x7F;
|
|
setRombank();
|
|
break;
|
|
case 2:
|
|
rambank_ = data;
|
|
setRambank();
|
|
break;
|
|
case 3:
|
|
if (rtc_)
|
|
rtc_->latch(data);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.rambank = rambank_;
|
|
ss.enableRam = enableRam_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
rambank_ = ss.rambank;
|
|
enableRam_ = ss.enableRam;
|
|
setRambank();
|
|
setRombank();
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
Rtc *const rtc_;
|
|
unsigned char rombank_;
|
|
unsigned char rambank_;
|
|
bool enableRam_;
|
|
|
|
void setRambank() const {
|
|
unsigned flags = enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0;
|
|
|
|
if (rtc_) {
|
|
rtc_->set(enableRam_, rambank_);
|
|
|
|
if (rtc_->activeData())
|
|
flags |= MemPtrs::rtc_en;
|
|
}
|
|
|
|
memptrs_.setRambank(flags, rambank_ & (rambanks(memptrs_) - 1));
|
|
}
|
|
|
|
void setRombank() const {
|
|
memptrs_.setRombank(std::max(rombank_ & (rombanks(memptrs_) - 1), 1u));
|
|
}
|
|
};
|
|
|
|
class HuC1 : public DefaultMbc {
|
|
public:
|
|
explicit HuC1(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, rombank_(1)
|
|
, rambank_(0)
|
|
, enableRam_(false)
|
|
, rambankMode_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p >> 13 & 3) {
|
|
case 0:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
setRambank();
|
|
break;
|
|
case 1:
|
|
rombank_ = data & 0x3F;
|
|
setRombank();
|
|
break;
|
|
case 2:
|
|
rambank_ = data & 3;
|
|
rambankMode_ ? setRambank() : setRombank();
|
|
break;
|
|
case 3:
|
|
rambankMode_ = data & 1;
|
|
setRambank();
|
|
setRombank();
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.rambank = rambank_;
|
|
ss.enableRam = enableRam_;
|
|
ss.rambankMode = rambankMode_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
rambank_ = ss.rambank;
|
|
enableRam_ = ss.enableRam;
|
|
rambankMode_ = ss.rambankMode;
|
|
setRambank();
|
|
setRombank();
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
unsigned char rombank_;
|
|
unsigned char rambank_;
|
|
bool enableRam_;
|
|
bool rambankMode_;
|
|
|
|
void setRambank() const {
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : MemPtrs::read_en,
|
|
rambankMode_ ? rambank_ & (rambanks(memptrs_) - 1) : 0);
|
|
}
|
|
|
|
void setRombank() const {
|
|
memptrs_.setRombank((rambankMode_ ? rombank_ : rambank_ << 6 | rombank_)
|
|
& (rombanks(memptrs_) - 1));
|
|
}
|
|
};
|
|
|
|
class Mbc5 : public DefaultMbc {
|
|
public:
|
|
explicit Mbc5(MemPtrs &memptrs)
|
|
: memptrs_(memptrs)
|
|
, rombank_(1)
|
|
, rambank_(0)
|
|
, enableRam_(false)
|
|
{
|
|
}
|
|
|
|
virtual void romWrite(unsigned const p, unsigned const data) {
|
|
switch (p >> 13 & 3) {
|
|
case 0:
|
|
enableRam_ = (data & 0xF) == 0xA;
|
|
setRambank();
|
|
break;
|
|
case 1:
|
|
rombank_ = p < 0x3000
|
|
? (rombank_ & 0x100) | data
|
|
: (data << 8 & 0x100) | (rombank_ & 0xFF);
|
|
setRombank();
|
|
break;
|
|
case 2:
|
|
rambank_ = data & 0xF;
|
|
setRambank();
|
|
break;
|
|
case 3:
|
|
break;
|
|
}
|
|
}
|
|
|
|
virtual void saveState(SaveState::Mem &ss) const {
|
|
ss.rombank = rombank_;
|
|
ss.rambank = rambank_;
|
|
ss.enableRam = enableRam_;
|
|
}
|
|
|
|
virtual void loadState(SaveState::Mem const &ss) {
|
|
rombank_ = ss.rombank;
|
|
rambank_ = ss.rambank;
|
|
enableRam_ = ss.enableRam;
|
|
setRambank();
|
|
setRombank();
|
|
}
|
|
|
|
private:
|
|
MemPtrs &memptrs_;
|
|
unsigned short rombank_;
|
|
unsigned char rambank_;
|
|
bool enableRam_;
|
|
|
|
void setRambank() const {
|
|
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0,
|
|
rambank_ & (rambanks(memptrs_) - 1));
|
|
}
|
|
|
|
void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); }
|
|
};
|
|
|
|
std::string stripExtension(std::string const &str) {
|
|
std::string::size_type const lastDot = str.find_last_of('.');
|
|
std::string::size_type const lastSlash = str.find_last_of('/');
|
|
|
|
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
|
|
return str.substr(0, lastDot);
|
|
|
|
return str;
|
|
}
|
|
|
|
std::string stripDir(std::string const &str) {
|
|
std::string::size_type const lastSlash = str.find_last_of('/');
|
|
if (lastSlash != std::string::npos)
|
|
return str.substr(lastSlash + 1);
|
|
|
|
return str;
|
|
}
|
|
|
|
void enforce8bit(unsigned char *data, std::size_t size) {
|
|
if (static_cast<unsigned char>(0x100))
|
|
while (size--)
|
|
*data++ &= 0xFF;
|
|
}
|
|
|
|
unsigned pow2ceil(unsigned n) {
|
|
--n;
|
|
n |= n >> 1;
|
|
n |= n >> 2;
|
|
n |= n >> 4;
|
|
n |= n >> 8;
|
|
++n;
|
|
|
|
return n;
|
|
}
|
|
|
|
bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
|
|
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
|
|
}
|
|
|
|
bool hasBattery(unsigned char headerByte0x147) {
|
|
switch (headerByte0x147) {
|
|
case 0x03:
|
|
case 0x06:
|
|
case 0x09:
|
|
case 0x0F:
|
|
case 0x10:
|
|
case 0x13:
|
|
case 0x1B:
|
|
case 0x1E:
|
|
case 0xFF:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool hasRtc(unsigned headerByte0x147) {
|
|
switch (headerByte0x147) {
|
|
case 0x0F:
|
|
case 0x10:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int asHex(char c) {
|
|
return c >= 'A' ? c - 'A' + 0xA : c - '0';
|
|
}
|
|
|
|
}
|
|
|
|
void Cartridge::setStatePtrs(SaveState &state) {
|
|
state.mem.vram.set(memptrs_.vramdata(), memptrs_.vramdataend() - memptrs_.vramdata());
|
|
state.mem.sram.set(memptrs_.rambankdata(), memptrs_.rambankdataend() - memptrs_.rambankdata());
|
|
state.mem.wram.set(memptrs_.wramdata(0), memptrs_.wramdataend() - memptrs_.wramdata(0));
|
|
}
|
|
|
|
void Cartridge::saveState(SaveState &state) const {
|
|
mbc_->saveState(state.mem);
|
|
rtc_.saveState(state);
|
|
}
|
|
|
|
void Cartridge::loadState(SaveState const &state) {
|
|
rtc_.loadState(state);
|
|
mbc_->loadState(state.mem);
|
|
}
|
|
|
|
std::string const Cartridge::saveBasePath() const {
|
|
return saveDir_.empty()
|
|
? defaultSaveBasePath_
|
|
: saveDir_ + stripDir(defaultSaveBasePath_);
|
|
}
|
|
|
|
void Cartridge::setSaveDir(std::string const &dir) {
|
|
saveDir_ = dir;
|
|
if (!saveDir_.empty() && saveDir_[saveDir_.length() - 1] != '/')
|
|
saveDir_ += '/';
|
|
}
|
|
|
|
LoadRes Cartridge::loadROM(std::string const &romfile,
|
|
bool const forceDmg,
|
|
bool const multicartCompat)
|
|
{
|
|
scoped_ptr<File> const rom(newFileInstance(romfile));
|
|
if (rom->fail())
|
|
return LOADRES_IO_ERROR;
|
|
|
|
enum Cartridgetype { type_plain,
|
|
type_mbc1,
|
|
type_mbc2,
|
|
type_mbc3,
|
|
type_mbc5,
|
|
type_huc1 };
|
|
Cartridgetype type = type_plain;
|
|
unsigned rambanks = 1;
|
|
unsigned rombanks = 2;
|
|
bool cgb = false;
|
|
|
|
{
|
|
unsigned char header[0x150];
|
|
rom->read(reinterpret_cast<char *>(header), sizeof header);
|
|
|
|
switch (header[0x0147]) {
|
|
case 0x00: type = type_plain; break;
|
|
case 0x01:
|
|
case 0x02:
|
|
case 0x03: type = type_mbc1; break;
|
|
case 0x05:
|
|
case 0x06: type = type_mbc2; break;
|
|
case 0x08:
|
|
case 0x09: type = type_plain; break;
|
|
case 0x0B:
|
|
case 0x0C:
|
|
case 0x0D: return LOADRES_UNSUPPORTED_MBC_MMM01;
|
|
case 0x0F:
|
|
case 0x10:
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13: type = type_mbc3; break;
|
|
case 0x15:
|
|
case 0x16:
|
|
case 0x17: return LOADRES_UNSUPPORTED_MBC_MBC4;
|
|
case 0x19:
|
|
case 0x1A:
|
|
case 0x1B:
|
|
case 0x1C:
|
|
case 0x1D:
|
|
case 0x1E: type = type_mbc5; break;
|
|
case 0x20: return LOADRES_UNSUPPORTED_MBC_MBC6;
|
|
case 0x22: return LOADRES_UNSUPPORTED_MBC_MBC7;
|
|
case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA;
|
|
case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5;
|
|
case 0xFE: return LOADRES_UNSUPPORTED_MBC_HUC3;
|
|
case 0xFF: type = type_huc1; break;
|
|
default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC;
|
|
}
|
|
|
|
/*switch (header[0x0148]) {
|
|
case 0x00: rombanks = 2; break;
|
|
case 0x01: rombanks = 4; break;
|
|
case 0x02: rombanks = 8; break;
|
|
case 0x03: rombanks = 16; break;
|
|
case 0x04: rombanks = 32; break;
|
|
case 0x05: rombanks = 64; break;
|
|
case 0x06: rombanks = 128; break;
|
|
case 0x07: rombanks = 256; break;
|
|
case 0x08: rombanks = 512; break;
|
|
case 0x52: rombanks = 72; break;
|
|
case 0x53: rombanks = 80; break;
|
|
case 0x54: rombanks = 96; break;
|
|
default: return -1;
|
|
}*/
|
|
|
|
rambanks = numRambanksFromH14x(header[0x147], header[0x149]);
|
|
cgb = header[0x0143] >> 7 & (1 ^ forceDmg);
|
|
}
|
|
|
|
std::size_t const filesize = rom->size();
|
|
rombanks = std::max(pow2ceil(filesize / rombank_size()), 2u);
|
|
|
|
defaultSaveBasePath_.clear();
|
|
ggUndoList_.clear();
|
|
mbc_.reset();
|
|
memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2);
|
|
rtc_.set(false, 0);
|
|
|
|
rom->rewind();
|
|
rom->read(reinterpret_cast<char*>(memptrs_.romdata()), filesize / rombank_size() * rombank_size());
|
|
std::memset(memptrs_.romdata() + filesize / rombank_size() * rombank_size(),
|
|
0xFF,
|
|
(rombanks - filesize / rombank_size()) * rombank_size());
|
|
enforce8bit(memptrs_.romdata(), rombanks * rombank_size());
|
|
|
|
if (rom->fail())
|
|
return LOADRES_IO_ERROR;
|
|
|
|
defaultSaveBasePath_ = stripExtension(romfile);
|
|
|
|
switch (type) {
|
|
case type_plain: mbc_.reset(new Mbc0(memptrs_)); break;
|
|
case type_mbc1:
|
|
if (multicartCompat && presumedMulti64Mbc1(memptrs_.romdata(), rombanks)) {
|
|
mbc_.reset(new Mbc1Multi64(memptrs_));
|
|
} else
|
|
mbc_.reset(new Mbc1(memptrs_));
|
|
|
|
break;
|
|
case type_mbc2: mbc_.reset(new Mbc2(memptrs_)); break;
|
|
case type_mbc3:
|
|
mbc_.reset(new Mbc3(memptrs_, hasRtc(memptrs_.romdata()[0x147]) ? &rtc_ : 0));
|
|
break;
|
|
case type_mbc5: mbc_.reset(new Mbc5(memptrs_)); break;
|
|
case type_huc1: mbc_.reset(new HuC1(memptrs_)); break;
|
|
}
|
|
|
|
return LOADRES_OK;
|
|
}
|
|
|
|
void Cartridge::loadSavedata() {
|
|
std::string const &sbp = saveBasePath();
|
|
|
|
if (hasBattery(memptrs_.romdata()[0x147])) {
|
|
std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
|
|
|
|
if (file.is_open()) {
|
|
file.read(reinterpret_cast<char*>(memptrs_.rambankdata()),
|
|
memptrs_.rambankdataend() - memptrs_.rambankdata());
|
|
enforce8bit(memptrs_.rambankdata(), memptrs_.rambankdataend() - memptrs_.rambankdata());
|
|
}
|
|
}
|
|
|
|
if (hasRtc(memptrs_.romdata()[0x147])) {
|
|
std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
|
|
if (file) {
|
|
unsigned long basetime = file.get() & 0xFF;
|
|
basetime = basetime << 8 | (file.get() & 0xFF);
|
|
basetime = basetime << 8 | (file.get() & 0xFF);
|
|
basetime = basetime << 8 | (file.get() & 0xFF);
|
|
rtc_.setBaseTime(basetime);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cartridge::saveSavedata() {
|
|
std::string const &sbp = saveBasePath();
|
|
|
|
if (hasBattery(memptrs_.romdata()[0x147])) {
|
|
std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
|
|
file.write(reinterpret_cast<char const *>(memptrs_.rambankdata()),
|
|
memptrs_.rambankdataend() - memptrs_.rambankdata());
|
|
}
|
|
|
|
if (hasRtc(memptrs_.romdata()[0x147])) {
|
|
std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
|
|
unsigned long const basetime = rtc_.baseTime();
|
|
file.put(basetime >> 24 & 0xFF);
|
|
file.put(basetime >> 16 & 0xFF);
|
|
file.put(basetime >> 8 & 0xFF);
|
|
file.put(basetime & 0xFF);
|
|
}
|
|
}
|
|
|
|
void Cartridge::applyGameGenie(std::string const &code) {
|
|
if (6 < code.length()) {
|
|
unsigned const val = (asHex(code[0]) << 4 | asHex(code[1])) & 0xFF;
|
|
unsigned const addr = ( asHex(code[2]) << 8
|
|
| asHex(code[4]) << 4
|
|
| asHex(code[5])
|
|
| (asHex(code[6]) ^ 0xF) << 12) & 0x7FFF;
|
|
unsigned cmp = 0xFFFF;
|
|
if (10 < code.length()) {
|
|
cmp = (asHex(code[8]) << 4 | asHex(code[10])) ^ 0xFF;
|
|
cmp = ((cmp >> 2 | cmp << 6) ^ 0x45) & 0xFF;
|
|
}
|
|
|
|
for (unsigned bank = 0; bank < rombanks(memptrs_); ++bank) {
|
|
if (mbc_->isAddressWithinAreaRombankCanBeMappedTo(addr, bank)
|
|
&& (cmp > 0xFF || memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()] == cmp)) {
|
|
ggUndoList_.push_back(AddrData(bank * rombank_size() + addr % rombank_size(),
|
|
memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()]));
|
|
memptrs_.romdata()[bank * rombank_size() + addr % rombank_size()] = val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cartridge::setGameGenie(std::string const &codes) {
|
|
if (loaded()) {
|
|
for (std::vector<AddrData>::reverse_iterator it =
|
|
ggUndoList_.rbegin(), end = ggUndoList_.rend(); it != end; ++it) {
|
|
if (memptrs_.romdata() + it->addr < memptrs_.romdataend())
|
|
memptrs_.romdata()[it->addr] = it->data;
|
|
}
|
|
|
|
ggUndoList_.clear();
|
|
|
|
std::string code;
|
|
for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) {
|
|
code = codes.substr(pos, codes.find(';', pos) - pos);
|
|
applyGameGenie(code);
|
|
}
|
|
}
|
|
}
|
|
|
|
PakInfo const Cartridge::pakInfo(bool const multipakCompat) const {
|
|
if (loaded()) {
|
|
unsigned const rombs = rombanks(memptrs_);
|
|
return PakInfo(multipakCompat && presumedMulti64Mbc1(memptrs_.romdata(), rombs),
|
|
rombs,
|
|
memptrs_.romdata());
|
|
}
|
|
|
|
return PakInfo();
|
|
}
|