save state selection

save state osd preview snapshots
fix a few potential security holes when loading invalid state
get rid of some undefined behaviour in statesaver
always draw in rgb32, color convert afterwards, too bad for maemo/16-bit
depth users
get rid of silly c string stuff


git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@142 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24
This commit is contained in:
sinamas 2008-05-05 01:07:25 +00:00
parent 3dda26e19b
commit 686e286d6a
29 changed files with 403 additions and 286 deletions

View File

@ -635,9 +635,6 @@ int GambatteSdl::exec() {
break;
case SDL_KEYDOWN:
if (e.key.keysym.sym == SDLK_ESCAPE)
return 0;
if (e.key.keysym.mod & KMOD_CTRL) {
switch (e.key.keysym.sym) {
case SDLK_f:
@ -649,6 +646,15 @@ int GambatteSdl::exec() {
break;
default: break;
}
} else {
switch (e.key.keysym.sym) {
case SDLK_ESCAPE: return 0;
case SDLK_F5: gambatte.saveState(); break;
case SDLK_F6: gambatte.selectState(gambatte.currentState() - 1); break;
case SDLK_F7: gambatte.selectState(gambatte.currentState() + 1); break;
case SDLK_F8: gambatte.loadState(); break;
default: break;
}
}
case SDL_KEYUP:
for (std::pair<keymap_t::iterator,keymap_t::iterator> range(keyMap.equal_range(e.key.keysym.sym));

View File

@ -31,6 +31,7 @@ class CPU;
namespace Gambatte {
class GB {
CPU *const z80;
int stateNo;
public:
GB();
@ -54,6 +55,8 @@ public:
bool isCgb() const;
void saveState();
void loadState();
void selectState(int n);
int currentState() const { return stateNo; }
};
}

View File

@ -19,6 +19,8 @@
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
template<typename T>
class Array {
T *a;

View File

@ -78,7 +78,7 @@ unsigned long rgb32ToUyvy(unsigned long rgb32) {
#endif
}
void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, unsigned w, unsigned h, unsigned dstPitch) {
void rgb32ToRgb16(const Gambatte::uint_least32_t *s, Gambatte::uint_least16_t *d, const unsigned w, unsigned h, const unsigned dstPitch) {
do {
unsigned n = w;

View File

@ -20,6 +20,7 @@
#define COLORCONVERSION_H
#include "int.h"
#include <algorithm>
class Rgb32ToUyvy {
struct CacheUnit {

View File

@ -88,6 +88,14 @@ public:
memory.set_savedir(sdir);
}
const std::string saveBasePath() const {
return memory.saveBasePath();
}
void setOsdElement(std::auto_ptr<OsdElement> osdElement) {
memory.setOsdElement(osdElement);
}
bool load(const char* romfile);
void sound_fill_buffer(Gambatte::uint_least16_t *const stream, const unsigned samples) {

View File

@ -71,13 +71,3 @@ void File::read(char *buffer, size_t amount)
count = 0;
}
}
size_t File::size()
{
return(fsize);
}
size_t File::gcount()
{
return(count);
}

View File

@ -35,7 +35,8 @@ class File {
void rewind();
bool is_open();
void close();
std::size_t size();
std::size_t size() const { return fsize; };
void read(char *buffer, std::size_t amount);
std::size_t gcount();
std::size_t gcount() const { return count; }
bool fail() const { return stream.fail(); }
};

View File

@ -163,13 +163,3 @@ void File::read(char *buffer, size_t amount)
count = 0;
}
}
size_t File::size()
{
return(fsize);
}
size_t File::gcount()
{
return(count);
}

View File

@ -21,9 +21,55 @@
#include "savestate.h"
#include "statesaver.h"
#include "initstate.h"
#include <string>
#include <sstream>
#include <fstream>
class SaveStateOsdElement : public OsdElement {
Gambatte::uint_least32_t pixels[StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT];
unsigned life;
public:
SaveStateOsdElement(const char *fileName, unsigned stateNo);
const Gambatte::uint_least32_t* update();
};
SaveStateOsdElement::SaveStateOsdElement(const char *fileName, unsigned stateNo) : OsdElement(stateNo * ((160 - StateSaver::SS_WIDTH) / 10) + ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT), life(4 * 60) {
std::memset(pixels, 0, sizeof(pixels));
std::ifstream file(fileName);
if (file.is_open() && file.get() == 0) {
file.ignore(4);
file.read(reinterpret_cast<char*>(pixels), sizeof(pixels));
}
}
const Gambatte::uint_least32_t* SaveStateOsdElement::update() {
if (life--)
return pixels;
return 0;
}
static const std::string itos(int i) {
std::stringstream ss;
ss << i;
std::string out;
ss >> out;
return out;
}
static const std::string statePath(const std::string &basePath, int stateNo) {
return basePath + "_" + itos(stateNo) + ".gqs";
}
namespace Gambatte {
GB::GB() : z80(new CPU) {}
GB::GB() : z80(new CPU), stateNo(0) {}
GB::~GB() {
delete z80;
@ -82,11 +128,13 @@ bool GB::load(const char* romfile) {
const bool failed = z80->load(romfile);
SaveState state;
z80->setStatePtrs(state);
setInitState(state, z80->isCgb());
z80->loadState(state);
z80->loadSavedata();
if (!failed) {
SaveState state;
z80->setStatePtrs(state);
setInitState(state, z80->isCgb());
z80->loadState(state);
z80->loadSavedata();
}
return failed;
}
@ -107,7 +155,7 @@ void GB::saveState() {
SaveState state;
z80->setStatePtrs(state);
z80->saveState(state);
StateSaver::saveState(state, "test.gqs");
StateSaver::saveState(state, statePath(z80->saveBasePath(), stateNo).c_str());
}
void GB::loadState() {
@ -115,7 +163,14 @@ void GB::loadState() {
SaveState state;
z80->setStatePtrs(state);
StateSaver::loadState(state, "test.gqs");
z80->loadState(state);
if (StateSaver::loadState(state, statePath(z80->saveBasePath(), stateNo).c_str()))
z80->loadState(state);
}
void GB::selectState(int n) {
n -= (n / 10) * 10;
stateNo = n < 0 ? n + 10 : n;
z80->setOsdElement(std::auto_ptr<OsdElement>(new SaveStateOsdElement(statePath(z80->saveBasePath(), stateNo).c_str(), stateNo)));
}
}

View File

@ -37,9 +37,6 @@ oamDmaSrc(NULL),
vrambank(vram),
rsrambankptr(NULL),
wsrambankptr(NULL),
romfile(NULL),
savedir(NULL),
savename(NULL),
getInput(NULL),
div_lastUpdate(0),
tima_lastUpdate(0),
@ -135,10 +132,10 @@ void Memory::loadState(const SaveState &state, const unsigned long oldCc) {
next_serialtime = state.mem.next_serialtime;
lastOamDmaUpdate = state.mem.lastOamDmaUpdate;
minIntTime = state.mem.minIntTime;
rombank = state.mem.rombank;
rombank = state.mem.rombank % rombanks;
dmaSource = state.mem.dmaSource;
dmaDestination = state.mem.dmaDestination;
rambank = state.mem.rambank;
rambank = state.mem.rambank & rambanks - 1;
oamDmaPos = state.mem.oamDmaPos;
IME = state.mem.IME;
enable_ram = state.mem.enable_ram;
@ -1544,15 +1541,15 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig
ioamhram[P - 0xFE00] = data;
}
bool Memory::loadROM(const char* file) {
delete []romfile;
romfile = new char[std::strlen(file) + 1];
std::strcpy(romfile, file);
return loadROM();
static void enforce8bit(unsigned char *data, unsigned long sz) {
if (static_cast<unsigned char>(0x100))
while (sz--)
*data++ &= 0xFF;
}
bool Memory::loadROM() {
bool Memory::loadROM(const char *romfile) {
romFilePath = romfile;
File rom(romfile);
if (!rom.is_open()) {
@ -1748,22 +1745,10 @@ bool Memory::loadROM() {
rom.rewind();
rom.read(reinterpret_cast<char*>(romdata[0]), rombanks * 0x4000ul);
rom.close();
{
char *tmp = std::strrchr(romfile, '/');
if (tmp == NULL || savedir == NULL)
tmp = romfile;
else
++tmp;
const unsigned int namelen = std::strrchr(tmp, '.') == NULL ? std::strlen(tmp) : std::strrchr(tmp, '.') - tmp;
delete []savename;
savename = new char[namelen + 1];
std::strncpy(savename, tmp, namelen);
savename[namelen] = '\0';
}
enforce8bit(romdata[0], rombanks * 0x4000ul);
if (rom.fail())
return 1;
sound.init(isCgb());
display.reset(isCgb());
@ -1772,108 +1757,82 @@ bool Memory::loadROM() {
}
void Memory::loadSavedata() {
const std::string &sbp = saveBasePath();
if (battery) {
char *savefile;
std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
if (savedir != NULL) {
savefile = new char[5 + std::strlen(savedir) + std::strlen(savename)];
std::sprintf(savefile, "%s%s.sav", savedir, savename);
} else {
savefile = new char[5 + std::strlen(savename)];
std::sprintf(savefile, "%s.sav", savename);
if (file.is_open()) {
file.read(reinterpret_cast<char*>(rambankdata), rambanks * 0x2000ul);
enforce8bit(rambankdata, rambanks * 0x2000ul);
}
std::ifstream load(savefile, std::ios::binary | std::ios::in);
if (load.is_open()) {
load.read(reinterpret_cast<char*>(rambankdata), rambanks * 0x2000ul);
load.close();
}
//else cout << "No savefile available\n";
delete []savefile;
}
if (rtcRom) {
char *savefile;
std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
if (savedir != NULL) {
savefile = new char[5 + std::strlen(savedir) + std::strlen(savename)];
std::sprintf(savefile, "%s%s.rtc", savedir, savename);
} else {
savefile = new char[5 + std::strlen(savename)];
std::sprintf(savefile, "%s.rtc", savename);
}
std::ifstream load(savefile, std::ios::binary | std::ios::in);
if (load.is_open()) {
std::time_t basetime;
load.read(reinterpret_cast<char*>(&basetime), sizeof(basetime));
load.close();
if (file.is_open()) {
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);
}
delete []savefile;
}
}
void Memory::saveSavedata() {
const std::string &sbp = saveBasePath();
if (battery) {
char *savefile;
std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
if (savedir != NULL) {
savefile = new char[5 + std::strlen(savedir) + std::strlen(savename)];
std::sprintf(savefile, "%s%s.sav", savedir, savename);
} else {
savefile = new char[5 + std::strlen(savename)];
std::sprintf(savefile, "%s.sav", savename);
}
std::ofstream save(savefile, std::ios::binary | std::ios::out);
if (save.is_open()) {
save.write(reinterpret_cast<char*>(rambankdata), rambanks * 0x2000ul);
save.close();
} /*else cout << "Saving ramdata failed\n";*/
delete []savefile;
file.write(reinterpret_cast<const char*>(rambankdata), rambanks * 0x2000ul);
}
if (rtcRom) {
char *savefile;
std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
const unsigned long basetime = rtc.getBaseTime();
if (savedir != NULL) {
savefile = new char[5 + std::strlen(savedir) + std::strlen(savename)];
std::sprintf(savefile, "%s%s.rtc", savedir, savename);
} else {
savefile = new char[5 + std::strlen(savename)];
std::sprintf(savefile, "%s.rtc", savename);
}
std::ofstream save(savefile, std::ios::binary | std::ios::out);
if (save.is_open()) {
std::time_t basetime = rtc.getBaseTime();
save.write(reinterpret_cast<char*>(&basetime), sizeof(basetime));
save.close();
} /*else cout << "Saving rtcdata failed\n";*/
delete []savefile;
file.put(basetime >> 24 & 0xFF);
file.put(basetime >> 16 & 0xFF);
file.put(basetime >> 8 & 0xFF);
file.put(basetime & 0xFF);
}
}
void Memory::set_savedir(const char *dir) {
delete []savedir;
savedir = NULL;
static const std::string stripExtension(const std::string &str) {
const std::string::size_type lastDot = str.find_last_of('.');
const std::string::size_type lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
if (dir != NULL) {
savedir = new char[std::strlen(dir) + 2];
std::strcpy(savedir, dir);
if (savedir[std::strlen(dir) - 1] != '/') {
savedir[std::strlen(dir)] = '/';
savedir[std::strlen(dir) + 1] = '\0';
}
static const std::string stripDir(const std::string &str) {
const std::string::size_type lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
const std::string Memory::saveBasePath() const {
const std::string &extStrippedFilePath = stripExtension(romFilePath);
return saveDir.empty() ? extStrippedFilePath : saveDir + stripDir(extStrippedFilePath);
}
void Memory::set_savedir(const char *dir) {
saveDir = dir ? dir : "";
if (!saveDir.empty() && saveDir[saveDir.length() - 1] != '/') {
saveDir += '/';
}
}
@ -1901,8 +1860,5 @@ void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned lon
Memory::~Memory() {
saveSavedata();
delete []romfile;
delete []savedir;
delete []savename;
delete []memchunk;
}

View File

@ -27,6 +27,7 @@ class SaveState;
#include "interrupter.h"
#include "rtc.h"
#include <string>
namespace Gambatte {
class InputStateGetter;
@ -57,10 +58,6 @@ private:
unsigned char *vrambank;
unsigned char *rsrambankptr;
unsigned char *wsrambankptr;
char *romfile;
char *savedir;
char *savename;
Gambatte::InputStateGetter *getInput;
@ -90,6 +87,9 @@ private:
irqEvents next_irqEvent;
cartridgetype romtype;
std::string romFilePath;
std::string saveDir;
unsigned short rombanks;
unsigned short rombank;
unsigned short dmaSource;
@ -137,8 +137,6 @@ private:
void rescheduleIrq(unsigned long cycleCounter);
void rescheduleHdmaReschedule();
bool loadROM();
bool isDoubleSpeed() const { return doubleSpeed; }
public:
@ -150,6 +148,11 @@ public:
void loadState(const SaveState &state, unsigned long oldCc);
void loadSavedata();
void saveSavedata();
const std::string saveBasePath() const;
void setOsdElement(std::auto_ptr<OsdElement> osdElement) {
display.setOsdElement(osdElement);
}
void speedChange(unsigned long cycleCounter);
bool isCgb() const { return cgb; }

View File

@ -0,0 +1,50 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "int.h"
class OsdElement {
unsigned x_;
unsigned y_;
unsigned w_;
unsigned h_;
protected:
OsdElement(unsigned x = 0, unsigned y = 0, unsigned w = 0, unsigned h = 0) {
setPos(x, y);
setSize(w, h);
}
void setPos(unsigned x, unsigned y) {
x_ = x;
y_ = y;
}
void setSize(unsigned w, unsigned h) {
w_ = w;
h_ = h;
}
public:
unsigned x() const { return x_; }
unsigned y() const { return y_; }
unsigned w() const { return w_; }
unsigned h() const { return h_; }
virtual const Gambatte::uint_least32_t* update() = 0;
};

View File

@ -19,7 +19,7 @@
#ifndef SAVESTATE_H
#define SAVESTATE_H
#include <ctime>
#include "int.h"
struct SaveState {
template<typename T>
@ -76,6 +76,7 @@ struct SaveState {
} mem;
struct PPU {
Ptr<Gambatte::uint_least32_t> drawBuffer;
Ptr<unsigned char> bgpData;
Ptr<unsigned char> objpData;
//SpriteMapper::OamReader
@ -168,8 +169,8 @@ struct SaveState {
} spu;
struct RTC {
std::time_t baseTime;
std::time_t haltTime;
unsigned long baseTime;
unsigned long haltTime;
unsigned char index;
unsigned char dataDh;
unsigned char dataDl;

View File

@ -95,7 +95,7 @@ void Channel1::SweepUnit::saveState(SaveState &state) const {
}
void Channel1::SweepUnit::loadState(const SaveState &state) {
counter = state.spu.ch1.sweep.counter;
counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow = state.spu.ch1.sweep.shadow;
nr0 = state.spu.ch1.sweep.nr0;
negging = state.spu.ch1.sweep.negging;
@ -202,9 +202,9 @@ void Channel1::saveState(SaveState &state) {
void Channel1::loadState(const SaveState &state) {
sweepUnit.loadState(state);
dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4);
envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112]);
lengthCounter.loadState(state.spu.ch1.lcounter);
dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter);
envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch1.nr4;

View File

@ -107,9 +107,9 @@ void Channel2::saveState(SaveState &state) {
}
void Channel2::loadState(const SaveState &state) {
dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4);
envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117]);
lengthCounter.loadState(state.spu.ch2.lcounter);
dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter);
envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch2.nr4;

View File

@ -19,6 +19,7 @@
#include "channel3.h"
#include "../savestate.h"
#include <cstring>
#include <algorithm>
static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
return 0x800 - (nr4 << 8 & 0x700 | nr3);
@ -109,14 +110,14 @@ void Channel3::saveState(SaveState &state) const {
}
void Channel3::loadState(const SaveState &state) {
lengthCounter.loadState(state.spu.ch3.lcounter);
lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
waveCounter = state.spu.ch3.waveCounter;
waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
lastReadTime = state.spu.ch3.lastReadTime;
nr3 = state.spu.ch3.nr3;
nr4 = state.spu.ch3.nr4;
wavePos = state.spu.ch3.wavePos;
wavePos = state.spu.ch3.wavePos & 0x1F;
sampleBuf = state.spu.ch3.sampleBuf;
master = state.spu.ch3.master;

View File

@ -18,6 +18,7 @@
***************************************************************************/
#include "channel4.h"
#include "../savestate.h"
#include <algorithm>
static unsigned long toPeriod(const unsigned nr3) {
unsigned s = (nr3 >> 4) + 3;
@ -154,7 +155,7 @@ void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
}
void Channel4::Lfsr::loadState(const SaveState &state) {
counter = backupCounter = state.spu.ch4.lfsr.counter;
counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter);
reg = state.spu.ch4.lfsr.reg;
master = state.spu.ch4.master;
nr3 = state.mem.ioamhram.get()[0x122];
@ -246,8 +247,8 @@ void Channel4::saveState(SaveState &state) {
void Channel4::loadState(const SaveState &state) {
lfsr.loadState(state);
envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121]);
lengthCounter.loadState(state.spu.ch4.lcounter);
envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch4.nr4;

View File

@ -17,6 +17,7 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "duty_unit.h"
#include <algorithm>
static inline bool toOutState(const unsigned duty, const unsigned pos) {
static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
@ -116,9 +117,9 @@ void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
dstate.pos = pos;
}
void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4) {
nextPosUpdate = dstate.nextPosUpdate;
pos = dstate.pos;
void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
pos = dstate.pos & 7;
setDuty(nr1);
period = toPeriod(nr4 << 8 & 0x700 | dstate.nr3);
enableEvents = true;

View File

@ -44,7 +44,7 @@ public:
void nr4Change(unsigned newNr4, unsigned long cc);
void reset();
void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4);
void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
void resetCounters(unsigned long oldCc);
void killCounter();
void reviveCounter(unsigned long cc);

View File

@ -17,6 +17,7 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "envelope_unit.h"
#include <algorithm>
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
@ -93,8 +94,8 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
estate.volume = volume;
}
void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2) {
counter = estate.counter;
void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
counter = std::max(estate.counter, cc);
volume = estate.volume;
this->nr2 = nr2;
}

View File

@ -44,7 +44,7 @@ public:
bool nr4Init(unsigned long cycleCounter);
void reset();
void saveState(SaveState::SPU::Env &estate) const;
void loadState(const SaveState::SPU::Env &estate, unsigned nr2);
void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
};
#endif

View File

@ -18,6 +18,7 @@
***************************************************************************/
#include "length_counter.h"
#include "master_disabler.h"
#include <algorithm>
LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
disableMaster(disabler),
@ -80,7 +81,7 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
lstate.lengthCounter = lengthCounter;
}
void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate) {
counter = lstate.counter;
void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
counter = std::max(lstate.counter, cc);
lengthCounter = lstate.lengthCounter;
}

View File

@ -33,12 +33,12 @@ class LengthCounter : public SoundUnit {
public:
LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
void event();
void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cycleCounter);
void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cycleCounter);
void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
// void reset();
void init(bool cgb);
void saveState(SaveState::SPU::LCounter &lstate) const;
void loadState(const SaveState::SPU::LCounter &lstate);
void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
};
#endif

View File

@ -24,6 +24,7 @@ class MasterDisabler {
public:
MasterDisabler(bool &m) : master(m) {}
virtual ~MasterDisabler() {}
virtual void operator()() { master = false; }
};

View File

@ -46,6 +46,12 @@ static inline bool operator<(const Saver &l, const Saver &r) {
return std::strcmp(l.label, r.label) < 0;
}
static void put24(std::ofstream &file, const unsigned long data) {
file.put(data >> 16 & 0xFF);
file.put(data >> 8 & 0xFF);
file.put(data & 0xFF);
}
static void put32(std::ofstream &file, const unsigned long data) {
file.put(data >> 24 & 0xFF);
file.put(data >> 16 & 0xFF);
@ -80,39 +86,23 @@ static inline void write(std::ofstream &file, const bool data) {
}
static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) {
file.put(sz >> 16 & 0xFF);
file.put(sz >> 8 & 0xFF);
file.put(sz & 0xFF);
put24(file, sz);
file.write(reinterpret_cast<const char*>(data), sz);
}
static void write(std::ofstream &file, const bool *data, const unsigned long sz) {
file.put(sz >> 16 & 0xFF);
file.put(sz >> 8 & 0xFF);
file.put(sz & 0xFF);
put24(file, sz);
for (unsigned long i = 0; i < sz; ++i)
file.put(data[i]);
}
static void writeTime(std::ofstream &file, const std::time_t data) {
static const char inf[] = { 0x00, 0x00, 0x08 };
file.write(inf, sizeof(inf));
if (sizeof(std::time_t) < sizeof(unsigned long)) {
const unsigned long uldata = data;
put32(file, (uldata >> 31) >> 1);
put32(file, uldata);
} else {
put32(file, (data >> 31) >> 1);
put32(file, data);
}
}
static unsigned long get24(std::ifstream &file) {
return static_cast<unsigned long>(file.get() & 0xFF) << 16 | (file.get() & 0xFF) << 8 | file.get() & 0xFF;
unsigned long tmp = file.get() & 0xFF;
tmp = tmp << 8 | file.get() & 0xFF;
return tmp << 8 | file.get() & 0xFF;
}
static unsigned long read(std::ifstream &file) {
@ -178,23 +168,6 @@ static void read(std::ifstream &file, bool *data, unsigned long sz) {
file.ignore(size - sz);
}
static void readTime(std::ifstream &file, std::time_t &data) {
unsigned long size = get24(file);
if (size > 8) {
file.ignore(size - 8);
size = 8;
}
std::time_t out = 0;
while (size-- && out >= 0) {
out = out << 8 | file.get() & 0xFF;
}
data = out;
}
class SaverList {
public:
typedef std::vector<Saver> list_t;
@ -231,16 +204,6 @@ SaverList::SaverList() {
Saver saver = { label, Func::save, Func::load, sizeof label }; \
list.push_back(saver); \
} while (0)
#define ADDTIME(arg) do { \
struct Func { \
static void save(std::ofstream &file, const SaveState &state) { writeTime(file, state.arg); } \
static void load(std::ifstream &file, SaveState &state) { readTime(file, state.arg); } \
}; \
\
Saver saver = { label, Func::save, Func::load, sizeof label }; \
list.push_back(saver); \
} while (0)
{ static const char label[] = { c,c, NUL }; ADD(cpu.cycleCounter); }
{ static const char label[] = { p,c, NUL }; ADD(cpu.PC); }
@ -334,8 +297,8 @@ SaverList::SaverList() {
{ static const char label[] = { l,e,n,NO4,v,a,l, NUL }; ADD(spu.ch4.lcounter.lengthCounter); }
{ static const char label[] = { n,r,NO4,NO4, NUL }; ADD(spu.ch4.nr4); }
{ static const char label[] = { c,NO4,m,a,s,t,r, NUL }; ADD(spu.ch4.master); }
{ static const char label[] = { r,t,c,b,a,s,e, NUL }; ADDTIME(rtc.baseTime); }
{ static const char label[] = { r,t,c,h,a,l,t, NUL }; ADDTIME(rtc.haltTime); }
{ static const char label[] = { r,t,c,b,a,s,e, NUL }; ADD(rtc.baseTime); }
{ static const char label[] = { r,t,c,h,a,l,t, NUL }; ADD(rtc.haltTime); }
{ static const char label[] = { r,t,c,i,n,d,x, NUL }; ADD(rtc.index); }
{ static const char label[] = { r,t,c,d,h, NUL }; ADD(rtc.dataDh); }
{ static const char label[] = { r,t,c,d,l, NUL }; ADD(rtc.dataDl); }
@ -359,6 +322,34 @@ SaverList::SaverList() {
}
}
static void writeSnapShot(std::ofstream &file, const Gambatte::uint_least32_t *pixels, const unsigned pitch) {
put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(Gambatte::uint_least32_t) : 0);
if (pixels) {
Gambatte::uint_least32_t buf[StateSaver::SS_WIDTH];
for (unsigned h = StateSaver::SS_HEIGHT; h--;) {
for (unsigned x = 0; x < StateSaver::SS_WIDTH; ++x) {
unsigned long rb = 0;
unsigned long g = 0;
static const unsigned w[StateSaver::SS_DIV] = { 1, 3, 3, 1 };
for (unsigned y = 0; y < StateSaver::SS_DIV; ++y)
for (unsigned xx = 0; xx < StateSaver::SS_DIV; ++xx) {
rb += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0xFF00FF) * w[y] * w[xx];
g += (pixels[x * StateSaver::SS_DIV + y * pitch + xx] & 0x00FF00) * w[y] * w[xx];
}
buf[x] = rb >> StateSaver::SS_SHIFT * 2 + 2 & 0xFF00FF | g >> StateSaver::SS_SHIFT * 2 + 2 & 0x00FF00;
}
file.write(reinterpret_cast<const char*>(buf), sizeof(buf));
pixels += pitch * StateSaver::SS_DIV;
}
}
}
static SaverList list;
void StateSaver::saveState(const SaveState &state, const char *filename) {
@ -369,19 +360,22 @@ void StateSaver::saveState(const SaveState &state, const char *filename) {
{ static const char ver[] = { 0, 0 }; file.write(ver, sizeof(ver)); }
writeSnapShot(file, state.ppu.drawBuffer.get(), state.ppu.drawBuffer.getSz() / 144);
for (SaverList::const_iterator it = list.begin(); it != list.end(); ++it) {
file.write(it->label, it->labelsize);
(*it->save)(file, state);
}
}
void StateSaver::loadState(SaveState &state, const char *filename) {
bool StateSaver::loadState(SaveState &state, const char *filename) {
std::ifstream file(filename, std::ios_base::binary);
if (file.fail() || file.get() != 0)
return;
return false;
file.ignore();
file.ignore(get24(file));
Array<char> labelbuf(list.maxLabelsize());
const Saver labelbufSaver = { label: labelbuf, save: 0, load: 0, labelsize: list.maxLabelsize() };
@ -405,4 +399,9 @@ void StateSaver::loadState(SaveState &state, const char *filename) {
(*it->load)(file, state);
}
state.cpu.cycleCounter &= 0x7FFFFFFF;
state.spu.cycleCounter &= 0x7FFFFFFF;
return true;
}

View File

@ -25,8 +25,13 @@ class StateSaver {
StateSaver();
public:
enum { SS_SHIFT = 2 };
enum { SS_DIV = 1 << 2 };
enum { SS_WIDTH = 160 >> SS_SHIFT };
enum { SS_HEIGHT = 144 >> SS_SHIFT };
static void saveState(const SaveState &state, const char *filename);
static void loadState(SaveState &state, const char *filename);
static bool loadState(SaveState &state, const char *filename);
};
#endif

View File

@ -192,6 +192,7 @@ void LCD::setDoubleSpeed(const bool ds) {
}
void LCD::setStatePtrs(SaveState &state) {
state.ppu.drawBuffer.set(static_cast<Gambatte::uint_least32_t*>(dbuffer), dpitch * 144);
state.ppu.bgpData.set(bgpData, sizeof bgpData);
state.ppu.objpData.set(objpData, sizeof objpData);
spriteMapper.setStatePtrs(state);
@ -221,9 +222,9 @@ void LCD::loadState(const SaveState &state, const unsigned char *oamram) {
setDoubleSpeed(cgb & state.mem.ioamhram.get()[0x14D] >> 7);
lastUpdate = state.cpu.cycleCounter;
videoCycles = state.ppu.videoCycles;
videoCycles = std::min(state.ppu.videoCycles, 70223ul);
enableDisplayM0Time = state.ppu.enableDisplayM0Time;
winYPos = state.ppu.winYPos;
winYPos = state.ppu.winYPos > 143 ? 0xFF : state.ppu.winYPos;
drawStartCycle = state.ppu.drawStartCycle;
scReadOffset = state.ppu.scReadOffset;
enabled = state.ppu.lcdc >> 7 & 1;
@ -277,7 +278,7 @@ void LCD::setVideoBlitter(Gambatte::VideoBlitter *vb) {
vBlitter = vb;
if (vBlitter) {
vBlitter->setBufferDimensions(filter ? filter->info().outWidth : 160, filter ? filter->info().outHeight : 144);
vBlitter->setBufferDimensions(videoWidth(), videoHeight());
pb = vBlitter->inBuffer();
}
@ -292,8 +293,8 @@ void LCD::videoBufferChange() {
}
void LCD::setVideoFilter(const unsigned n) {
const unsigned oldw = filter ? filter->info().outWidth : 160;
const unsigned oldh = filter ? filter->info().outHeight : 144;
const unsigned oldw = videoWidth();
const unsigned oldh = videoHeight();
if (filter)
filter->outit();
@ -304,8 +305,8 @@ void LCD::setVideoFilter(const unsigned n) {
filter->init();
}
if (vBlitter && (oldw != (filter ? filter->info().outWidth : 160) || oldh != (filter ? filter->info().outHeight : 144))) {
vBlitter->setBufferDimensions(filter ? filter->info().outWidth : 160, filter ? filter->info().outHeight : 144);
if (vBlitter && (oldw != videoWidth() || oldh != videoHeight())) {
vBlitter->setBufferDimensions(videoWidth(), videoHeight());
pb = vBlitter->inBuffer();
}
@ -336,19 +337,41 @@ void LCD::updateScreen(const unsigned long cycleCounter) {
update(cycleCounter);
if (pb.pixels) {
if (dbuffer && osdElement.get()) {
const Gambatte::uint_least32_t *s = osdElement->update();
if (s) {
Gambatte::uint_least32_t *d = static_cast<Gambatte::uint_least32_t*>(dbuffer) + osdElement->y() * dpitch + osdElement->x();
for (unsigned h = osdElement->h(); h--;) {
for (unsigned w = osdElement->w(); w--;) {
if (*s != 0xFFFFFFFF)
*d = *s * 7 + *d - ((*s & 0x070707) * 7 + (*d & 0x070707) & 0x070707) >> 3;
++d;
++s;
}
// std::memcpy(d, s, osdElement->w() * sizeof(Gambatte::uint_least32_t));
// s += osdElement->w();
d += dpitch - osdElement->w();
}
} else
osdElement.reset();
}
if (filter) {
filter->filter(static_cast<Gambatte::uint_least32_t*>(tmpbuf ? tmpbuf : pb.pixels), pb.pitch);
if (tmpbuf) {
switch (pb.format) {
case Gambatte::PixelBuffer::RGB16:
rgb32ToRgb16(tmpbuf, static_cast<Gambatte::uint_least16_t*>(pb.pixels), filter->info().outWidth, filter->info().outHeight, pb.pitch);
break;
case Gambatte::PixelBuffer::UYVY:
rgb32ToUyvy(tmpbuf, static_cast<Gambatte::uint_least32_t*>(pb.pixels), filter->info().outWidth, filter->info().outHeight, pb.pitch);
break;
default: break;
}
}
if (tmpbuf) {
switch (pb.format) {
case Gambatte::PixelBuffer::RGB16:
rgb32ToRgb16(tmpbuf, static_cast<Gambatte::uint_least16_t*>(pb.pixels), videoWidth(), videoHeight(), pb.pitch);
break;
case Gambatte::PixelBuffer::UYVY:
rgb32ToUyvy(tmpbuf, static_cast<Gambatte::uint_least32_t*>(pb.pixels), videoWidth(), videoHeight(), pb.pitch);
break;
default: break;
}
}
@ -385,10 +408,7 @@ void LCD::enableChange(const unsigned long cycleCounter) {
if (!enabled && dbuffer) {
const unsigned long color = cgb ? (*gbcToFormat)(0xFFFF) : dmgColors[0];
if (!filter && pb.format == Gambatte::PixelBuffer::RGB16)
clear(static_cast<Gambatte::uint_least16_t*>(dbuffer), color, dpitch);
else
clear(static_cast<Gambatte::uint_least32_t*>(dbuffer), color, dpitch);
clear(static_cast<Gambatte::uint_least32_t*>(dbuffer), color, dpitch);
// updateScreen(cycleCounter);
}
@ -854,42 +874,25 @@ void LCD::update(const unsigned long cycleCounter) {
}
void LCD::setDBuffer() {
tmpbuf.reset(pb.format == Gambatte::PixelBuffer::RGB32 ? 0 : videoWidth() * videoHeight());
if (cgb)
draw = &LCD::cgb_draw<Gambatte::uint_least32_t>;
else
draw = &LCD::dmg_draw<Gambatte::uint_least32_t>;
gbcToFormat = &gbcToRgb32;
dmgColors = dmgColorsRgb32;
if (filter) {
dbuffer = filter->inBuffer();
dpitch = filter->inPitch();
} else {
} else if (pb.format == Gambatte::PixelBuffer::RGB32) {
dbuffer = pb.pixels;
dpitch = pb.pitch;
}
tmpbuf.reset(filter && pb.format != Gambatte::PixelBuffer::RGB32 ? filter->info().outWidth * filter->info().outHeight : 0);
/*if (filter || pb.bpp == 16)
draw = memory.cgb ? &LCD::cgb_draw<uint16_t> : &LCD::dmg_draw<uint16_t>;
else
draw = memory.cgb ? &LCD::cgb_draw<uint32_t> : &LCD::dmg_draw<uint32_t>;*/
if (!filter && pb.format == Gambatte::PixelBuffer::RGB16) {
if (cgb)
draw = &LCD::cgb_draw<Gambatte::uint_least16_t>;
else
draw = &LCD::dmg_draw<Gambatte::uint_least16_t>;
gbcToFormat = &gbcToRgb16;
dmgColors = dmgColorsRgb16;
} else {
if (cgb)
draw = &LCD::cgb_draw<Gambatte::uint_least32_t>;
else
draw = &LCD::dmg_draw<Gambatte::uint_least32_t>;
if (filter || pb.format == Gambatte::PixelBuffer::RGB32) {
gbcToFormat = &gbcToRgb32;
dmgColors = dmgColorsRgb32;
} else {
gbcToFormat = &gbcToUyvy;
dmgColors = dmgColorsUyvy;
}
dbuffer = tmpbuf;
dpitch = 160;
}
if (dbuffer == NULL)
@ -1009,6 +1012,37 @@ static const unsigned char xflipt[0x100] = {
#undef FLIP_ROW
#undef FLIP
/*
#define PREP(u8) (u8)
#define EXPAND(u8) ( PREP(u8) << 7 & 0x4000 | PREP(u8) << 6 & 0x1000 | PREP(u8) << 5 & 0x0400 | PREP(u8) << 4 & 0x0100 | \
PREP(u8) << 3 & 0x0040 | PREP(u8) << 2 & 0x0010 | PREP(u8) << 1 & 0x0004 | PREP(u8) & 0x0001 )
#define EXPAND_ROW(n) EXPAND((n)|0x0), EXPAND((n)|0x1), EXPAND((n)|0x2), EXPAND((n)|0x3), \
EXPAND((n)|0x4), EXPAND((n)|0x5), EXPAND((n)|0x6), EXPAND((n)|0x7), \
EXPAND((n)|0x8), EXPAND((n)|0x9), EXPAND((n)|0xA), EXPAND((n)|0xB), \
EXPAND((n)|0xC), EXPAND((n)|0xD), EXPAND((n)|0xE), EXPAND((n)|0xF)
#define EXPAND_TABLE EXPAND_ROW(0x00), EXPAND_ROW(0x10), EXPAND_ROW(0x20), EXPAND_ROW(0x30), \
EXPAND_ROW(0x40), EXPAND_ROW(0x50), EXPAND_ROW(0x60), EXPAND_ROW(0x70), \
EXPAND_ROW(0x80), EXPAND_ROW(0x90), EXPAND_ROW(0xA0), EXPAND_ROW(0xB0), \
EXPAND_ROW(0xC0), EXPAND_ROW(0xD0), EXPAND_ROW(0xE0), EXPAND_ROW(0xF0)
static const unsigned short expand_lut[0x200] = {
EXPAND_TABLE,
#undef PREP
#define PREP(u8) ( (u8) << 7 & 0x80 | (u8) << 5 & 0x40 | (u8) << 3 & 0x20 | (u8) << 1 & 0x10 | \
(u8) >> 1 & 0x08 | (u8) >> 3 & 0x04 | (u8) >> 5 & 0x02 | (u8) >> 7 & 0x01 )
EXPAND_TABLE
};
#undef EXPAND_TABLE
#undef EXPAND_ROW
#undef EXPAND
#undef PREP
*/
//shoud work for the window too, if -wx is passed as scx.
//tilemap and tiledata must point to the areas in the first vram bank

View File

@ -28,11 +28,13 @@ class Filter;
class SaveState;
#include <vector>
#include <memory>
#include "event_queue.h"
#include "videoblitter.h"
#include "array.h"
#include "int.h"
#include "colorconversion.h"
#include "osd_element.h"
#include "video/video_event_comparer.h"
#include "video/ly_counter.h"
@ -104,6 +106,7 @@ class LCD {
Gambatte::PixelBuffer pb;
Array<Gambatte::uint_least32_t> tmpbuf;
Rgb32ToUyvy rgb32ToUyvy;
std::auto_ptr<OsdElement> osdElement;
std::vector<Filter*> filters;
@ -167,6 +170,10 @@ public:
unsigned videoHeight() const;
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
void setOsdElement(std::auto_ptr<OsdElement> osdElement) {
this->osdElement = osdElement;
}
void wdTileMapSelectChange(bool newValue, unsigned long cycleCounter);
void bgTileMapSelectChange(bool newValue, unsigned long cycleCounter);
void bgTileDataSelectChange(bool newValue, unsigned long cycleCounter);