From 24eb71f288305085f52961b2c1bcb9273aebed49 Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Thu, 12 Jul 2012 20:49:57 +0300
Subject: [PATCH] Changes to make libgambatte rerecording-friendly

---
 Makefile                                     |   10 +
 libgambatte/Makefile                         |   18 ++
 libgambatte/include/gambatte.h               |   68 ++++++-
 libgambatte/src/bitmap_font.cpp              |   10 +-
 libgambatte/src/bitmap_font.h                |    6 +-
 libgambatte/src/cpu.cpp                      |   45 ++++-
 libgambatte/src/cpu.h                        |   31 +++-
 libgambatte/src/file/file.cpp                |   34 ++++
 libgambatte/src/file/file.h                  |   10 +
 libgambatte/src/gambatte.cpp                 |  129 +++++++++++--
 libgambatte/src/initstate.cpp                |    8 +-
 libgambatte/src/initstate.h                  |    8 +-
 libgambatte/src/interrupter.cpp              |   17 ++-
 libgambatte/src/interrupter.h                |   18 ++-
 libgambatte/src/interruptrequester.cpp       |   23 ++-
 libgambatte/src/interruptrequester.h         |   27 ++-
 libgambatte/src/loadsave.cpp                 |  266 ++++++++++++++++++++++++++
 libgambatte/src/loadsave.h                   |  160 ++++++++++++++++
 libgambatte/src/mem/cartridge.cpp            |  155 +++++++++++++--
 libgambatte/src/mem/cartridge.h              |   36 +++-
 libgambatte/src/mem/memptrs.cpp              |   32 +++-
 libgambatte/src/mem/memptrs.h                |   11 +-
 libgambatte/src/mem/rtc.cpp                  |   57 +++++-
 libgambatte/src/mem/rtc.h                    |   11 +-
 libgambatte/src/memory.cpp                   |  110 +++++++----
 libgambatte/src/memory.h                     |   80 +++++---
 libgambatte/src/minkeeper.h                  |   30 ++-
 libgambatte/src/savestate.h                  |   54 +++---
 libgambatte/src/sound.cpp                    |   36 +++-
 libgambatte/src/sound.h                      |   17 ++-
 libgambatte/src/sound/channel1.cpp           |   44 ++++-
 libgambatte/src/sound/channel1.h             |   25 ++-
 libgambatte/src/sound/channel2.cpp           |   41 +++-
 libgambatte/src/sound/channel2.h             |   17 ++-
 libgambatte/src/sound/channel3.cpp           |   41 +++-
 libgambatte/src/sound/channel3.h             |   27 ++-
 libgambatte/src/sound/channel4.cpp           |   59 ++++--
 libgambatte/src/sound/channel4.h             |   40 +++--
 libgambatte/src/sound/duty_unit.cpp          |   34 +++-
 libgambatte/src/sound/duty_unit.h            |   29 ++-
 libgambatte/src/sound/envelope_unit.cpp      |   19 ++-
 libgambatte/src/sound/envelope_unit.h        |   12 +-
 libgambatte/src/sound/length_counter.cpp     |   19 ++-
 libgambatte/src/sound/length_counter.h       |   13 +-
 libgambatte/src/sound/sound_unit.h           |   15 ++-
 libgambatte/src/sound/static_output_tester.h |    8 +-
 libgambatte/src/state_osd_elements.cpp       |    8 +-
 libgambatte/src/statesaver.cpp               |  111 +++++++----
 libgambatte/src/statesaver.h                 |    7 +
 libgambatte/src/tima.cpp                     |   35 +++-
 libgambatte/src/tima.h                       |   28 ++-
 libgambatte/src/video.cpp                    |  120 +++++++-----
 libgambatte/src/video.h                      |  118 +++++++-----
 libgambatte/src/video/ly_counter.cpp         |   14 +-
 libgambatte/src/video/ly_counter.h           |   26 ++-
 libgambatte/src/video/lyc_irq.cpp            |   14 +-
 libgambatte/src/video/lyc_irq.h              |   27 ++-
 libgambatte/src/video/next_m0_time.h         |   10 +
 libgambatte/src/video/ppu.cpp                |  123 +++++++++---
 libgambatte/src/video/ppu.h                  |   84 ++++++---
 libgambatte/src/video/sprite_mapper.cpp      |   17 +-
 libgambatte/src/video/sprite_mapper.h        |   53 ++++--
 62 files changed, 2152 insertions(+), 603 deletions(-)
 create mode 100644 Makefile
 create mode 100644 libgambatte/Makefile
 create mode 100644 libgambatte/src/loadsave.cpp
 create mode 100644 libgambatte/src/loadsave.h

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2714a5b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+all: libgambatte/__all_files__
+
+libgambatte/__all_files__: forcelook
+	$(MAKE) -C libgambatte
+
+clean:  forcelook
+	$(MAKE) -C libgambatte clean
+
+forcelook:
+	@true
diff --git a/libgambatte/Makefile b/libgambatte/Makefile
new file mode 100644
index 0000000..7c3724e
--- /dev/null
+++ b/libgambatte/Makefile
@@ -0,0 +1,18 @@
+all: libgambatte.$(ARCHIVE_SUFFIX)
+
+REALAR=$(CROSS_PREFIX)ar
+
+OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard src/*.cpp src/video/*.cpp src/mem/*.cpp src/sound/*.cpp src/file/file.cpp))
+
+%.o: %.cpp
+	$(gambatte_compiler) $(CFLAGS) -Wno-deprecated-declarations -DHAVE_CSTDINT -I../common -Iinclude -Isrc -c -o $@ $<
+
+libgambatte.$(ARCHIVE_SUFFIX): $(OBJECTS)
+	$(REALAR) crvs $@ $^
+	$(REALRANLIB) $@
+
+clean: forcelook
+	rm -f $(OBJECTS) libgambatte.$(ARCHIVE_SUFFIX)
+
+forcelook:
+	@true
\ No newline at end of file
diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index b13e4be..1db8086 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -22,6 +22,11 @@
 #include "inputgetter.h"
 #include "gbint.h"
 #include <string>
+#include <vector>
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
@@ -44,7 +49,15 @@ public:
 	  * @return 0 on success, negative value on failure.
 	  */
 	int load(const std::string &romfile, unsigned flags = 0);
-	
+	/** Load ROM image.
+	  *
+	  * @param image    Raw ROM image data.
+	  * @param isize    Size of raw ROM image data.
+	  * @param flags    ORed combination of LoadFlags.
+	  * @return 0 on success, negative value on failure.
+	  */
+	int load(const unsigned char* image, size_t isize, unsigned flags = 0);
+
 	/** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
 	  * or until a video frame has been drawn.
 	  *
@@ -66,7 +79,7 @@ public:
 	  * @param samples in: number of stereo samples to produce, out: actual number of samples produced
 	  * @return sample number at which the video frame was produced. -1 means no frame was produced.
 	  */
-	long runFor(gambatte::uint_least32_t *videoBuf, int pitch,
+	signed runFor(gambatte::uint_least32_t *videoBuf, int pitch,
 			gambatte::uint_least32_t *soundBuf, unsigned &samples);
 	
 	/** Reset to initial state.
@@ -113,7 +126,18 @@ public:
 	/** Loads emulator state from the file given by 'filepath'.
 	  */
 	void loadState(const std::string &filepath);
-	
+
+	/** Save savestate to given buffer.
+	  */
+	void saveState(std::vector<char>& data, const std::vector<char>& cmpdata);
+	/** Save savestate to given buffer.
+	  */
+	void saveState(std::vector<char>& data);
+	/** Load savestate from given buffer.
+	  */
+	void loadState(const std::vector<char>& data);
+
+
 	/** Selects which state slot to save state to or load state from.
 	  * There are 10 such slots, numbered from 0 to 9 (periodically extended for all n).
 	  */
@@ -134,10 +158,46 @@ public:
 	  * @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F]
 	  */
 	void setGameShark(const std::string &codes);
-	
+
+	/** Set RTC base time.
+	  */
+	void setRtcBase(time_t time);
+
+	/** Get RTC base time.
+	  */
+	time_t getRtcBase();
+
+	/** Get pointer and size to Work RAM.
+	  * @return The pointer and size of Work RAM.
+	 */
+	std::pair<unsigned char*, size_t> getWorkRam();
+
+	/** Get pointer and size to Save RAM.
+	  * @return The pointer and size of Save RAM.
+	 */
+	std::pair<unsigned char*, size_t> getSaveRam();
+
+	/** Get pointer and size to I/O RAM.
+	  * @return The pointer and size of I/O RAM.
+	 */
+	std::pair<unsigned char*, size_t> getIoRam();
+
+	/** Get pointer and size to Video RAM.
+	  * @return The pointer and size of Video RAM.
+	 */
+	std::pair<unsigned char*, size_t> getVideoRam();
+
+	/** Function to get wall time. */
+	void set_walltime_fn(time_t (*_walltime)());
+
+	/** Get version. */
+	static std::string version();
 private:
+	void preload_common();
+	void postload_common(const unsigned flags);
 	struct Priv;
 	Priv *const p_;
+	time_t (*walltime)();
 
 	void loadState(const std::string &filepath, bool osdMessage);
 	GB(const GB &);
diff --git a/libgambatte/src/bitmap_font.cpp b/libgambatte/src/bitmap_font.cpp
index 7ef835f..0e40bc4 100644
--- a/libgambatte/src/bitmap_font.cpp
+++ b/libgambatte/src/bitmap_font.cpp
@@ -68,6 +68,10 @@
   gnome dot org.
 */
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "bitmap_font.h"
 
 static const unsigned char n0_bits[] = { 0x68,
@@ -285,10 +289,10 @@ unsigned getWidth(const char *chars) {
 
 namespace {
 class Rgb32Fill {
-	const unsigned long color;
+	const uint_least32_t color;
 	
 public:
-	explicit Rgb32Fill(unsigned long color) : color(color) {}
+	explicit Rgb32Fill(uint_least32_t color) : color(color) {}
 	
 	void operator()(gambatte::uint_least32_t *dest, unsigned /*pitch*/) const {
 		*dest = color;
@@ -296,7 +300,7 @@ public:
 };
 }
 
-void print(gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) {
+void print(gambatte::uint_least32_t *dest, const unsigned pitch, const uint_least32_t color, const char *chars) {
 	print(dest, pitch, Rgb32Fill(color), chars);
 }
 
diff --git a/libgambatte/src/bitmap_font.h b/libgambatte/src/bitmap_font.h
index 35b29fa..34e3f6b 100644
--- a/libgambatte/src/bitmap_font.h
+++ b/libgambatte/src/bitmap_font.h
@@ -19,6 +19,10 @@
 #ifndef BITMAP_FONT_H
 #define BITMAP_FONT_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "gbint.h"
 
 namespace bitmapfont {
@@ -42,7 +46,7 @@ unsigned getWidth(const char *chars);
 template<class RandomAccessIterator, class Fill>
 void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars);
 
-void print(gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars);
+void print(gambatte::uint_least32_t *dest, unsigned pitch, uint_least32_t color, const char *chars);
 void utoa(unsigned u, char *a);
 
 // --- INTERFACE END ---
diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
index 2b19eeb..9c63ddb 100644
--- a/libgambatte/src/cpu.cpp
+++ b/libgambatte/src/cpu.cpp
@@ -20,10 +20,14 @@
 #include "memory.h"
 #include "savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
-CPU::CPU()
-: memory(Interrupter(SP, PC_)),
+CPU::CPU(time_t (**_getCurrentTime)())
+: memory(Interrupter(SP, PC_), _getCurrentTime),
   cycleCounter_(0),
   PC_(0x100),
   SP(0xFFFE),
@@ -42,10 +46,10 @@ CPU::CPU()
 {
 }
 
-long CPU::runFor(const unsigned long cycles) {
+signed CPU::runFor(const unsigned cycles) {
 	process(cycles/* << memory.isDoubleSpeed()*/);
 	
-	const long csb = memory.cyclesSinceBlit(cycleCounter_);
+	const signed csb = memory.cyclesSinceBlit(cycleCounter_);
 	
 	if (cycleCounter_ & 0x80000000)
 		cycleCounter_ = memory.resetCounters(cycleCounter_);
@@ -124,6 +128,27 @@ void CPU::loadState(const SaveState &state) {
 	skip = state.cpu.skip;
 }
 
+void CPU::loadOrSave(loadsave& state)
+{
+	memory.loadOrSave(state);
+	state(cycleCounter_);
+	state(PC_);
+	state(SP);
+	state(HF1);
+	state(HF2);
+	state(ZF);
+	state(CF);
+	state(A_);
+	state(B);
+	state(C);
+	state(D);
+	state(E);
+	state(H);
+	state(L);
+	state(skip);
+}
+
+
 #define BC() ( B << 8 | C )
 #define DE() ( D << 8 | E )
 #define HL() ( H << 8 | L )
@@ -503,18 +528,18 @@ void CPU::loadState(const SaveState &state) {
 	PC_MOD(ret_var_h << 8 | ret_var_l); \
 } while (0)
 
-void CPU::process(const unsigned long cycles) {
+void CPU::process(const unsigned cycles) {
 	memory.setEndtime(cycleCounter_, cycles);
 	
 	unsigned char A = A_;
-	unsigned long cycleCounter = cycleCounter_;
+	unsigned cycleCounter = cycleCounter_;
 	
 	while (memory.isActive()) {
 		unsigned short PC = PC_;
-		
+
 		if (memory.halted()) {
 			if (cycleCounter < memory.nextEventTime()) {
-				const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+				const unsigned cycles = memory.nextEventTime() - cycleCounter;
 				cycleCounter += cycles + (-cycles & 3);
 			}
 		} else while (cycleCounter < memory.nextEventTime()) {
@@ -612,7 +637,7 @@ void CPU::process(const unsigned long cycles) {
 				cycleCounter = memory.stop(cycleCounter);
 
 				if (cycleCounter < memory.nextEventTime()) {
-					const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+					const unsigned cycles = memory.nextEventTime() - cycleCounter;
 					cycleCounter += cycles + (-cycles & 3);
 				}
 				
@@ -1140,7 +1165,7 @@ void CPU::process(const unsigned long cycles) {
 					memory.halt();
 
 					if (cycleCounter < memory.nextEventTime()) {
-						const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+						const unsigned cycles = memory.nextEventTime() - cycleCounter;
 						cycleCounter += cycles + (-cycles & 3);
 					}
 				}
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index 238e639..98f25c6 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -19,14 +19,19 @@
 #ifndef CPU_H
 #define CPU_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "memory.h"
+#include "loadsave.h"
 
 namespace gambatte {
 
 class CPU {
 	Memory memory;
 	
-	unsigned long cycleCounter_;
+	unsigned cycleCounter_;
 
 	unsigned short PC_;
 	unsigned short SP;
@@ -37,20 +42,22 @@ class CPU {
 
 	bool skip;
 	
-	void process(unsigned long cycles);
+	void process(unsigned cycles);
 	
 public:
 	
-	CPU();
+	CPU(time_t (**_getCurrentTime)());
 // 	void halt();
 
 // 	unsigned interrupt(unsigned address, unsigned cycleCounter);
 	
-	long runFor(unsigned long cycles);
+	signed runFor(unsigned cycles);
 	void setStatePtrs(SaveState &state);
 	void saveState(SaveState &state);
 	void loadState(const SaveState &state);
-	
+
+	void loadOrSave(loadsave& state);
+
 	void loadSavedata() { memory.loadSavedata(); }
 	void saveSavedata() { memory.saveSavedata(); }
 	
@@ -77,7 +84,11 @@ public:
 	int load(const std::string &romfile, bool forceDmg, bool multicartCompat) {
 		return memory.loadROM(romfile, forceDmg, multicartCompat);
 	}
-	
+
+	int load(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat) {
+		return memory.loadROM(image, isize, forceDmg, multicartCompat);
+	}
+
 	bool loaded() const { return memory.loaded(); }
 	const char * romTitle() const { return memory.romTitle(); }
 	
@@ -92,6 +103,14 @@ public:
 	
 	void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
 	void setGameShark(const std::string &codes) { memory.setGameShark(codes); }
+
+	void setRtcBase(time_t time) { memory.setRtcBase(time); }
+	time_t getRtcBase() { return memory.getRtcBase(); }
+	std::pair<unsigned char*, size_t> getWorkRam() { return memory.getWorkRam(); }
+	std::pair<unsigned char*, size_t> getSaveRam() { return memory.getSaveRam(); }
+	std::pair<unsigned char*, size_t> getIoRam() { return memory.getIoRam(); }
+	std::pair<unsigned char*, size_t> getVideoRam() { return memory.getVideoRam(); };
+
 };
 
 }
diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp
index fff66d2..5b5739e 100644
--- a/libgambatte/src/file/file.cpp
+++ b/libgambatte/src/file/file.cpp
@@ -20,7 +20,41 @@ Free Software Foundation, Inc.,
 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ***************************************************************************/
 #include "stdfile.h"
+#include <cstring>
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 std::auto_ptr<gambatte::File> gambatte::newFileInstance(const std::string &filepath) {
 	return std::auto_ptr<File>(new StdFile(filepath.c_str()));
 }
+
+namespace
+{
+	struct MemoryFile : public gambatte::File
+	{
+		MemoryFile(const unsigned char* image, size_t isize) : buf(image), bufsize(isize),
+			ptr(0), xfail(false) {}
+		~MemoryFile() {}
+		void rewind() { ptr = 0; xfail = false; }
+		std::size_t size() const { return bufsize; }
+		void read(char *buffer, std::size_t amount) {
+			if(amount > bufsize - ptr) {
+				memcpy(buffer, buf, bufsize - ptr);
+				xfail = true;
+			} else
+				memcpy(buffer, buf, amount);
+		}
+		bool fail() const { return xfail; }
+	private:
+		const unsigned char* buf;
+		size_t bufsize;
+		size_t ptr;
+		bool xfail;
+	};
+}
+
+std::auto_ptr<gambatte::File> gambatte::newFileInstance(const unsigned char* image, size_t isize) {
+	return std::auto_ptr<File>(new MemoryFile(image, isize));
+}
diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h
index 23b23c0..1a95543 100644
--- a/libgambatte/src/file/file.h
+++ b/libgambatte/src/file/file.h
@@ -22,9 +22,18 @@ Free Software Foundation, Inc.,
 #ifndef GAMBATTE_FILE_H
 #define GAMBATTE_FILE_H
 
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include <memory>
 #include <string>
 
+//
+// Modified 2012-07-10 by H. Ilari Liusvaara
+//	- New API methods.
+
 namespace gambatte {
 
 class File {
@@ -37,6 +46,7 @@ public:
 };
 
 std::auto_ptr<File> newFileInstance(const std::string &filepath);
+std::auto_ptr<File> newFileInstance(const unsigned char* image, size_t isize);
 
 }
 
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index d33b3f2..589a608 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -25,6 +25,10 @@
 #include <sstream>
 #include <cstring>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 static const std::string itos(const int i) {
 	std::stringstream ss;
 	ss << i;
@@ -35,16 +39,24 @@ static const std::string statePath(const std::string &basePath, const int stateN
 	return basePath + "_" + itos(stateNo) + ".gqs";
 }
 
+namespace
+{
+	time_t default_walltime()
+	{
+		return time(0);
+	}
+}
+
 namespace gambatte {
 struct GB::Priv {
 	CPU cpu;
 	int stateNo;
 	bool gbaCgbMode;
 	
-	Priv() : stateNo(1), gbaCgbMode(false) {}
+	Priv(time_t (**_getCurrentTime)()) : stateNo(1), gbaCgbMode(false), cpu(_getCurrentTime) {}
 };
 	
-GB::GB() : p_(new Priv) {}
+GB::GB() : p_(new Priv(&walltime)), walltime(default_walltime) {}
 
 GB::~GB() {
 	if (p_->cpu.loaded())
@@ -53,7 +65,7 @@ GB::~GB() {
 	delete p_;
 }
 
-long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
+signed GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
 			gambatte::uint_least32_t *const soundBuf, unsigned &samples) {
 	if (!p_->cpu.loaded()) {
 		samples = 0;
@@ -62,10 +74,10 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
 	
 	p_->cpu.setVideoBuffer(videoBuf, pitch);
 	p_->cpu.setSoundBuffer(soundBuf);
-	const long cyclesSinceBlit = p_->cpu.runFor(samples * 2);
+	const signed cyclesSinceBlit = p_->cpu.runFor(samples * 2);
 	samples = p_->cpu.fillSoundBuffer();
 	
-	return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
+	return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<signed>(samples) - (cyclesSinceBlit >> 1);
 }
 
 void GB::reset() {
@@ -74,7 +86,7 @@ void GB::reset() {
 		
 		SaveState state;
 		p_->cpu.setStatePtrs(state);
-		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode);
+		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, walltime());
 		p_->cpu.loadState(state);
 		p_->cpu.loadSavedata();
 	}
@@ -88,23 +100,43 @@ void GB::setSaveDir(const std::string &sdir) {
 	p_->cpu.setSaveDir(sdir);
 }
 
-int GB::load(const std::string &romfile, const unsigned flags) {
+void GB::preload_common()
+{
 	if (p_->cpu.loaded())
 		p_->cpu.saveSavedata();
-	
+}
+
+void GB::postload_common(const unsigned flags)
+{
+	SaveState state;
+	p_->cpu.setStatePtrs(state);
+	setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, walltime());
+	p_->cpu.loadState(state);
+	p_->cpu.loadSavedata();
+
+	p_->stateNo = 1;
+	p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
+}
+
+int GB::load(const std::string &romfile, const unsigned flags) {
+	preload_common();
+
 	const int failed = p_->cpu.load(romfile, flags & FORCE_DMG, flags & MULTICART_COMPAT);
-	
-	if (!failed) {
-		SaveState state;
-		p_->cpu.setStatePtrs(state);
-		setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB);
-		p_->cpu.loadState(state);
-		p_->cpu.loadSavedata();
-		
-		p_->stateNo = 1;
-		p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
-	}
-	
+
+	if (!failed)
+		postload_common(flags);
+
+	return failed;
+}
+
+int GB::load(const unsigned char* image, size_t isize, unsigned flags) {
+	preload_common();
+
+	const int failed = p_->cpu.load(image, isize, flags & FORCE_DMG, flags & MULTICART_COMPAT);
+
+	if (!failed)
+		postload_common(flags);
+
 	return failed;
 }
 
@@ -160,6 +192,29 @@ void GB::loadState(const std::string &filepath) {
 	loadState(filepath, false);
 }
 
+void GB::saveState(std::vector<char>& data, const std::vector<char>& cmpdata) {
+	if (p_->cpu.loaded()) {
+		loadsave_save l(cmpdata);
+		p_->cpu.loadOrSave(l);
+		data = l.get();
+	}
+}
+
+void GB::saveState(std::vector<char>& data) {
+	if (p_->cpu.loaded()) {
+		loadsave_save l;
+		p_->cpu.loadOrSave(l);
+		data = l.get();
+	}
+}
+
+void GB::loadState(const std::vector<char>& data) {
+	if (p_->cpu.loaded()) {
+		loadsave_load l(data);
+		p_->cpu.loadOrSave(l);
+	}
+}
+
 void GB::selectState(int n) {
 	n -= (n / 10) * 10;
 	p_->stateNo = n < 0 ? n + 10 : n;
@@ -189,4 +244,38 @@ void GB::setGameShark(const std::string &codes) {
 	p_->cpu.setGameShark(codes);
 }
 
+void GB::setRtcBase(time_t time) {
+	p_->cpu.setRtcBase(time);
+}
+
+time_t GB::getRtcBase() {
+	return p_->cpu.getRtcBase();
+}
+
+std::pair<unsigned char*, size_t> GB::getWorkRam() {
+	return p_->cpu.getWorkRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getSaveRam() {
+	return p_->cpu.getSaveRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getIoRam() {
+	return p_->cpu.getIoRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getVideoRam() {
+	return p_->cpu.getVideoRam();
+}
+
+void GB::set_walltime_fn(time_t (*_walltime)())
+{
+	walltime = _walltime;
+}
+
+std::string GB::version()
+{
+	return "SVN320";
+}
+
 }
diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp
index 0d61e66..fdad344 100644
--- a/libgambatte/src/initstate.cpp
+++ b/libgambatte/src/initstate.cpp
@@ -24,6 +24,10 @@
 #include <cstring>
 #include <ctime>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace {
 
 static void setInitialCgbWram(unsigned char *const wram) {
@@ -1147,7 +1151,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
 
 } // anon namespace
 
-void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
+void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, time_t starttime) {
 	static const unsigned char cgbObjpDump[0x40] = {
 		0x00, 0x00, 0xF2, 0xAB, 
 		0x61, 0xC2, 0xD9, 0xBA, 
@@ -1308,7 +1312,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
 	state.spu.ch4.nr4 = 0;
 	state.spu.ch4.master = false;
 	
-	state.rtc.baseTime = std::time(0);
+	state.rtc.baseTime = starttime;
 	state.rtc.haltTime = state.rtc.baseTime;
 	state.rtc.dataDh = 0;
 	state.rtc.dataDl = 0;
diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h
index 0aba307..171b6d0 100644
--- a/libgambatte/src/initstate.h
+++ b/libgambatte/src/initstate.h
@@ -19,8 +19,14 @@
 #ifndef INITSTATE_H
 #define INITSTATE_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include <ctime>
+
 namespace gambatte {
-void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
+void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, time_t starttime);
 }
 
 #endif
diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp
index 66db0c7..230bdc2 100644
--- a/libgambatte/src/interrupter.cpp
+++ b/libgambatte/src/interrupter.cpp
@@ -19,6 +19,10 @@
 #include "interrupter.h"
 #include "memory.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
@@ -26,7 +30,7 @@ Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
 	PC(PC_in)
 {}
 
-unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) {
+unsigned Interrupter::interrupt(const unsigned address, unsigned cycleCounter, Memory &memory) {
 	cycleCounter += 8;
 	SP = (SP - 1) & 0xFFFF;
 	memory.write(SP, PC >> 8, cycleCounter);
@@ -61,11 +65,20 @@ void Interrupter::setGameShark(const std::string &codes) {
 	}
 }
 
-void Interrupter::applyVblankCheats(const unsigned long cycleCounter, Memory &memory) {
+void Interrupter::applyVblankCheats(const unsigned cycleCounter, Memory &memory) {
 	for (std::size_t i = 0, size = gsCodes.size(); i < size; ++i) {
 		if (gsCodes[i].type == 0x01)
 			memory.write(gsCodes[i].address, gsCodes[i].value, cycleCounter);
 	}
 }
 
+void Interrupter::loadOrSave(loadsave& state) {
+	unsigned gssize = gsCodes.size();
+	state(gssize);
+	if(!state.saving())
+		gsCodes.resize(gssize);
+	for(unsigned i = 0; i < gssize; i++)
+		gsCodes[i].loadOrSave(state);
+}
+
 }
diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h
index d8f2f10..e0619e3 100644
--- a/libgambatte/src/interrupter.h
+++ b/libgambatte/src/interrupter.h
@@ -19,8 +19,14 @@
 #ifndef INTERRUPTER_H
 #define INTERRUPTER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include <string>
 #include <vector>
+#include "loadsave.h"
+
 
 namespace gambatte {
 
@@ -28,6 +34,12 @@ struct GsCode {
 	unsigned short address;
 	unsigned char value;
 	unsigned char type;
+
+	void loadOrSave(loadsave& state) {
+		state(address);
+		state(value);
+		state(type);
+	}
 };
 
 class Interrupter {
@@ -35,11 +47,13 @@ class Interrupter {
 	unsigned short &PC;
 	std::vector<GsCode> gsCodes;
 	
-	void applyVblankCheats(unsigned long cc, class Memory &mem);
+	void applyVblankCheats(unsigned cc, class Memory &mem);
 public:
 	Interrupter(unsigned short &SP, unsigned short &PC);
-	unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory);
+	unsigned interrupt(const unsigned address, unsigned cycleCounter, class Memory &memory);
 	void setGameShark(const std::string &codes);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp
index 914f617..f63c0a1 100644
--- a/libgambatte/src/interruptrequester.cpp
+++ b/libgambatte/src/interruptrequester.cpp
@@ -19,6 +19,10 @@
 #include "interruptrequester.h"
 #include "savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {}
@@ -35,17 +39,26 @@ void InterruptRequester::loadState(const SaveState &state) {
 	iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
 	intFlags.set(state.mem.IME, state.mem.halted);
 	
-	eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+	eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
+}
+
+void InterruptRequester::loadOrSave(loadsave& state)
+{
+	eventTimes.loadOrSave(state);
+	state(minIntTime);
+	state(ifreg_);
+	state(iereg_);
+	intFlags.loadOrSave(state);
 }
 
-void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
+void InterruptRequester::resetCc(const unsigned oldCc, const unsigned newCc) {
 	minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
 	
 	if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
 		eventTimes.setValue<INTERRUPTS>(minIntTime);
 }
 
-void InterruptRequester::ei(const unsigned long cc) {
+void InterruptRequester::ei(const unsigned cc) {
 	intFlags.setIme();
 	minIntTime = cc + 1;
 	
@@ -90,14 +103,14 @@ void InterruptRequester::setIereg(const unsigned iereg) {
 	iereg_ = iereg & 0x1F;
 	
 	if (intFlags.imeOrHalted())
-		eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+		eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
 }
 
 void InterruptRequester::setIfreg(const unsigned ifreg) {
 	ifreg_ = ifreg;
 	
 	if (intFlags.imeOrHalted())
-		eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+		eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
 }
 
 }
diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h
index 78c9d3f..61326fb 100644
--- a/libgambatte/src/interruptrequester.h
+++ b/libgambatte/src/interruptrequester.h
@@ -19,8 +19,13 @@
 #ifndef INTERRUPT_REQUESTER_H
 #define INTERRUPT_REQUESTER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "counterdef.h"
 #include "minkeeper.h"
+#include "loadsave.h"
 
 namespace gambatte {
 struct SaveState;
@@ -28,7 +33,7 @@ enum MemEventId { UNHALT, END, BLIT, SERIAL, OAM, DMA, TIMA, VIDEO, INTERRUPTS }
 
 class InterruptRequester {
 	MinKeeper<INTERRUPTS + 1> eventTimes;
-	unsigned long minIntTime;
+	unsigned minIntTime;
 	unsigned ifreg_;
 	unsigned iereg_;
 	
@@ -38,7 +43,9 @@ class InterruptRequester {
 		
 	public:
 		IntFlags() : flags_(0) {}
-		
+
+		void loadOrSave(loadsave& state) { state(flags_); }
+
 		bool ime() const { return flags_ & IME_MASK; }
 		bool halted() const { return flags_ & HALTED_MASK; }
 		bool imeOrHalted() const { return flags_; }
@@ -57,15 +64,17 @@ public:
 	
 	void saveState(SaveState &) const;
 	void loadState(const SaveState &);
-	
-	void resetCc(unsigned long oldCc, unsigned long newCc);
+
+	void loadOrSave(loadsave& state);
+
+	void resetCc(unsigned oldCc, unsigned newCc);
 	
 	unsigned ifreg() const { return ifreg_; }
 	unsigned pendingIrqs() const { return ifreg_ & iereg_; }
 	bool ime() const { return intFlags.ime(); }
 	bool halted() const { return intFlags.halted(); }
 	
-	void ei(unsigned long cc);
+	void ei(unsigned cc);
 	void di();
 	void halt();
 	void unhalt();
@@ -75,10 +84,10 @@ public:
 	void setIfreg(unsigned ifreg);
 	
 	MemEventId minEventId() const { return static_cast<MemEventId>(eventTimes.min()); }
-	unsigned long minEventTime() const { return eventTimes.minValue(); }
-	template<MemEventId id> void setEventTime(unsigned long value) { eventTimes.setValue<id>(value); }
-	void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
-	unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
+	unsigned minEventTime() const { return eventTimes.minValue(); }
+	template<MemEventId id> void setEventTime(unsigned value) { eventTimes.setValue<id>(value); }
+	void setEventTime(const MemEventId id, unsigned value) { eventTimes.setValue(id, value); }
+	unsigned eventTime(MemEventId id) const { return eventTimes.value(id); }
 };
 
 inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
diff --git a/libgambatte/src/loadsave.cpp b/libgambatte/src/loadsave.cpp
new file mode 100644
index 0000000..37ea71a
--- /dev/null
+++ b/libgambatte/src/loadsave.cpp
@@ -0,0 +1,266 @@
+/***************************************************************************
+ *   Copyright (C) 2012 by H. Ilari Liusvaara                              *
+ *   ilari.liusvaara@elisanet.fi                                           *
+ *                                                                         *
+ *   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 "loadsave.h"
+#include <stdexcept>
+#include <cstring>
+#include <iostream>
+
+namespace gambatte {
+
+loadsave::~loadsave() throw() {}
+
+loadsave_load::loadsave_load(const std::vector<char>& _memory)
+	: memory(_memory)
+{
+	ptr = 0;
+}
+
+template<typename T> void loadsave_load::do_op(T& x)
+{
+	unsigned long long v = 0;
+	if(ptr + sizeof(T) > memory.size())
+		throw std::runtime_error("Loadstate overflow");
+	for(size_t i = 0; i < sizeof(T); i++)
+		v |= ((unsigned long long)(unsigned char)memory[ptr++] << (8 * (sizeof(T) - i - 1)));
+	x = (T)v;
+}
+
+template<typename T> void loadsave_load::do_op(T& x, unsigned char _tag)
+{
+	if(ptr + 1 > memory.size())
+		throw std::runtime_error("Loadstate overflow");
+	unsigned char _rtag = memory[ptr++];
+	if(_rtag != _tag) {
+		std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+		throw std::runtime_error("Loadstate desynced");
+	}
+	do_op(x);
+}
+
+template<typename T> void loadsave_load::do_op(T* x, size_t s, unsigned char _tag)
+{
+	if(ptr + 1 > memory.size())
+		throw std::runtime_error("Loadstate overflow");
+	unsigned char _rtag = memory[ptr++];
+	if(_rtag != _tag) {
+		std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+		throw std::runtime_error("Loadstate desynced");
+	}
+	unsigned size;
+	do_op(size);
+	if(size != s) {
+		std::cerr << "Wrong number of entries: expected=" << s << ", got=" << size << std::endl;
+		throw std::runtime_error("Loadstate desynced");
+	}
+	for(size_t i = 0; i < s; i++)
+		do_op(x[i]);
+}
+
+void loadsave_load::operator()(bool& x)
+{
+	char c;
+	do_op(c, 0);
+	x = (c != 0);
+}
+
+
+loadsave_load::~loadsave_load() throw() {}
+void loadsave_load::operator()(signed char& x) { do_op(x, 1); }
+void loadsave_load::operator()(unsigned char& x) { do_op(x, 2); }
+void loadsave_load::operator()(signed short& x) { do_op(x, 3); }
+void loadsave_load::operator()(unsigned short& x) { do_op(x, 4); }
+void loadsave_load::operator()(signed int& x) { do_op(x, 5); }
+void loadsave_load::operator()(unsigned int& x) { do_op(x, 6); }
+void loadsave_load::operator()(signed long long& x) { do_op(x, 7); }
+void loadsave_load::operator()(unsigned long long& x) { do_op(x, 8); }
+void loadsave_load::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
+void loadsave_load::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
+void loadsave_load::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
+void loadsave_load::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
+void loadsave_load::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
+void loadsave_load::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
+void loadsave_load::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
+void loadsave_load::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
+
+void loadsave_load::tag(unsigned short _tag)
+{
+	unsigned short _rtag;
+	do_op(_rtag, 18);
+	if(_tag != _rtag) {
+		std::cerr << "Wrong inner tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+		throw std::runtime_error("Loadstate desynced");
+	}
+}
+
+void loadsave_load::operator()(unsigned char*& ptr, unsigned char* abase)
+{
+	char x;
+	do_op(x, 17);
+	if(!x)
+		ptr = NULL;
+	else {
+		unsigned y;
+		do_op(y);
+		ptr = abase + y;
+	}
+}
+
+void loadsave_load::operator()(const unsigned char*& ptr, unsigned char* abase)
+{
+	char x;
+	do_op(x, 19);
+	if(!x)
+		ptr = NULL;
+	else {
+		unsigned y;
+		do_op(y);
+		ptr = abase + y;
+	}
+}
+
+
+bool loadsave_load::saving() { return false; }
+
+#define BLOCKBYTES 65500
+
+void loadsave_save::pushbytes(char* bytes, size_t amount)
+{
+	if(!nextptr || memory[nextptr - 1].second + amount > BLOCKBYTES) {
+		memory.push_back(std::make_pair(new char[BLOCKBYTES], (size_t)0));
+		nextptr++;
+	}
+	if(cmp.size())
+		try { if(cmp.size() < used + amount || memcmp(&cmp[used], bytes, amount)) throw 42; } catch(...) {}
+	memcpy(memory[nextptr - 1].first + memory[nextptr - 1].second, bytes, amount);
+	memory[nextptr - 1].second += amount;
+	used += amount;
+}
+
+template<typename T> void loadsave_save::do_op(T& x)
+{
+	unsigned long long v = x;
+	char buf[sizeof(T)];
+	for(size_t i = 0; i < sizeof(T); i++)
+		buf[i] = v >> (8 * (sizeof(T) - i - 1));
+	pushbytes(buf, sizeof(T));
+}
+
+template<typename T> void loadsave_save::do_op(T& x, unsigned char _tag)
+{
+	pushbytes((char*)&_tag, 1);
+	do_op(x);
+}
+
+template<typename T> void loadsave_save::do_op(T* x, size_t s, unsigned char _tag)
+{
+	pushbytes((char*)&_tag, 1);
+	unsigned size = s;
+	do_op(size);
+	for(size_t i = 0; i < s; i++)
+		do_op(x[i]);
+}
+
+loadsave_save::loadsave_save()
+{
+	used = 0;
+	nextptr = 0;
+}
+
+loadsave_save::loadsave_save(const std::vector<char>& _memory)
+{
+	used = 0;
+	nextptr = 0;
+	cmp = _memory;
+}
+
+loadsave_save::~loadsave_save() throw()
+{
+	for(auto i : memory)
+		delete[] i.first;
+}
+
+void loadsave_save::operator()(bool& x)
+{
+	char y = x ? 1 : 0;
+	char z = 0;
+	pushbytes(&z, 1);
+	pushbytes(&y, 1);
+}
+
+void loadsave_save::operator()(signed char& x) { do_op(x, 1); }
+void loadsave_save::operator()(unsigned char& x) { do_op(x, 2); }
+void loadsave_save::operator()(signed short& x) { do_op(x, 3); }
+void loadsave_save::operator()(unsigned short& x) { do_op(x, 4); }
+void loadsave_save::operator()(signed int& x) { do_op(x, 5); }
+void loadsave_save::operator()(unsigned int& x) { do_op(x, 6); }
+void loadsave_save::operator()(signed long long& x) { do_op(x, 7); }
+void loadsave_save::operator()(unsigned long long& x) { do_op(x, 8); }
+void loadsave_save::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
+void loadsave_save::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
+void loadsave_save::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
+void loadsave_save::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
+void loadsave_save::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
+void loadsave_save::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
+void loadsave_save::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
+void loadsave_save::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
+bool loadsave_save::saving() { return true; }
+
+void loadsave_save::operator()(unsigned char*& ptr, unsigned char* abase)
+{
+	if(!ptr) {
+		char x = 0;
+		do_op(x, 17);
+	} else {
+		char x = 1;
+		unsigned y = ptr - abase;
+		do_op(x, 17);
+		do_op(y);
+	}
+}
+
+void loadsave_save::operator()(const unsigned char*& ptr, unsigned char* abase)
+{
+	if(!ptr) {
+		char x = 0;
+		do_op(x, 19);
+	} else {
+		char x = 1;
+		unsigned y = ptr - abase;
+		do_op(x, 19);
+		do_op(y);
+	}
+}
+
+void loadsave_save::tag(unsigned short _tag)
+{
+	do_op(_tag, 18);
+}
+
+std::vector<char> loadsave_save::get()
+{
+	std::vector<char> x;
+	x.resize(used);
+	size_t ptr = 0;
+	for(auto i : memory) {
+		memcpy(&x[ptr], i.first, i.second);
+		ptr += i.second;
+	}
+	return x;
+}
+}
diff --git a/libgambatte/src/loadsave.h b/libgambatte/src/loadsave.h
new file mode 100644
index 0000000..10ebf63
--- /dev/null
+++ b/libgambatte/src/loadsave.h
@@ -0,0 +1,160 @@
+#ifndef _loadsave__hpp__included__
+#define _loadsave__hpp__included__
+/***************************************************************************
+ *   Copyright (C) 2012 by H. Ilari Liusvaara                              *
+ *   ilari.liusvaara@elisanet.fi                                           *
+ *                                                                         *
+ *   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 <cstdint>
+#include <vector>
+#include <cstdlib>
+#include <stdexcept>
+
+namespace gambatte {
+	class loadsave
+	{
+	private:
+		unsigned enumVal;
+		bool enumAssigned;
+	public:
+		virtual ~loadsave() throw();
+		virtual void operator()(bool& x) = 0;
+		virtual void operator()(signed char& x) = 0;
+		virtual void operator()(unsigned char& x) = 0;
+		virtual void operator()(signed short& x) = 0;
+		virtual void operator()(unsigned short& x) = 0;
+		virtual void operator()(signed int& x) = 0;
+		virtual void operator()(unsigned int& x) = 0;
+		virtual void operator()(signed long long& x) = 0;
+		virtual void operator()(unsigned long long& x) = 0;
+		virtual void operator()(signed char* x, size_t s) = 0;
+		virtual void operator()(unsigned char* x, size_t s) = 0;
+		virtual void operator()(signed short* x, size_t s) = 0;
+		virtual void operator()(unsigned short* x, size_t s) = 0;
+		virtual void operator()(signed int* x, size_t s) = 0;
+		virtual void operator()(unsigned int* x, size_t s) = 0;
+		virtual void operator()(long long* x, size_t s) = 0;
+		virtual void operator()(unsigned long long* x, size_t s) = 0;
+		virtual void operator()(unsigned char*& ptr, unsigned char* abase) = 0;
+		virtual void operator()(const unsigned char*& ptr, unsigned char* abase) = 0;
+		virtual void tag(unsigned short tag) = 0;
+		void time(time_t& t) {
+			unsigned long long t_ = t;
+			(*this)(t_);
+			t = t_;
+		}
+		void startEnumeration() {
+			enumAssigned = false;
+			enumVal = 0xFFFFFFFFU;
+			if(!saving())
+				(*this)(enumVal);
+		}
+		template<typename T> void enumerate(T& ptr, T candiate, unsigned symbol) {
+			if(saving()) {
+				if(ptr == candiate) {
+					enumVal = symbol;
+					enumAssigned = true;
+				}
+			} else {
+				if(enumVal == symbol) {
+					ptr = candiate;
+					enumAssigned = true;
+				}
+			}
+		}
+		void endEnumeration() {
+			if(saving())
+				(*this)(enumVal);
+			if(!enumAssigned)
+				throw std::runtime_error("Enumeration missing a choice");
+		}
+		virtual bool saving() = 0;
+	};
+
+	class loadsave_load : public loadsave
+	{
+		const std::vector<char>& memory;
+		size_t ptr;
+		template<typename T> inline void do_op(T& x);
+		template<typename T> inline void do_op(T& x, unsigned char _tag);
+		template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
+	public:
+		loadsave_load(const std::vector<char>& _memory);
+		~loadsave_load() throw();
+		void operator()(bool& x);
+		void operator()(signed char& x);
+		void operator()(unsigned char& x);
+		void operator()(signed short& x);
+		void operator()(unsigned short& x);
+		void operator()(signed int& x);
+		void operator()(unsigned int& x);
+		void operator()(signed long long& x);
+		void operator()(unsigned long long& x);
+		void operator()(signed char* x, size_t s);
+		void operator()(unsigned char* x, size_t s);
+		void operator()(signed short* x, size_t s);
+		void operator()(unsigned short* x, size_t s);
+		void operator()(signed int* x, size_t s);
+		void operator()(unsigned int* x, size_t s);
+		void operator()(signed long long* x, size_t s);
+		void operator()(unsigned long long* x, size_t s);
+		void operator()(unsigned char*& ptr, unsigned char* abase);
+		void operator()(const unsigned char*& ptr, unsigned char* abase);
+		void tag(unsigned short _tag);
+		bool saving();
+	};
+
+	class loadsave_save : public loadsave
+	{
+		std::vector<std::pair<char*, size_t>> memory;
+		size_t nextptr;
+		size_t used;
+		inline void pushbytes(char* bytes, size_t amount);
+		template<typename T> inline void do_op(T& x);
+		template<typename T> inline void do_op(T& x, unsigned char _tag);
+		template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
+		std::vector<char> cmp;
+	public:
+		loadsave_save();
+		loadsave_save(const std::vector<char>& _memory);
+		~loadsave_save() throw();
+		void operator()(bool& x);
+		void operator()(signed char& x);
+		void operator()(unsigned char& x);
+		void operator()(signed short& x);
+		void operator()(unsigned short& x);
+		void operator()(signed int& x);
+		void operator()(unsigned int& x);
+		void operator()(signed long long& x);
+		void operator()(unsigned long long& x);
+		void operator()(signed char* x, size_t s);
+		void operator()(unsigned char* x, size_t s);
+		void operator()(signed short* x, size_t s);
+		void operator()(unsigned short* x, size_t s);
+		void operator()(signed int* x, size_t s);
+		void operator()(unsigned int* x, size_t s);
+		void operator()(signed long long* x, size_t s);
+		void operator()(unsigned long long* x, size_t s);
+		void operator()(unsigned char*& ptr, unsigned char* abase);
+		void operator()(const unsigned char*& ptr, unsigned char* abase);
+		void tag(unsigned short _tag);
+		bool saving();
+		std::vector<char> get();
+	};
+}
+
+#endif
diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp
index 452efa4..dcfed26 100644
--- a/libgambatte/src/mem/cartridge.cpp
+++ b/libgambatte/src/mem/cartridge.cpp
@@ -23,6 +23,10 @@
 #include <cstring>
 #include <fstream>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 namespace {
@@ -36,6 +40,8 @@ public:
 	virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
 		return (addr< 0x4000) == (bank == 0);
 	}
+	void loadOrSave(loadsave& state) {
+	}
 };
 
 class Mbc0 : public DefaultMbc {
@@ -64,6 +70,10 @@ public:
 		enableRam = ss.enableRam;
 		memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
 	}
+
+	void loadOrSave(loadsave& state) {
+		state(enableRam);
+	}
 };
 
 static inline unsigned rambanks(const MemPtrs &memptrs) {
@@ -95,6 +105,13 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(rambank);
+		state(enableRam);
+		state(rambankMode);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P >> 13 & 3) {
 		case 0:
@@ -168,6 +185,12 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(enableRam);
+		state(rombank0Mode);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P >> 13 & 3) {
 		case 0:
@@ -221,6 +244,11 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(enableRam);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P & 0x6100) {
 		case 0x0000:
@@ -277,6 +305,12 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(rambank);
+		state(enableRam);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P >> 13 & 3) {
 		case 0:
@@ -338,6 +372,13 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(rambank);
+		state(enableRam);
+		state(rambankMode);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P >> 13 & 3) {
 		case 0:
@@ -396,6 +437,12 @@ public:
 	{
 	}
 
+	void loadOrSave(loadsave& state) {
+		state(rombank);
+		state(rambank);
+		state(enableRam);
+	}
+
 	virtual void romWrite(const unsigned P, const unsigned data) {
 		switch (P >> 13 & 3) {
 		case 0:
@@ -505,7 +552,15 @@ static unsigned pow2ceil(unsigned n) {
 
 int Cartridge::loadROM(const std::string &romfile, const bool forceDmg, const bool multicartCompat) {
 	const std::auto_ptr<File> rom(newFileInstance(romfile));
+	return loadROM(rom.get(), forceDmg, multicartCompat, romfile);
+}
 
+int Cartridge::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
+	const std::auto_ptr<File> rom(newFileInstance(image, isize));
+	return loadROM(rom.get(), forceDmg, multicartCompat, "");
+}
+
+int Cartridge::loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename) {
 	if (rom->fail())
 		return -1;
 	
@@ -613,8 +668,15 @@ int Cartridge::loadROM(const std::string &romfile, const bool forceDmg, const bo
 	if (rom->fail())
 		return -1;
 	
-	defaultSaveBasePath = stripExtension(romfile);
-	
+	if(filename != "") {
+		defaultSaveBasePath = stripExtension(filename);
+		memoryCartridge = false;
+	} else {
+		defaultSaveBasePath = "";
+		memoryCartridge = true;
+	}
+	clearMemorySavedData();
+
 	switch (type) {
 	case PLAIN: mbc.reset(new Mbc0(memptrs)); break;
 	case MBC1:
@@ -653,45 +715,69 @@ void Cartridge::loadSavedata() {
 	const std::string &sbp = saveBasePath();
 
 	if (hasBattery(memptrs.romdata()[0x147])) {
-		std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
+		if(memoryCartridge) {
+			if(memoryCartridgeSram.size())
+				memcpy(memptrs.rambankdata(), &memoryCartridgeSram[0], memptrs.rambankdataend() - memptrs.rambankdata());
+		} else {
+			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 (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(memoryCartridge) {
+			rtc.setBaseTime(memoryCartridgeRtcBase);
+		} else {
+			std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
 
-		if (file.is_open()) {
-			unsigned long basetime = file.get() & 0xFF;
+			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);
+				basetime = basetime << 8 | (file.get() & 0xFF);
+				basetime = basetime << 8 | (file.get() & 0xFF);
+				basetime = basetime << 8 | (file.get() & 0xFF);
 
-			rtc.setBaseTime(basetime);
+				rtc.setBaseTime(basetime);
+			}
 		}
 	}
 }
 
+void Cartridge::clearMemorySavedData()
+{
+	memoryCartridgeRtcBase = 0;
+	memoryCartridgeSram.resize(0);
+}
+
 void Cartridge::saveSavedata() {
 	const std::string &sbp = saveBasePath();
 
 	if (hasBattery(memptrs.romdata()[0x147])) {
-		std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
-		file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
+		if(memoryCartridge) {
+			memoryCartridgeSram.resize(memptrs.rambankdataend() - memptrs.rambankdata());
+			memcpy(&memoryCartridgeSram[0], memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
+		} else {
+			std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
+			file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
+		}
 	}
 
 	if (hasRtc(memptrs.romdata()[0x147])) {
-		std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
-		const unsigned long basetime = rtc.getBaseTime();
+		if(memoryCartridge) {
+			memoryCartridgeRtcBase = rtc.getBaseTime();
+		} else {
+			std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
+			const unsigned long basetime = rtc.getBaseTime();
 
-		file.put(basetime >> 24 & 0xFF);
-		file.put(basetime >> 16 & 0xFF);
-		file.put(basetime >>  8 & 0xFF);
-		file.put(basetime       & 0xFF);
+			file.put(basetime >> 24 & 0xFF);
+			file.put(basetime >> 16 & 0xFF);
+			file.put(basetime >>  8 & 0xFF);
+			file.put(basetime       & 0xFF);
+		}
 	}
 }
 
@@ -737,4 +823,31 @@ void Cartridge::setGameGenie(const std::string &codes) {
 	}
 }
 
+std::pair<unsigned char*, size_t> Cartridge::getSaveRam() {
+	size_t sramsize = memptrs.rambankdataend() - memptrs.rambankdata();
+	return std::make_pair(memptrs.rambankdata(), sramsize);
+}
+
+std::pair<unsigned char*, size_t> Cartridge::getWorkRam() {
+	size_t worksize = memptrs.wramdataend() - memptrs.wramdata(0);
+	return std::make_pair(memptrs.wramdata(0), worksize);
+}
+
+Cartridge::Cartridge(time_t (**_getCurrentTime)())
+	: rtc(_getCurrentTime) {
+	memoryCartridge = true;
+}
+
+void Cartridge::loadOrSave(loadsave& state) {
+	memptrs.loadOrSave(state);
+	rtc.loadOrSave(state);
+	mbc->loadOrSave(state);
+	unsigned ggsize = ggUndoList.size();
+	state(ggsize);
+	if(!state.saving())
+		ggUndoList.resize(ggsize);
+	for(size_t i = 0; i < ggsize; i++)
+		ggUndoList[i].loadOrSave(state);
+}
+
 }
diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
index bbb3662..3ae779e 100644
--- a/libgambatte/src/mem/cartridge.h
+++ b/libgambatte/src/mem/cartridge.h
@@ -25,9 +25,16 @@
 #include <memory>
 #include <string>
 #include <vector>
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 
+class File;
+
 class Mbc {
 public:
 	virtual ~Mbc() {}
@@ -35,13 +42,19 @@ public:
 	virtual void saveState(SaveState::Mem &ss) const = 0;
 	virtual void loadState(const SaveState::Mem &ss) = 0;
 	virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
+	virtual void loadOrSave(loadsave& state) = 0;
 };
 
 class Cartridge {
 	struct AddrData {
-		unsigned long addr;
+		unsigned addr;
 		unsigned char data;
-		AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
+		AddrData(unsigned addr, unsigned data) : addr(addr), data(data) {}
+		AddrData() {}
+		void loadOrSave(loadsave& state) {
+			state(addr);
+			state(data);
+		}
 	};
 	
 	MemPtrs memptrs;
@@ -50,14 +63,22 @@ class Cartridge {
 	std::string defaultSaveBasePath;
 	std::string saveDir;
 	std::vector<AddrData> ggUndoList;
-	
+	bool memoryCartridge;
+	time_t memoryCartridgeRtcBase;
+	std::vector<unsigned char> memoryCartridgeSram;
+
 	void applyGameGenie(const std::string &code);
-	
+
+	int loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename);
+	void clearMemorySavedData();
 public:
+	Cartridge(time_t (**_getCurrentTime)());
 	void setStatePtrs(SaveState &);
 	void saveState(SaveState &) const;
 	void loadState(const SaveState &);
 	
+	void loadOrSave(loadsave& state);
+
 	bool loaded() const { return mbc.get(); }
 	
 	const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); }
@@ -84,8 +105,15 @@ public:
 	const std::string saveBasePath() const;
 	void setSaveDir(const std::string &dir);
 	int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
+	int loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
 	const char * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
 	void setGameGenie(const std::string &codes);
+
+	void setRtcBase(time_t time) { rtc.setBaseTime(time); }
+	time_t getRtcBase() { return rtc.getBaseTime(); }
+	std::pair<unsigned char*, size_t> getWorkRam();
+	std::pair<unsigned char*, size_t> getSaveRam();
+
 };
 
 }
diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp
index 243810c..049a8ba 100644
--- a/libgambatte/src/mem/memptrs.cpp
+++ b/libgambatte/src/mem/memptrs.cpp
@@ -20,6 +20,10 @@
 #include <algorithm>
 #include <cstring>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 MemPtrs::MemPtrs() :
@@ -39,7 +43,11 @@ MemPtrs::~MemPtrs() {
 
 void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) {
 	delete []memchunk_;
-	memchunk_ = new unsigned char[0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000];
+	memchunk_size = 0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000; 
+	memchunk_ = new unsigned char[memchunk_size];
+
+	//FIXME: Make this random.
+	memset(memchunk_, 0, memchunk_size);
 
 	romdata_[0] = romdata();
 	rambankdata_ = romdata_[0] + rombanks * 0x4000ul;
@@ -141,4 +149,26 @@ void MemPtrs::disconnectOamDmaAreas() {
 	}
 }
 
+void MemPtrs::loadOrSave(loadsave& state)
+{
+	state(memchunk_, 0x4000);
+	state(rambankdata_, memchunk_size - (rambankdata_ - memchunk_));
+	int oamDmaSrc_2 = oamDmaSrc_;
+	state(oamDmaSrc_2);
+	oamDmaSrc_ = (OamDmaSrc)oamDmaSrc_2;
+	//Rmem is constant.
+	for(unsigned i = 0; i < 0x10; i++)
+		state(wmem_[i], memchunk_);
+	for(unsigned i = 0; i < 0x10; i++)
+		state(rmem_[i], memchunk_);
+	state(romdata_[0], memchunk_);
+	state(romdata_[1], memchunk_);
+	state(rambankdata_, memchunk_);
+	state(rdisabledRam_, memchunk_);
+	state(wdisabledRam_, memchunk_);
+	state(rsrambankptr_, memchunk_);
+	state(wsrambankptr_, memchunk_);
+	//memchunk_size is cart constant, not saved.
+}
+
 }
diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h
index 62c0fa9..2fc2789 100644
--- a/libgambatte/src/mem/memptrs.h
+++ b/libgambatte/src/mem/memptrs.h
@@ -19,6 +19,12 @@
 #ifndef MEMPTRS_H
 #define MEMPTRS_H
 
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM,
@@ -36,7 +42,8 @@ class MemPtrs {
 	unsigned char *wdisabledRam_;
 	unsigned char *rsrambankptr_;
 	unsigned char *wsrambankptr_;
-	
+	unsigned memchunk_size;
+
 	OamDmaSrc oamDmaSrc_;
 	
 	MemPtrs(const MemPtrs &);
@@ -68,6 +75,8 @@ public:
 	void setRambank(unsigned ramFlags, unsigned rambank);
 	void setWrambank(unsigned bank);
 	void setOamDmaSrc(OamDmaSrc oamDmaSrc);
+
+	void loadOrSave(loadsave& state);
 };
 
 inline bool isCgb(const MemPtrs &memptrs) {
diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp
index 993db29..62e0dac 100644
--- a/libgambatte/src/mem/rtc.cpp
+++ b/libgambatte/src/mem/rtc.cpp
@@ -19,9 +19,13 @@
 #include "rtc.h"
 #include "../savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
-Rtc::Rtc()
+Rtc::Rtc(time_t (**_getCurrentTime)())
 : activeData(NULL),
   activeSet(NULL),
   baseTime(0),
@@ -33,12 +37,13 @@ Rtc::Rtc()
   dataM(0),
   dataS(0),
   enabled(false),
-  lastLatchData(false)
+  lastLatchData(false),
+  getCurrentTime(_getCurrentTime)
 {
 }
 
 void Rtc::doLatch() {
-	std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
+	std::time_t tmp = ((dataDh & 0x40) ? haltTime : (*getCurrentTime)()) - baseTime;
 	
 	while (tmp > 0x1FF * 86400) {
 		baseTime += 0x1FF * 86400;
@@ -113,44 +118,76 @@ void Rtc::loadState(const SaveState &state) {
 }
 
 void Rtc::setDh(const unsigned new_dh) {
-	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
 	const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100;
 	baseTime += old_highdays * 86400;
 	baseTime -= ((new_dh & 0x1) << 8) * 86400;
 	
 	if ((dataDh ^ new_dh) & 0x40) {
 		if (new_dh & 0x40)
-			haltTime = std::time(0);
+			haltTime = (*getCurrentTime)();
 		else
-			baseTime += std::time(0) - haltTime;
+			baseTime += (*getCurrentTime)() - haltTime;
 	}
 }
 
 void Rtc::setDl(const unsigned new_lowdays) {
-	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
 	const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
 	baseTime += old_lowdays * 86400;
 	baseTime -= new_lowdays * 86400;
 }
 
 void Rtc::setH(const unsigned new_hours) {
-	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
 	const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
 	baseTime += old_hours * 3600;
 	baseTime -= new_hours * 3600;
 }
 
 void Rtc::setM(const unsigned new_minutes) {
-	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
 	const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
 	baseTime += old_minutes * 60;
 	baseTime -= new_minutes * 60;
 }
 
 void Rtc::setS(const unsigned new_seconds) {
-	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+	const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
 	baseTime += (unixtime - baseTime) % 60;
 	baseTime -= new_seconds;
 }
 
+void Rtc::loadOrSave(loadsave& state)
+{
+	state.startEnumeration();
+	state.enumerate<unsigned char*>(activeData, NULL, 0);
+	state.enumerate(activeData, &dataDh, 1);
+	state.enumerate(activeData, &dataDl, 2);
+	state.enumerate(activeData, &dataH, 3);
+	state.enumerate(activeData, &dataM, 4);
+	state.enumerate(activeData, &dataS, 5);
+	state.endEnumeration();
+
+	state.startEnumeration();
+	state.enumerate<void (gambatte::Rtc::*)(unsigned int)>(activeSet, NULL, 0);
+	state.enumerate(activeSet, &Rtc::setDh, 1);
+	state.enumerate(activeSet, &Rtc::setDl, 2);
+	state.enumerate(activeSet, &Rtc::setH, 3);
+	state.enumerate(activeSet, &Rtc::setM, 4);
+	state.enumerate(activeSet, &Rtc::setS, 5);
+	state.endEnumeration();
+
+	state.time(baseTime);
+	state.time(haltTime);
+	state(index);
+	state(dataDh);
+	state(dataDl);
+	state(dataH);
+	state(dataM);
+	state(dataS);
+	state(enabled);
+	state(lastLatchData);
+}
+
 }
diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h
index c5b3b5a..206b46e 100644
--- a/libgambatte/src/mem/rtc.h
+++ b/libgambatte/src/mem/rtc.h
@@ -20,6 +20,12 @@
 #define RTC_H
 
 #include <ctime>
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 
 namespace gambatte {
 
@@ -39,6 +45,7 @@ private:
 	unsigned char dataS;
 	bool enabled;
 	bool lastLatchData;
+	time_t (**getCurrentTime)();
 	
 	void doLatch();
 	void doSwapActive();
@@ -49,7 +56,7 @@ private:
 	void setS(unsigned new_seconds);
 	
 public:
-	Rtc();
+	Rtc(time_t (**_getCurrentTime)());
 	
 	const unsigned char* getActive() const { return activeData; }
 	std::time_t getBaseTime() const { return baseTime; }
@@ -84,6 +91,8 @@ public:
 		(this->*activeSet)(data);
 		*activeData = data;
 	}
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index 01abb57..edba55b 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -23,9 +23,13 @@
 #include "savestate.h"
 #include <cstring>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
-Memory::Memory(const Interrupter &interrupter_in)
+Memory::Memory(const Interrupter &interrupter_in, time_t (**_getCurrentTime)())
 : vrambank(vram),
   getInput(0),
   divLastUpdate(0),
@@ -36,7 +40,8 @@ Memory::Memory(const Interrupter &interrupter_in)
   dmaDestination(0),
   oamDmaPos(0xFE),
   serialCnt(0),
-  blanklcd(false)
+  blanklcd(false),
+  cart(_getCurrentTime)
 {
 	intreq.setEventTime<BLIT>(144*456ul);
 	intreq.setEventTime<END>(0);
@@ -51,7 +56,7 @@ void Memory::setStatePtrs(SaveState &state) {
 	sound.setStatePtrs(state);
 }
 
-unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
+unsigned Memory::saveState(SaveState &state, unsigned cycleCounter) {
 	cycleCounter = resetCounters(cycleCounter);
 	nontrivial_ff_read(0xFF05, cycleCounter);
 	nontrivial_ff_read(0xFF0F, cycleCounter);
@@ -74,7 +79,7 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
 	return cycleCounter;
 }
 
-static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
+static inline int serialCntFrom(const unsigned cyclesUntilDone, const bool cgbFast) {
 	return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
 }
 
@@ -116,14 +121,14 @@ void Memory::loadState(const SaveState &state) {
 		std::memset(vram + 0x2000, 0, 0x2000);
 }
 
-void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
+void Memory::setEndtime(const unsigned cycleCounter, const unsigned inc) {
 	if (intreq.eventTime(BLIT) <= cycleCounter)
 		intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
 	
 	intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
 }
 
-void Memory::updateSerial(const unsigned long cc) {
+void Memory::updateSerial(const unsigned cc) {
 	if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
 		if (intreq.eventTime(SERIAL) <= cc) {
 			ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
@@ -138,18 +143,18 @@ void Memory::updateSerial(const unsigned long cc) {
 	}
 }
 
-void Memory::updateTimaIrq(const unsigned long cc) {
+void Memory::updateTimaIrq(const unsigned cc) {
 	while (intreq.eventTime(TIMA) <= cc)
 		tima.doIrqEvent(TimaInterruptRequester(intreq));
 }
 
-void Memory::updateIrqs(const unsigned long cc) {
+void Memory::updateIrqs(const unsigned cc) {
 	updateSerial(cc);
 	updateTimaIrq(cc);
 	display.update(cc);
 }
 
-unsigned long Memory::event(unsigned long cycleCounter) {
+unsigned Memory::event(unsigned cycleCounter) {
 	if (lastOamDmaUpdate != DISABLED_TIME)
 		updateOamDma(cycleCounter);
 
@@ -170,10 +175,10 @@ unsigned long Memory::event(unsigned long cycleCounter) {
 	case BLIT:
 		{
 			const bool lcden = ioamhram[0x140] >> 7 & 1;
-			unsigned long blitTime = intreq.eventTime(BLIT);
+			unsigned blitTime = intreq.eventTime(BLIT);
 			
 			if (lcden | blanklcd) {
-				display.updateScreen(blanklcd, cycleCounter);
+				display.updateScreen(blanklcd, cycleCounter, videoBuf_, pitch_);
 				intreq.setEventTime<BLIT>(DISABLED_TIME);
 				intreq.setEventTime<END>(DISABLED_TIME);
 				
@@ -191,7 +196,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
 		break;
 	case OAM:
 		intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
-				static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
+				static_cast<unsigned>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
 		break;
 	case DMA:
 		{
@@ -203,7 +208,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
 			
 			ackDmaReq(&intreq);
 
-			if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
+			if ((static_cast<unsigned>(dmaDest) + length) & 0x10000) {
 				length = 0x10000 - dmaDest;
 				ioamhram[0x155] |= 0x80;
 			}
@@ -214,7 +219,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
 				dmaLength = 0;
 
 			{
-				unsigned long lOamDmaUpdate = lastOamDmaUpdate;
+				unsigned lOamDmaUpdate = lastOamDmaUpdate;
 				lastOamDmaUpdate = DISABLED_TIME;
 
 				while (length--) {
@@ -295,7 +300,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
 	return cycleCounter;
 }
 
-unsigned long Memory::stop(unsigned long cycleCounter) {
+unsigned Memory::stop(unsigned cycleCounter) {
 	cycleCounter += 4 << isDoubleSpeed();
 	
 	if (ioamhram[0x14D] & isCgb()) {
@@ -318,31 +323,31 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
 	return cycleCounter;
 }
 
-static void decCycles(unsigned long &counter, const unsigned long dec) {
+static void decCycles(unsigned &counter, const unsigned dec) {
 	if (counter != DISABLED_TIME)
 		counter -= dec;
 }
 
-void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
+void Memory::decEventCycles(const MemEventId eventId, const unsigned dec) {
 	if (intreq.eventTime(eventId) != DISABLED_TIME)
 		intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
 }
 
-unsigned long Memory::resetCounters(unsigned long cycleCounter) {
+unsigned Memory::resetCounters(unsigned cycleCounter) {
 	if (lastOamDmaUpdate != DISABLED_TIME)
 		updateOamDma(cycleCounter);
 
 	updateIrqs(cycleCounter);
 
-	const unsigned long oldCC = cycleCounter;
+	const unsigned oldCC = cycleCounter;
 
 	{
-		const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
+		const unsigned divinc = (cycleCounter - divLastUpdate) >> 8;
 		ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
 		divLastUpdate += divinc << 8;
 	}
 
-	const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
+	const unsigned dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
 
 	decCycles(divLastUpdate, dec);
 	decCycles(lastOamDmaUpdate, dec);
@@ -381,7 +386,7 @@ void Memory::updateInput() {
 		ioamhram[0x100] &= button;
 }
 
-void Memory::updateOamDma(const unsigned long cycleCounter) {
+void Memory::updateOamDma(const unsigned cycleCounter) {
 	const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
 	unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
 
@@ -429,17 +434,17 @@ const unsigned char * Memory::oamDmaSrcPtr() const {
 	return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
 }
 
-void Memory::startOamDma(const unsigned long cycleCounter) {
+void Memory::startOamDma(const unsigned cycleCounter) {
 	display.oamChange(cart.rdisabledRam(), cycleCounter);
 }
 
-void Memory::endOamDma(const unsigned long cycleCounter) {
+void Memory::endOamDma(const unsigned cycleCounter) {
 	oamDmaPos = 0xFE;
 	cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
 	display.oamChange(ioamhram, cycleCounter);
 }
 
-unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
+unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned cycleCounter) {
 	if (lastOamDmaUpdate != DISABLED_TIME)
 		updateOamDma(cycleCounter);
 
@@ -453,7 +458,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
 		break;
 	case 0x04:
 		{
-			const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
+			const unsigned divcycles = (cycleCounter - divLastUpdate) >> 8;
 			ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
 			divLastUpdate += divcycles << 8;
 		}
@@ -532,7 +537,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add
 	return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
 }
 
-unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
+unsigned Memory::nontrivial_read(const unsigned P, const unsigned cycleCounter) {
 	if (P < 0xFF80) {
 		if (lastOamDmaUpdate != DISABLED_TIME) {
 			updateOamDma(cycleCounter);
@@ -571,7 +576,7 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
 	return ioamhram[P - 0xFE00];
 }
 
-void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
+void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned cycleCounter) {
 	if (lastOamDmaUpdate != DISABLED_TIME)
 		updateOamDma(cycleCounter);
 
@@ -588,7 +593,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
 		serialCnt = 8;
 		intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
 				? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
-				: static_cast<unsigned long>(DISABLED_TIME));
+				: static_cast<unsigned>(DISABLED_TIME));
 
 		data |= 0x7E - isCgb() * 2;
 		break;
@@ -949,7 +954,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
 	ioamhram[P - 0xFE00] = data;
 }
 
-void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
 	if (lastOamDmaUpdate != DISABLED_TIME) {
 		updateOamDma(cycleCounter);
 		
@@ -986,24 +991,59 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig
 		ioamhram[P - 0xFE00] = data;
 }
 
+void Memory::postLoadRom()
+{
+	sound.init(cart.isCgb());
+	display.reset(ioamhram, cart.isCgb());
+	interrupter.setGameShark(std::string());
+}
+
 int Memory::loadROM(const std::string &romfile, const bool forceDmg, const bool multicartCompat) {
 	if (const int fail = cart.loadROM(romfile, forceDmg, multicartCompat))
 		return fail;
 
-	sound.init(cart.isCgb());
-	display.reset(ioamhram, cart.isCgb());
-	interrupter.setGameShark(std::string());
+	postLoadRom();
 
 	return 0;
 }
 
-unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
+int Memory::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
+	if (const int fail = cart.loadROM(image, isize, forceDmg, multicartCompat))
+		return fail;
+
+	postLoadRom();
+
+	return 0;
+}
+
+unsigned Memory::fillSoundBuffer(const unsigned cycleCounter) {
 	sound.generate_samples(cycleCounter, isDoubleSpeed());
 	return sound.fillBuffer();
 }
 
-void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
+void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) {
 	display.setDmgPaletteColor(palNum, colorNum, rgb32);
 }
 
+void Memory::loadOrSave(loadsave& state)
+{
+	state(ioamhram, 0x200);
+	state(vram, 0x2000 * 2);
+	state(vrambank, vram);
+	//Don't save getInput, it has no state.
+	state(divLastUpdate);
+	state(lastOamDmaUpdate);
+	intreq.loadOrSave(state);
+	cart.loadOrSave(state);
+	tima.loadOrSave(state);
+	display.loadOrSave(state);
+	sound.loadOrSave(state);
+	interrupter.loadOrSave(state);
+	state(dmaSource);
+	state(dmaDestination);
+	state(oamDmaPos);
+	state(serialCnt);
+	state(blanklcd);
+}
+
 }
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index 9163367..8e4c4ee 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -19,6 +19,10 @@
 #ifndef MEMORY_H
 #define MEMORY_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "mem/cartridge.h"
 #include "video.h"
 #include "sound.h"
@@ -35,8 +39,8 @@ class Memory {
 	unsigned char *vrambank;
 	
 	InputGetter *getInput;
-	unsigned long divLastUpdate;
-	unsigned long lastOamDmaUpdate;
+	unsigned divLastUpdate;
+	unsigned lastOamDmaUpdate;
 	
 	InterruptRequester intreq;
 	Cartridge cart;
@@ -51,34 +55,40 @@ class Memory {
 	unsigned char serialCnt;
 	bool blanklcd;
 
+	uint_least32_t* videoBuf_;
+	unsigned pitch_;
+
 	void updateInput();
-	void decEventCycles(MemEventId eventId, unsigned long dec);
+	void decEventCycles(MemEventId eventId, unsigned dec);
 
 	void oamDmaInitSetup();
-	void updateOamDma(unsigned long cycleCounter);
-	void startOamDma(unsigned long cycleCounter);
-	void endOamDma(unsigned long cycleCounter);
+	void updateOamDma(unsigned cycleCounter);
+	void startOamDma(unsigned cycleCounter);
+	void endOamDma(unsigned cycleCounter);
 	const unsigned char * oamDmaSrcPtr() const;
 	
-	unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
-	unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
-	void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
-	void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
+	unsigned nontrivial_ff_read(unsigned P, unsigned cycleCounter);
+	unsigned nontrivial_read(unsigned P, unsigned cycleCounter);
+	void nontrivial_ff_write(unsigned P, unsigned data, unsigned cycleCounter);
+	void nontrivial_write(unsigned P, unsigned data, unsigned cycleCounter);
 	
-	void updateSerial(unsigned long cc);
-	void updateTimaIrq(unsigned long cc);
-	void updateIrqs(unsigned long cc);
+	void updateSerial(unsigned cc);
+	void updateTimaIrq(unsigned cc);
+	void updateIrqs(unsigned cc);
 	
 	bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
 
+	void postLoadRom();
 public:
-	explicit Memory(const Interrupter &interrupter);
+	explicit Memory(const Interrupter &interrupter, time_t (**_getCurrentTime)());
 	
 	bool loaded() const { return cart.loaded(); }
 	const char * romTitle() const { return cart.romTitle(); }
 
+	void loadOrSave(loadsave& state);
+
 	void setStatePtrs(SaveState &state);
-	unsigned long saveState(SaveState &state, unsigned long cc);
+	unsigned saveState(SaveState &state, unsigned cc);
 	void loadState(const SaveState &state/*, unsigned long oldCc*/);
 	void loadSavedata() { cart.loadSavedata(); }
 	void saveSavedata() { cart.saveSavedata(); }
@@ -88,67 +98,77 @@ public:
 		display.setOsdElement(osdElement);
 	}
 
-	unsigned long stop(unsigned long cycleCounter);
+	unsigned stop(unsigned cycleCounter);
 	bool isCgb() const { return display.isCgb(); }
 	bool ime() const { return intreq.ime(); }
 	bool halted() const { return intreq.halted(); }
-	unsigned long nextEventTime() const { return intreq.minEventTime(); }
+	unsigned nextEventTime() const { return intreq.minEventTime(); }
 	
 	bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
 	
-	long cyclesSinceBlit(const unsigned long cc) const {
-		return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
+	signed cyclesSinceBlit(const unsigned cc) const {
+		return cc < intreq.eventTime(BLIT) ? -1 : static_cast<signed>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
 	}
 
 	void halt() { intreq.halt(); }
-	void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
+	void ei(unsigned cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
 
 	void di() { intreq.di(); }
 
-	unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
+	unsigned ff_read(const unsigned P, const unsigned cycleCounter) {
 		return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
 	}
 
-	unsigned read(const unsigned P, const unsigned long cycleCounter) {
+	unsigned read(const unsigned P, const unsigned cycleCounter) {
 		return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
 	}
 
-	void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+	void write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
 		if (cart.wmem(P >> 12)) {
 			cart.wmem(P >> 12)[P] = data;
 		} else
 			nontrivial_write(P, data, cycleCounter);
 	}
 	
-	void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+	void ff_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
 		if (P - 0xFF80u < 0x7Fu) {
 			ioamhram[P - 0xFE00] = data;
 		} else
 			nontrivial_ff_write(P, data, cycleCounter);
 	}
 
-	unsigned long event(unsigned long cycleCounter);
-	unsigned long resetCounters(unsigned long cycleCounter);
+	unsigned event(unsigned cycleCounter);
+	unsigned resetCounters(unsigned cycleCounter);
 
 	int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
+	int loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
 	void setSaveDir(const std::string &dir) { cart.setSaveDir(dir); }
 
 	void setInputGetter(InputGetter *getInput) {
 		this->getInput = getInput;
 	}
 
-	void setEndtime(unsigned long cc, unsigned long inc);
+	void setEndtime(unsigned cc, unsigned inc);
 	
 	void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
-	unsigned fillSoundBuffer(unsigned long cc);
+	unsigned fillSoundBuffer(unsigned cc);
 	
 	void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
-		display.setVideoBuffer(videoBuf, pitch);
+		videoBuf_ = videoBuf;
+		pitch_ = pitch;
 	}
 	
-	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
+	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
 	void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
 	void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
+
+	void setRtcBase(time_t time) { cart.setRtcBase(time); }
+	time_t getRtcBase() { return cart.getRtcBase(); }
+	std::pair<unsigned char*, size_t> getWorkRam() { return cart.getWorkRam(); }
+	std::pair<unsigned char*, size_t> getSaveRam() { return cart.getSaveRam(); }
+	std::pair<unsigned char*, size_t> getIoRam() { return std::make_pair(ioamhram, sizeof(ioamhram)); }
+	std::pair<unsigned char*, size_t> getVideoRam() { return std::make_pair(vram, sizeof(vram)); };
+
 };
 
 }
diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h
index e745007..7a2bdf8 100644
--- a/libgambatte/src/minkeeper.h
+++ b/libgambatte/src/minkeeper.h
@@ -19,7 +19,12 @@
 #ifndef MINKEEPER_H
 #define MINKEEPER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include <algorithm>
+#include "loadsave.h"
 
 namespace MinKeeperUtil {
 template<int n> struct CeiledLog2 { enum { RESULT = 1 + CeiledLog2<(n + 1) / 2>::RESULT }; };
@@ -74,35 +79,42 @@ class MinKeeper {
 	};
 	
 	
-	unsigned long values[ids];
-	unsigned long minValue_;
+	unsigned values[ids];
+	unsigned minValue_;
 	void (*updateValueLut[Num<LEVELS-1>::RESULT])(MinKeeper<ids>*);
 	int a[Sum<LEVELS>::RESULT];
 	
 	template<int id> static void updateValue(MinKeeper<ids> *s);
 	
 public:
-	explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF);
-	
+	explicit MinKeeper(unsigned initValue = 0xFFFFFFFFULL);
+
+	void loadOrSave(gambatte::loadsave& state) {
+		state(values, ids);
+		state(minValue_);
+		//updateValueLut is constant for our purposes.
+		state(a, Sum<LEVELS>::RESULT);
+	}
+
 	int min() const { return a[0]; }
-	unsigned long minValue() const { return minValue_; }
+	unsigned minValue() const { return minValue_; }
 	
 	template<int id>
-	void setValue(const unsigned long cnt) {
+	void setValue(const unsigned cnt) {
 		values[id] = cnt;
 		updateValue<id / 2>(this);
 	}
 	
-	void setValue(const int id, const unsigned long cnt) {
+	void setValue(const int id, const unsigned cnt) {
 		values[id] = cnt;
 		updateValueLut[id >> 1](this);
 	}
 	
-	unsigned long value(const int id) const { return values[id]; }
+	unsigned value(const int id) const { return values[id]; }
 };
 
 template<int ids>
-MinKeeper<ids>::MinKeeper(const unsigned long initValue) {
+MinKeeper<ids>::MinKeeper(const unsigned initValue) {
 	std::fill(values, values + ids, initValue);
 	
 	for (int i = 0; i < Num<LEVELS-1>::RESULT; ++i) {
diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h
index 1916c90..d85220d 100644
--- a/libgambatte/src/savestate.h
+++ b/libgambatte/src/savestate.h
@@ -19,6 +19,12 @@
 #ifndef SAVESTATE_H
 #define SAVESTATE_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include <ctime>
+
 namespace gambatte {
 
 class SaverList;
@@ -27,20 +33,20 @@ struct SaveState {
 	template<typename T>
 	class Ptr {
 		T *ptr;
-		unsigned long sz;
+		unsigned sz;
 		
 	public:
 		Ptr() : ptr(0), sz(0) {}
 		const T* get() const { return ptr; }
-		unsigned long getSz() const { return sz; }
-		void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
+		unsigned getSz() const { return sz; }
+		void set(T *ptr, const unsigned sz) { this->ptr = ptr; this->sz = sz; }
 		
 		friend class SaverList;
-		friend void setInitState(SaveState &, bool, bool);
+		friend void setInitState(SaveState &, bool, bool, time_t);
 	};
 
 	struct CPU {
-		unsigned long cycleCounter;
+		unsigned cycleCounter;
 		unsigned short PC;
 		unsigned short SP;
 		unsigned char A;
@@ -59,13 +65,13 @@ struct SaveState {
 		Ptr<unsigned char> sram;
 		Ptr<unsigned char> wram;
 		Ptr<unsigned char> ioamhram;
-		unsigned long divLastUpdate;
-		unsigned long timaLastUpdate;
-		unsigned long tmatime;
-		unsigned long nextSerialtime;
-		unsigned long lastOamDmaUpdate;
-		unsigned long minIntTime;
-		unsigned long unhaltTime;
+		unsigned divLastUpdate;
+		unsigned timaLastUpdate;
+		unsigned tmatime;
+		unsigned nextSerialtime;
+		unsigned lastOamDmaUpdate;
+		unsigned minIntTime;
+		unsigned unhaltTime;
 		unsigned short rombank;
 		unsigned short dmaSource;
 		unsigned short dmaDestination;
@@ -85,8 +91,8 @@ struct SaveState {
 		Ptr<unsigned char> oamReaderBuf;
 		Ptr<bool> oamReaderSzbuf;
 		
-		unsigned long videoCycles;
-		unsigned long enableDisplayM0Time;
+		unsigned videoCycles;
+		unsigned enableDisplayM0Time;
 		unsigned short lastM0Time;
 		unsigned short nextM0Irq;
 		unsigned short tileword;
@@ -115,24 +121,24 @@ struct SaveState {
 	
 	struct SPU {
 		struct Duty {
-			unsigned long nextPosUpdate;
+			unsigned nextPosUpdate;
 			unsigned char nr3;
 			unsigned char pos;
 		};
 		
 		struct Env {
-			unsigned long counter;
+			unsigned counter;
 			unsigned char volume;
 		};
 		
 		struct LCounter {
-			unsigned long counter;
+			unsigned counter;
 			unsigned short lengthCounter;
 		};
 		
 		struct {
 			struct {
-				unsigned long counter;
+				unsigned counter;
 				unsigned short shadow;
 				unsigned char nr0;
 				bool negging;
@@ -155,8 +161,8 @@ struct SaveState {
 		struct {
 			Ptr<unsigned char> waveRam;
 			LCounter lcounter;
-			unsigned long waveCounter;
-			unsigned long lastReadTime;
+			unsigned waveCounter;
+			unsigned lastReadTime;
 			unsigned char nr3;
 			unsigned char nr4;
 			unsigned char wavePos;
@@ -166,7 +172,7 @@ struct SaveState {
 		
 		struct {
 			struct {
-				unsigned long counter;
+				unsigned counter;
 				unsigned short reg;
 			} lfsr;
 			Env env;
@@ -175,12 +181,12 @@ struct SaveState {
 			bool master;
 		} ch4;
 		
-		unsigned long cycleCounter;
+		unsigned cycleCounter;
 	} spu;
 	
 	struct RTC {
-		unsigned long baseTime;
-		unsigned long haltTime;
+		unsigned baseTime;
+		unsigned haltTime;
 		unsigned char dataDh;
 		unsigned char dataDl;
 		unsigned char dataH;
diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp
index 14d994e..5bf1e81 100644
--- a/libgambatte/src/sound.cpp
+++ b/libgambatte/src/sound.cpp
@@ -21,6 +21,10 @@
 #include <cstring>
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 /*
 	Frame Sequencer
 
@@ -89,7 +93,7 @@ void PSG::loadState(const SaveState &state) {
 	enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
 }
 
-void PSG::accumulate_channels(const unsigned long cycles) {
+void PSG::accumulate_channels(const unsigned cycles) {
 	uint_least32_t *const buf = buffer + bufferPos;
 	
 	std::memset(buf, 0, cycles * sizeof(uint_least32_t));
@@ -99,17 +103,16 @@ void PSG::accumulate_channels(const unsigned long cycles) {
 	ch4.update(buf, soVol, cycles);
 }
 
-void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
-	const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
+void PSG::generate_samples(const unsigned cycleCounter, const unsigned doubleSpeed) {
+	const unsigned cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
 	lastUpdate += cycles << (1 + doubleSpeed);
-
 	if (cycles)
 		accumulate_channels(cycles);
 	
 	bufferPos += cycles;
 }
 
-void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
+void PSG::resetCounter(const unsigned newCc, const unsigned oldCc, const unsigned doubleSpeed) {
 	generate_samples(oldCc, doubleSpeed);
 	lastUpdate = newCc - (oldCc - lastUpdate);
 }
@@ -155,11 +158,11 @@ unsigned PSG::fillBuffer() {
 }
 
 #ifdef WORDS_BIGENDIAN
-static const unsigned long so1Mul = 0x00000001;
-static const unsigned long so2Mul = 0x00010000;
+static const unsigned so1Mul = 0x00000001;
+static const unsigned so2Mul = 0x00010000;
 #else
-static const unsigned long so1Mul = 0x00010000;
-static const unsigned long so2Mul = 0x00000001;
+static const unsigned so1Mul = 0x00010000;
+static const unsigned so2Mul = 0x00000001;
 #endif
 
 void PSG::set_so_volume(const unsigned nr50) {
@@ -167,7 +170,7 @@ void PSG::set_so_volume(const unsigned nr50) {
 }
 
 void PSG::map_so(const unsigned nr51) {
-	const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
+	const unsigned tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
 	
 	ch1.setSo((tmp      & 0x00010001) * 0xFFFF);
 	ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
@@ -179,4 +182,17 @@ unsigned PSG::getStatus() const {
 	return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
 }
 
+void PSG::loadOrSave(loadsave& state)
+{
+	ch1.loadOrSave(state);
+	ch2.loadOrSave(state);
+	ch3.loadOrSave(state);
+	ch4.loadOrSave(state);
+	state(lastUpdate);
+	state(soVol);
+	state(rsum);
+	state(bufferPos);
+	state(enabled);
+}
+
 }
diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h
index 71d5cdc..7fd3f82 100644
--- a/libgambatte/src/sound.h
+++ b/libgambatte/src/sound.h
@@ -19,10 +19,15 @@
 #ifndef SOUND_H
 #define SOUND_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "sound/channel1.h"
 #include "sound/channel2.h"
 #include "sound/channel3.h"
 #include "sound/channel4.h"
+#include "loadsave.h"
 
 namespace gambatte {
 
@@ -34,8 +39,8 @@ class PSG {
 		
 	uint_least32_t *buffer;
 	
-	unsigned long lastUpdate;
-	unsigned long soVol;
+	unsigned lastUpdate;
+	unsigned soVol;
 	
 	uint_least32_t rsum;
 	
@@ -43,7 +48,7 @@ class PSG {
 	
 	bool enabled;
 
-	void accumulate_channels(unsigned long cycles);
+	void accumulate_channels(unsigned cycles);
 
 public:
 	PSG();
@@ -53,8 +58,10 @@ public:
 	void saveState(SaveState &state);
 	void loadState(const SaveState &state);
 
-	void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
-	void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
+	void loadOrSave(loadsave& state);
+
+	void generate_samples(unsigned cycleCounter, unsigned doubleSpeed);
+	void resetCounter(unsigned newCc, unsigned oldCc, unsigned doubleSpeed);
 	unsigned fillBuffer();
 	void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
 	
diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp
index 82e623a..e282d0a 100644
--- a/libgambatte/src/sound/channel1.cpp
+++ b/libgambatte/src/sound/channel1.cpp
@@ -20,6 +20,9 @@
 #include "../savestate.h"
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 
@@ -47,7 +50,7 @@ unsigned Channel1::SweepUnit::calcFreq() {
 }
 
 void Channel1::SweepUnit::event() {
-	const unsigned long period = nr0 >> 4 & 0x07;
+	const unsigned period = nr0 >> 4 & 0x07;
 	
 	if (period) {
 		const unsigned freq = calcFreq();
@@ -70,7 +73,7 @@ void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
 	nr0 = newNr0;
 }
 
-void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
+void Channel1::SweepUnit::nr4Init(const unsigned cc) {
 	negging = false;
 	shadow = dutyUnit.getFreq();
 	
@@ -172,7 +175,7 @@ void Channel1::setNr4(const unsigned data) {
 	setEvent();
 }
 
-void Channel1::setSo(const unsigned long soMask) {
+void Channel1::setSo(const unsigned soMask) {
 	this->soMask = soMask;
 	staticOutputTest(cycleCounter);
 	setEvent();
@@ -215,15 +218,15 @@ void Channel1::loadState(const SaveState &state) {
 	master = state.spu.ch1.master;
 }
 
-void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
-	const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
-	const unsigned long outLow = outBase * (0 - 15ul);
-	const unsigned long endCycles = cycleCounter + cycles;
+void Channel1::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+	const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+	const unsigned outLow = outBase * (0 - 15ul);
+	const unsigned endCycles = cycleCounter + cycles;
 	
 	for (;;) {
-		const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
-		const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
-		unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
+		const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
+		const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+		unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
 		
 		while (dutyUnit.getCounter() <= nextMajorEvent) {
 			*buf = out - prevOut;
@@ -259,4 +262,25 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
 	}
 }
 
+void Channel1::loadOrSave(loadsave& state) {
+	//disableMaster has no state.
+	lengthCounter.loadOrSave(state);
+	dutyUnit.loadOrSave(state);
+	envelopeUnit.loadOrSave(state);
+	sweepUnit.loadOrSave(state);
+
+	state.startEnumeration();
+	state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+	state.enumerate<SoundUnit*>(nextEventUnit, &sweepUnit, 1);
+	state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+	state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 3);
+	state.endEnumeration();
+
+	state(cycleCounter);
+	state(soMask);
+	state(prevOut);
+	state(nr4);
+	state(master);
+}
+
 }
diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h
index 9c77277..1f4eba9 100644
--- a/libgambatte/src/sound/channel1.h
+++ b/libgambatte/src/sound/channel1.h
@@ -19,12 +19,17 @@
 #ifndef SOUND_CHANNEL1_H
 #define SOUND_CHANNEL1_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "gbint.h"
 #include "master_disabler.h"
 #include "length_counter.h"
 #include "duty_unit.h"
 #include "envelope_unit.h"
 #include "static_output_tester.h"
+#include "loadsave.h"
 
 namespace gambatte {
 
@@ -44,10 +49,16 @@ class Channel1 {
 		SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
 		void event();
 		void nr0Change(unsigned newNr0);
-		void nr4Init(unsigned long cycleCounter);
+		void nr4Init(unsigned cycleCounter);
 		void reset();
 		void saveState(SaveState &state) const;
 		void loadState(const SaveState &state);
+		void loadOrSave(loadsave& state) {
+			loadOrSave2(state);
+			state(shadow);
+			state(nr0);
+			state(negging);
+		}
 	};
 	
 	friend class StaticOutputTester<Channel1,DutyUnit>;
@@ -61,9 +72,9 @@ class Channel1 {
 	
 	SoundUnit *nextEventUnit;
 	
-	unsigned long cycleCounter;
-	unsigned long soMask;
-	unsigned long prevOut;
+	unsigned cycleCounter;
+	unsigned soMask;
+	unsigned prevOut;
 	
 	unsigned char nr4;
 	bool master;
@@ -78,15 +89,17 @@ public:
 	void setNr3(unsigned data);
 	void setNr4(unsigned data);
 	
-	void setSo(unsigned long soMask);
+	void setSo(unsigned soMask);
 	bool isActive() const { return master; }
 	
-	void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+	void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
 	
 	void reset();
 	void init(bool cgb);
 	void saveState(SaveState &state);
 	void loadState(const SaveState &state);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp
index 91e15ee..16905bd 100644
--- a/libgambatte/src/sound/channel2.cpp
+++ b/libgambatte/src/sound/channel2.cpp
@@ -19,6 +19,10 @@
 #include "channel2.h"
 #include "../savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 Channel2::Channel2() :
@@ -80,7 +84,7 @@ void Channel2::setNr4(const unsigned data) {
 	setEvent();
 }
 
-void Channel2::setSo(const unsigned long soMask) {
+void Channel2::setSo(const unsigned soMask) {
 	this->soMask = soMask;
 	staticOutputTest(cycleCounter);
 	setEvent();
@@ -119,16 +123,15 @@ void Channel2::loadState(const SaveState &state) {
 	master = state.spu.ch2.master;
 }
 
-void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
-	const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
-	const unsigned long outLow = outBase * (0 - 15ul);
-	const unsigned long endCycles = cycleCounter + cycles;
+void Channel2::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+	const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+	const unsigned outLow = outBase * (0 - 15ul);
+	const unsigned endCycles = cycleCounter + cycles;
 	
 	for (;;) {
-		const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
-		const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
-		unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
-		
+		const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
+		const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+		unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
 		while (dutyUnit.getCounter() <= nextMajorEvent) {
 			*buf += out - prevOut;
 			prevOut = out;
@@ -162,4 +165,24 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
 	}
 }
 
+void Channel2::loadOrSave(loadsave& state)
+{
+	//disableMaster has no state.
+	lengthCounter.loadOrSave(state);
+	dutyUnit.loadOrSave(state);
+	envelopeUnit.loadOrSave(state);
+
+	state.startEnumeration();
+	state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+	state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
+	state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+	state.endEnumeration();
+
+	state(cycleCounter);
+	state(soMask);
+	state(prevOut);
+	state(nr4);
+	state(master);
+}
+
 }
diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h
index d6ba821..b654da9 100644
--- a/libgambatte/src/sound/channel2.h
+++ b/libgambatte/src/sound/channel2.h
@@ -24,6 +24,11 @@
 #include "duty_unit.h"
 #include "envelope_unit.h"
 #include "static_output_tester.h"
+#include "loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 
@@ -40,9 +45,9 @@ class Channel2 {
 	
 	SoundUnit *nextEventUnit;
 	
-	unsigned long cycleCounter;
-	unsigned long soMask;
-	unsigned long prevOut;
+	unsigned cycleCounter;
+	unsigned soMask;
+	unsigned prevOut;
 	
 	unsigned char nr4;
 	bool master;
@@ -56,16 +61,18 @@ public:
 	void setNr3(unsigned data);
 	void setNr4(unsigned data);
 	
-	void setSo(unsigned long soMask);
+	void setSo(unsigned soMask);
 	// void deactivate() { disableMaster(); setEvent(); }
 	bool isActive() const { return master; }
 	
-	void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+	void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
 	
 	void reset();
 	void init(bool cgb);
 	void saveState(SaveState &state);
 	void loadState(const SaveState &state);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp
index a1bde94..a3f3aa4 100644
--- a/libgambatte/src/sound/channel3.cpp
+++ b/libgambatte/src/sound/channel3.cpp
@@ -21,6 +21,10 @@
 #include <cstring>
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
 	return 0x800 - ((nr4 << 8 & 0x700) | nr3);
 }
@@ -80,7 +84,7 @@ void Channel3::setNr4(const unsigned data) {
 	}
 }
 
-void Channel3::setSo(const unsigned long soMask) {
+void Channel3::setSo(const unsigned soMask) {
 	this->soMask = soMask;
 }
 
@@ -128,10 +132,10 @@ void Channel3::loadState(const SaveState &state) {
 	setNr2(state.mem.ioamhram.get()[0x11C]);
 }
 
-void Channel3::updateWaveCounter(const unsigned long cc) {
+void Channel3::updateWaveCounter(const unsigned cc) {
 	if (cc >= waveCounter) {
 		const unsigned period = toPeriod(nr3, nr4);
-		const unsigned long periods = (cc - waveCounter) / period;
+		const unsigned periods = (cc - waveCounter) / period;
 
 		lastReadTime = waveCounter + periods * period;
 		waveCounter = lastReadTime + period;
@@ -143,15 +147,15 @@ void Channel3::updateWaveCounter(const unsigned long cc) {
 	}
 }
 
-void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
-	const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
+void Channel3::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+	const unsigned outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
 	
 	if (outBase && rShift != 4) {
-		const unsigned long endCycles = cycleCounter + cycles;
+		const unsigned endCycles = cycleCounter + cycles;
 		
 		for (;;) {
-			const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
-			unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
+			const unsigned nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
+			unsigned out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
 		
 			while (waveCounter <= nextMajorEvent) {
 				*buf += out - prevOut;
@@ -181,7 +185,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
 		}
 	} else {
 		if (outBase) {
-			const unsigned long out = outBase * (0 - 15ul);
+			const unsigned out = outBase * (0 - 15ul);
 			
 			*buf += out - prevOut;
 			prevOut = out;
@@ -208,4 +212,23 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
 	}
 }
 
+void Channel3::loadOrSave(loadsave& state) {
+	state(waveRam, 0x10);
+	//disableMaster has no saveable state.
+	lengthCounter.loadOrSave(state);
+	state(cycleCounter);
+	state(soMask);
+	state(prevOut);
+	state(waveCounter);
+	state(lastReadTime);
+	state(nr0);
+	state(nr3);
+	state(nr4);
+	state(wavePos);
+	state(rShift);
+	state(sampleBuf);
+	state(master);
+	state(cgb);
+}
+
 }
diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h
index e80ec68..8fe0fe3 100644
--- a/libgambatte/src/sound/channel3.h
+++ b/libgambatte/src/sound/channel3.h
@@ -19,9 +19,14 @@
 #ifndef SOUND_CHANNEL3_H
 #define SOUND_CHANNEL3_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "gbint.h"
 #include "master_disabler.h"
 #include "length_counter.h"
+#include "loadsave.h"
 
 namespace gambatte {
 
@@ -29,10 +34,10 @@ struct SaveState;
 
 class Channel3 {
 	class Ch3MasterDisabler : public MasterDisabler {
-		unsigned long &waveCounter;
+		unsigned &waveCounter;
 		
 	public:
-		Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
+		Ch3MasterDisabler(bool &m, unsigned &wC) : MasterDisabler(m), waveCounter(wC) {}
 		void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
 	};
 	
@@ -41,11 +46,11 @@ class Channel3 {
 	Ch3MasterDisabler disableMaster;
 	LengthCounter lengthCounter;
 	
-	unsigned long cycleCounter;
-	unsigned long soMask;
-	unsigned long prevOut;
-	unsigned long waveCounter;
-	unsigned long lastReadTime;
+	unsigned cycleCounter;
+	unsigned soMask;
+	unsigned prevOut;
+	unsigned waveCounter;
+	unsigned lastReadTime;
 	
 	unsigned char nr0;
 	unsigned char nr3;
@@ -57,7 +62,7 @@ class Channel3 {
 	bool master;
 	bool cgb;
 	
-	void updateWaveCounter(unsigned long cc);
+	void updateWaveCounter(unsigned cc);
 	
 public:
 	Channel3();
@@ -72,8 +77,8 @@ public:
 	void setNr2(unsigned data);
 	void setNr3(unsigned data) { nr3 = data; }
 	void setNr4(unsigned data);
-	void setSo(unsigned long soMask);
-	void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+	void setSo(unsigned soMask);
+	void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
 	
 	unsigned waveRamRead(unsigned index) const {
 		if (master) {
@@ -96,6 +101,8 @@ public:
 		
 		waveRam[index] = data;
 	}
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp
index 35c3c00..9d89db7 100644
--- a/libgambatte/src/sound/channel4.cpp
+++ b/libgambatte/src/sound/channel4.cpp
@@ -20,7 +20,11 @@
 #include "../savestate.h"
 #include <algorithm>
 
-static unsigned long toPeriod(const unsigned nr3) {
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+static unsigned toPeriod(const unsigned nr3) {
 	unsigned s = (nr3 >> 4) + 3;
 	unsigned r = nr3 & 7;
 	
@@ -41,15 +45,15 @@ nr3(0),
 master(false)
 {}
 
-void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
+void Channel4::Lfsr::updateBackupCounter(const unsigned cc) {
 	/*if (backupCounter <= cc) {
 		const unsigned long period = toPeriod(nr3);
 		backupCounter = cc - (cc - backupCounter) % period + period;
 	}*/
 	
 	if (backupCounter <= cc) {
-		const unsigned long period = toPeriod(nr3);
-		unsigned long periods = (cc - backupCounter) / period + 1;
+		const unsigned period = toPeriod(nr3);
+		unsigned periods = (cc - backupCounter) / period + 1;
 		
 		backupCounter += periods * period;
 		
@@ -75,7 +79,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
 	}
 }
 
-void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
+void Channel4::Lfsr::reviveCounter(const unsigned cc) {
 	updateBackupCounter(cc);
 	counter = backupCounter;
 }
@@ -121,7 +125,7 @@ inline void Channel4::Lfsr::event() {
 	counter += period * nextStateDistance[reg & 0x3F];*/
 }
 
-void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
+void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned cc) {
 	updateBackupCounter(cc);
 	nr3 = newNr3;
 	
@@ -129,7 +133,7 @@ void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
 // 		counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
 }
 
-void Channel4::Lfsr::nr4Init(unsigned long cc) {
+void Channel4::Lfsr::nr4Init(unsigned cc) {
 	disableMaster();
 	updateBackupCounter(cc);
 	master = true;
@@ -138,19 +142,19 @@ void Channel4::Lfsr::nr4Init(unsigned long cc) {
 // 	counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
 }
 
-void Channel4::Lfsr::reset(const unsigned long cc) {
+void Channel4::Lfsr::reset(const unsigned cc) {
 	nr3 = 0;
 	disableMaster();
 	backupCounter = cc + toPeriod(nr3);
 }
 
-void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
+void Channel4::Lfsr::resetCounters(const unsigned oldCc) {
 	updateBackupCounter(oldCc);
 	backupCounter -= COUNTER_MAX;
 	SoundUnit::resetCounters(oldCc);
 }
 
-void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
+void Channel4::Lfsr::saveState(SaveState &state, const unsigned cc) {
 	updateBackupCounter(cc);
 	state.spu.ch4.lfsr.counter = backupCounter;
 	state.spu.ch4.lfsr.reg = reg;
@@ -219,7 +223,7 @@ void Channel4::setNr4(const unsigned data) {
 	setEvent();
 }
 
-void Channel4::setSo(const unsigned long soMask) {
+void Channel4::setSo(const unsigned soMask) {
 	this->soMask = soMask;
 	staticOutputTest(cycleCounter);
 	setEvent();
@@ -258,15 +262,15 @@ void Channel4::loadState(const SaveState &state) {
 	master = state.spu.ch4.master;
 }
 
-void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
-	const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
-	const unsigned long outLow = outBase * (0 - 15ul);
-	const unsigned long endCycles = cycleCounter + cycles;
+void Channel4::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+	const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+	const unsigned outLow = outBase * (0 - 15ul);
+	const unsigned endCycles = cycleCounter + cycles;
 	
 	for (;;) {
-		const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
-		const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
-		unsigned long out = lfsr.isHighState() ? outHigh : outLow;
+		const unsigned outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
+		const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+		unsigned out = lfsr.isHighState() ? outHigh : outLow;
 		
 		while (lfsr.getCounter() <= nextMajorEvent) {
 			*buf += out - prevOut;
@@ -301,4 +305,23 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
 	}
 }
 
+void Channel4::loadOrSave(loadsave& state) {
+	//DisableMaster has no state.
+	lengthCounter.loadOrSave(state);
+	envelopeUnit.loadOrSave(state);
+	lfsr.loadOrSave(state);
+
+	state.startEnumeration();
+	state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+	state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
+	state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+	state.endEnumeration();
+
+	state(cycleCounter);
+	state(soMask);
+	state(prevOut);
+	state(nr4);
+	state(master);
+}
+
 }
diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h
index 00b0b5a..d52d9f7 100644
--- a/libgambatte/src/sound/channel4.h
+++ b/libgambatte/src/sound/channel4.h
@@ -24,6 +24,11 @@
 #include "length_counter.h"
 #include "envelope_unit.h"
 #include "static_output_tester.h"
+#include "loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 
@@ -31,26 +36,33 @@ struct SaveState;
 
 class Channel4 {
 	class Lfsr : public SoundUnit {
-		unsigned long backupCounter;
+		unsigned backupCounter;
 		unsigned short reg;
 		unsigned char nr3;
 		bool master;
 		
-		void updateBackupCounter(unsigned long cc);
+		void updateBackupCounter(unsigned cc);
 		
 	public:
 		Lfsr();
 		void event();
 		bool isHighState() const { return ~reg & 1; }
-		void nr3Change(unsigned newNr3, unsigned long cc);
-		void nr4Init(unsigned long cc);
-		void reset(unsigned long cc);
-		void saveState(SaveState &state, const unsigned long cc);
+		void nr3Change(unsigned newNr3, unsigned cc);
+		void nr4Init(unsigned cc);
+		void reset(unsigned cc);
+		void saveState(SaveState &state, const unsigned cc);
 		void loadState(const SaveState &state);
-		void resetCounters(unsigned long oldCc);
+		void resetCounters(unsigned oldCc);
 		void disableMaster() { killCounter(); master = false; reg = 0xFF; }
 		void killCounter() { counter = COUNTER_DISABLED; }
-		void reviveCounter(unsigned long cc);
+		void reviveCounter(unsigned cc);
+		void loadOrSave(loadsave& state) {
+			loadOrSave2(state);
+			state(backupCounter);
+			state(reg);
+			state(nr3);
+			state(master);
+		}
 	};
 	
 	class Ch4MasterDisabler : public MasterDisabler {
@@ -70,9 +82,9 @@ class Channel4 {
 	
 	SoundUnit *nextEventUnit;
 	
-	unsigned long cycleCounter;
-	unsigned long soMask;
-	unsigned long prevOut;
+	unsigned cycleCounter;
+	unsigned soMask;
+	unsigned prevOut;
 	
 	unsigned char nr4;
 	bool master;
@@ -86,15 +98,17 @@ public:
 	void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
 	void setNr4(unsigned data);
 	
-	void setSo(unsigned long soMask);
+	void setSo(unsigned soMask);
 	bool isActive() const { return master; }
 	
-	void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+	void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
 	
 	void reset();
 	void init(bool cgb);
 	void saveState(SaveState &state);
 	void loadState(const SaveState &state);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp
index 23b8e52..83cedbf 100644
--- a/libgambatte/src/sound/duty_unit.cpp
+++ b/libgambatte/src/sound/duty_unit.cpp
@@ -19,6 +19,10 @@
 #include "duty_unit.h"
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 static inline bool toOutState(const unsigned duty, const unsigned pos) {
 	static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
 	
@@ -31,9 +35,9 @@ static inline unsigned toPeriod(const unsigned freq) {
 
 namespace gambatte {
 
-void DutyUnit::updatePos(const unsigned long cc) {
+void DutyUnit::updatePos(const unsigned cc) {
 	if (cc >= nextPosUpdate) {
-		const unsigned long inc = (cc - nextPosUpdate) / period + 1;
+		const unsigned inc = (cc - nextPosUpdate) / period + 1;
 		nextPosUpdate += period * inc;
 		pos += inc;
 		pos &= 7;
@@ -59,7 +63,7 @@ void DutyUnit::setCounter() {
 		counter = COUNTER_DISABLED;
 }
 
-void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
+void DutyUnit::setFreq(const unsigned newFreq, const unsigned cc) {
 	updatePos(cc);
 	period = toPeriod(newFreq);
 	setCounter();
@@ -77,17 +81,17 @@ void DutyUnit::event() {
 	counter += inc;
 }
 
-void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
+void DutyUnit::nr1Change(const unsigned newNr1, const unsigned cc) {
 	updatePos(cc);
 	setDuty(newNr1);
 	setCounter();
 }
 
-void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
+void DutyUnit::nr3Change(const unsigned newNr3, const unsigned cc) {
 	setFreq((getFreq() & 0x700) | newNr3, cc);
 }
 
-void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
+void DutyUnit::nr4Change(const unsigned newNr4, const unsigned cc) {
 	setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
 	
 	if (newNr4 & 0x80) {
@@ -112,14 +116,14 @@ void DutyUnit::reset() {
 	setCounter();
 }
 
-void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
+void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned cc) {
 	updatePos(cc);
 	dstate.nextPosUpdate = nextPosUpdate;
 	dstate.nr3 = getFreq() & 0xFF;
 	dstate.pos = pos;
 }
 
-void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
+void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned cc) {
 	nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
 	pos = dstate.pos & 7;
 	setDuty(nr1);
@@ -128,7 +132,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1,
 	setCounter();
 }
 
-void DutyUnit::resetCounters(const unsigned long oldCc) {
+void DutyUnit::resetCounters(const unsigned oldCc) {
 	if (nextPosUpdate == COUNTER_DISABLED)
 		return;
 	
@@ -142,11 +146,21 @@ void DutyUnit::killCounter() {
 	setCounter();
 }
 
-void DutyUnit::reviveCounter(const unsigned long cc) {
+void DutyUnit::reviveCounter(const unsigned cc) {
 	updatePos(cc);
 	high = toOutState(duty, pos);
 	enableEvents = true;
 	setCounter();
 }
 
+void DutyUnit::loadOrSave(loadsave& state) {
+	loadOrSave2(state);
+	state(nextPosUpdate);
+	state(period);
+	state(pos);
+	state(duty);
+	state(high);
+	state(enableEvents);
+}
+
 }
diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h
index f0fc49c..cf7f3b3 100644
--- a/libgambatte/src/sound/duty_unit.h
+++ b/libgambatte/src/sound/duty_unit.h
@@ -19,14 +19,19 @@
 #ifndef DUTY_UNIT_H
 #define DUTY_UNIT_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "sound_unit.h"
 #include "master_disabler.h"
 #include "../savestate.h"
+#include "../loadsave.h"
 
 namespace gambatte {
 
 class DutyUnit : public SoundUnit {
-	unsigned long nextPosUpdate;
+	unsigned nextPosUpdate;
 	unsigned short period;
 	unsigned char pos;
 	unsigned char duty;
@@ -35,25 +40,27 @@ class DutyUnit : public SoundUnit {
 
 	void setCounter();
 	void setDuty(unsigned nr1);
-	void updatePos(unsigned long cc);
+	void updatePos(unsigned cc);
 
 public:
 	DutyUnit();
 	void event();
 	bool isHighState() const { return high; }
-	void nr1Change(unsigned newNr1, unsigned long cc);
-	void nr3Change(unsigned newNr3, unsigned long cc);
-	void nr4Change(unsigned newNr4, unsigned long cc);
+	void nr1Change(unsigned newNr1, unsigned cc);
+	void nr3Change(unsigned newNr3, unsigned cc);
+	void nr4Change(unsigned newNr4, unsigned cc);
 	void reset();
-	void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
-	void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
-	void resetCounters(unsigned long oldCc);
+	void saveState(SaveState::SPU::Duty &dstate, unsigned cc);
+	void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned cc);
+	void resetCounters(unsigned oldCc);
 	void killCounter();
-	void reviveCounter(unsigned long cc);
-	
+	void reviveCounter(unsigned cc);
+
+	void loadOrSave(loadsave& state);
+
 	//intended for use by SweepUnit only.
 	unsigned getFreq() const { return 2048 - (period >> 1); }
-	void setFreq(unsigned newFreq, unsigned long cc);
+	void setFreq(unsigned newFreq, unsigned cc);
 };
 
 class DutyMasterDisabler : public MasterDisabler {
diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp
index 7bcb783..6036256 100644
--- a/libgambatte/src/sound/envelope_unit.cpp
+++ b/libgambatte/src/sound/envelope_unit.cpp
@@ -19,12 +19,16 @@
 #include "envelope_unit.h"
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
 
 void EnvelopeUnit::event() {
-	const unsigned long period = nr2 & 7;
+	const unsigned period = nr2 & 7;
 	
 	if (period) {
 		unsigned newVol = volume;
@@ -63,9 +67,9 @@ bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
 	return !(newNr2 & 0xF8);
 }
 
-bool EnvelopeUnit::nr4Init(const unsigned long cc) {
+bool EnvelopeUnit::nr4Init(const unsigned cc) {
 	{
-		unsigned long period = nr2 & 7;
+		unsigned period = nr2 & 7;
 		
 		if (!period)
 			period = 8;
@@ -97,10 +101,17 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
 	estate.volume = volume;
 }
 
-void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
+void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned cc) {
 	counter = std::max(estate.counter, cc);
 	volume = estate.volume;
 	this->nr2 = nr2;
 }
 
+void EnvelopeUnit::loadOrSave(loadsave& state)
+{
+	loadOrSave2(state);
+	state(nr2);
+	state(volume);
+}
+
 }
diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h
index fc2551c..aa0cac4 100644
--- a/libgambatte/src/sound/envelope_unit.h
+++ b/libgambatte/src/sound/envelope_unit.h
@@ -19,8 +19,13 @@
 #ifndef ENVELOPE_UNIT_H
 #define ENVELOPE_UNIT_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "sound_unit.h"
 #include "../savestate.h"
+#include "../loadsave.h"
 
 namespace gambatte {
 
@@ -28,7 +33,7 @@ class EnvelopeUnit : public SoundUnit {
 public:
 	struct VolOnOffEvent {
 		virtual ~VolOnOffEvent() {}
-		virtual void operator()(unsigned long /*cc*/) {}
+		virtual void operator()(unsigned /*cc*/) {}
 	};
 	
 private:
@@ -43,10 +48,11 @@ public:
 	bool dacIsOn() const { return nr2 & 0xF8; }
 	unsigned getVolume() const { return volume; }
 	bool nr2Change(unsigned newNr2);
-	bool nr4Init(unsigned long cycleCounter);
+	bool nr4Init(unsigned cycleCounter);
 	void reset();
 	void saveState(SaveState::SPU::Env &estate) const;
-	void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
+	void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned cc);
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp
index d295dc0..0fd9503 100644
--- a/libgambatte/src/sound/length_counter.cpp
+++ b/libgambatte/src/sound/length_counter.cpp
@@ -20,6 +20,10 @@
 #include "master_disabler.h"
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
@@ -36,12 +40,12 @@ void LengthCounter::event() {
 	disableMaster();
 }
 
-void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
+void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned cycleCounter) {
 	lengthCounter = (~newNr1 & lengthMask) + 1;
-	counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
+	counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned>(COUNTER_DISABLED);
 }
 
-void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
+void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned cycleCounter) {
 	if (counter != COUNTER_DISABLED)
 		lengthCounter = (counter >> 13) - (cycleCounter >> 13);
 	
@@ -83,9 +87,16 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
 	lstate.lengthCounter = lengthCounter;
 }
 
-void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
+void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned cc) {
 	counter = std::max(lstate.counter, cc);
 	lengthCounter = lstate.lengthCounter;
 }
 
+void LengthCounter::loadOrSave(loadsave& state)
+{
+	loadOrSave2(state);
+	state(lengthCounter);
+	state(cgb);
+}
+
 }
diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h
index 3360ab1..f21fc9d 100644
--- a/libgambatte/src/sound/length_counter.h
+++ b/libgambatte/src/sound/length_counter.h
@@ -19,8 +19,13 @@
 #ifndef LENGTH_COUNTER_H
 #define LENGTH_COUNTER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "sound_unit.h"
 #include "../savestate.h"
+#include "../loadsave.h"
 
 namespace gambatte {
 
@@ -35,12 +40,14 @@ class LengthCounter : public SoundUnit {
 public:
 	LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
 	void event();
-	void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
-	void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
+	void nr1Change(unsigned newNr1, unsigned nr4, unsigned cc);
+	void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned cc);
 // 	void reset();
 	void init(bool cgb);
 	void saveState(SaveState::SPU::LCounter &lstate) const;
-	void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
+	void loadState(const SaveState::SPU::LCounter &lstate, unsigned cc);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h
index b1ca691..55b69a9 100644
--- a/libgambatte/src/sound/sound_unit.h
+++ b/libgambatte/src/sound/sound_unit.h
@@ -19,19 +19,28 @@
 #ifndef SOUND_UNIT_H
 #define SOUND_UNIT_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
 namespace gambatte {
 
 class SoundUnit {
 protected:
-	unsigned long counter;
+	unsigned counter;
 public:
 	enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
 	
 	SoundUnit() : counter(COUNTER_DISABLED) {}
 	virtual ~SoundUnit() {}
 	virtual void event() = 0;
-	unsigned long getCounter() const { return counter; }
-	virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
+	unsigned getCounter() const { return counter; }
+	virtual void resetCounters(unsigned /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
+	void loadOrSave2(loadsave& state) {
+		state(counter);
+	}
 };
 
 }
diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h
index ac785e5..4ef164f 100644
--- a/libgambatte/src/sound/static_output_tester.h
+++ b/libgambatte/src/sound/static_output_tester.h
@@ -19,6 +19,10 @@
 #ifndef STATIC_OUTPUT_TESTER_H
 #define STATIC_OUTPUT_TESTER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "envelope_unit.h"
 
 namespace gambatte {
@@ -29,11 +33,11 @@ class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
 	Unit &unit;
 public:
 	StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
-	void operator()(unsigned long cc);
+	void operator()(unsigned cc);
 };
 
 template<class Channel, class Unit>
-void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
+void StaticOutputTester<Channel, Unit>::operator()(const unsigned cc) {
 	if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
 		unit.reviveCounter(cc);
 	else
diff --git a/libgambatte/src/state_osd_elements.cpp b/libgambatte/src/state_osd_elements.cpp
index 4ec0c52..ffd91a3 100644
--- a/libgambatte/src/state_osd_elements.cpp
+++ b/libgambatte/src/state_osd_elements.cpp
@@ -22,6 +22,10 @@
 #include <fstream>
 #include <cstring>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace {
 
 using namespace gambatte;
@@ -71,7 +75,7 @@ life(4 * 60) {
 	print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/
 	
 	print(pixels, w(), ShadeFill(), txt);
-	print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);
+	print(pixels + 1 * w() + 1, w(), 0xE0E0E0u, txt);
 }
 
 ShadedTextOsdElment::~ShadedTextOsdElment() {
@@ -142,7 +146,7 @@ life(4 * 60) {
 			
 			static const char txt[] = { E,m,p,t,bitmapfont::y,0 };
 			
-			print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt);
+			print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080u, txt);
 		}
 	}
 }
diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp
index 5d6c313..29fac25 100644
--- a/libgambatte/src/statesaver.cpp
+++ b/libgambatte/src/statesaver.cpp
@@ -23,6 +23,11 @@
 #include <cstring>
 #include <algorithm>
 #include <fstream>
+#include <sstream>
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace {
 
@@ -41,36 +46,36 @@ enum AsciiChar {
 
 struct Saver {
 	const char *label;
-	void (*save)(std::ofstream &file, const SaveState &state);
-	void (*load)(std::ifstream &file, SaveState &state);
-	unsigned char labelsize;
+	void (*save)(std::ostream &file, const SaveState &state);
+	void (*load)(std::istream &file, SaveState &state);
+	unsigned int labelsize;
 };
 
 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) {
+static void put24(std::ostream &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) {
+static void put32(std::ostream &file, const unsigned long data) {
 	file.put(data >> 24 & 0xFF);
 	file.put(data >> 16 & 0xFF);
 	file.put(data >> 8 & 0xFF);
 	file.put(data & 0xFF);
 }
 
-static void write(std::ofstream &file, const unsigned char data) {
+static void write(std::ostream &file, const unsigned char data) {
 	static const char inf[] = { 0x00, 0x00, 0x01 };
 	
 	file.write(inf, sizeof(inf));
 	file.put(data & 0xFF);
 }
 
-static void write(std::ofstream &file, const unsigned short data) {
+static void write(std::ostream &file, const unsigned short data) {
 	static const char inf[] = { 0x00, 0x00, 0x02 };
 	
 	file.write(inf, sizeof(inf));
@@ -78,30 +83,30 @@ static void write(std::ofstream &file, const unsigned short data) {
 	file.put(data & 0xFF);
 }
 
-static void write(std::ofstream &file, const unsigned long data) {
+static void write(std::ostream &file, const unsigned int data) {
 	static const char inf[] = { 0x00, 0x00, 0x04 };
 	
 	file.write(inf, sizeof(inf));
 	put32(file, data);
 }
 
-static inline void write(std::ofstream &file, const bool data) {
+static inline void write(std::ostream &file, const bool data) {
 	write(file, static_cast<unsigned char>(data));
 }
 
-static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) {
+static void write(std::ostream &file, const unsigned char *data, const unsigned long sz) {
 	put24(file, sz);
 	file.write(reinterpret_cast<const char*>(data), sz);
 }
 
-static void write(std::ofstream &file, const bool *data, const unsigned long sz) {
+static void write(std::ostream &file, const bool *data, const unsigned long sz) {
 	put24(file, sz);
 	
 	for (unsigned long i = 0; i < sz; ++i)
 		file.put(data[i]);
 }
 
-static unsigned long get24(std::ifstream &file) {
+static unsigned long get24(std::istream &file) {
 	unsigned long tmp = file.get() & 0xFF;
 	
 	tmp = tmp << 8 | (file.get() & 0xFF);
@@ -109,15 +114,15 @@ static unsigned long get24(std::ifstream &file) {
 	return tmp << 8 | (file.get() & 0xFF);
 }
 
-static unsigned long read(std::ifstream &file) {
-	unsigned long size = get24(file);
+static unsigned read(std::istream &file) {
+	unsigned size = get24(file);
 	
 	if (size > 4) {
 		file.ignore(size - 4);
 		size = 4;
 	}
 	
-	unsigned long out = 0;
+	unsigned out = 0;
 	
 	switch (size) {
 	case 4: out = (out | (file.get() & 0xFF)) << 8;
@@ -129,23 +134,23 @@ static unsigned long read(std::ifstream &file) {
 	return out;
 }
 
-static inline void read(std::ifstream &file, unsigned char &data) {
+static inline void read(std::istream &file, unsigned char &data) {
 	data = read(file) & 0xFF;
 }
 
-static inline void read(std::ifstream &file, unsigned short &data) {
+static inline void read(std::istream &file, unsigned short &data) {
 	data = read(file) & 0xFFFF;
 }
 
-static inline void read(std::ifstream &file, unsigned long &data) {
+static inline void read(std::istream &file, unsigned int &data) {
 	data = read(file);
 }
 
-static inline void read(std::ifstream &file, bool &data) {
+static inline void read(std::istream &file, bool &data) {
 	data = read(file);
 }
 
-static void read(std::ifstream &file, unsigned char *data, unsigned long sz) {
+static void read(std::istream &file, unsigned char *data, unsigned long sz) {
 	const unsigned long size = get24(file);
 	
 	if (size < sz)
@@ -160,7 +165,7 @@ static void read(std::ifstream &file, unsigned char *data, unsigned long sz) {
 	}
 }
 
-static void read(std::ifstream &file, bool *data, unsigned long sz) {
+static void read(std::istream &file, bool *data, unsigned long sz) {
 	const unsigned long size = get24(file);
 	
 	if (size < sz)
@@ -193,8 +198,8 @@ public:
 };
 
 static void pushSaver(SaverList::list_t &list, const char *label,
-		void (*save)(std::ofstream &file, const SaveState &state),
-		void (*load)(std::ifstream &file, SaveState &state), unsigned labelsize) {
+		void (*save)(std::ostream &file, const SaveState &state),
+		void (*load)(std::istream &file, SaveState &state), unsigned labelsize) {
 	const Saver saver = { label, save, load, labelsize };
 	list.push_back(saver);
 }
@@ -202,8 +207,8 @@ static void pushSaver(SaverList::list_t &list, const char *label,
 SaverList::SaverList() {
 #define ADD(arg) do { \
 	struct Func { \
-		static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg); } \
-		static void load(std::ifstream &file, SaveState &state) { read(file, state.arg); } \
+		static void save(std::ostream &file, const SaveState &state) { write(file, state.arg); } \
+		static void load(std::istream &file, SaveState &state) { read(file, state.arg); } \
 	}; \
 	\
 	pushSaver(list, label, Func::save, Func::load, sizeof label); \
@@ -211,8 +216,8 @@ SaverList::SaverList() {
 
 #define ADDPTR(arg) do { \
 	struct Func { \
-		static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \
-		static void load(std::ifstream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \
+		static void save(std::ostream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \
+		static void load(std::istream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \
 	}; \
 	\
 	pushSaver(list, label, Func::save, Func::load, sizeof label); \
@@ -220,8 +225,8 @@ SaverList::SaverList() {
 
 #define ADDARRAY(arg) do { \
 	struct Func { \
-		static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg, sizeof(state.arg)); } \
-		static void load(std::ifstream &file, SaveState &state) { read(file, state.arg, sizeof(state.arg)); } \
+		static void save(std::ostream &file, const SaveState &state) { write(file, state.arg, sizeof(state.arg)); } \
+		static void load(std::istream &file, SaveState &state) { read(file, state.arg, sizeof(state.arg)); } \
 	}; \
 	\
 	pushSaver(list, label, Func::save, Func::load, sizeof label); \
@@ -373,7 +378,7 @@ static void blendPxlPairs(PxlSum *const dst, const PxlSum *const sums) {
 	dst->g  = sums[1].g  * 8 + (sums[0].g  - sums[1].g ) * 3;
 }
 
-static void writeSnapShot(std::ofstream &file, const uint_least32_t *pixels, const int pitch) {
+static void writeSnapShot(std::ostream &file, const uint_least32_t *pixels, const int pitch) {
 	put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(uint_least32_t) : 0);
 	
 	if (pixels) {
@@ -405,15 +410,9 @@ static void writeSnapShot(std::ofstream &file, const uint_least32_t *pixels, con
 
 static SaverList list;
 
-} // anon namespace
-
-namespace gambatte {
-
-void StateSaver::saveState(const SaveState &state,
+void saveStateI(const SaveState &state,
 		const uint_least32_t *const videoBuf,
-		const int pitch, const std::string &filename) {
-	std::ofstream file(filename.c_str(), std::ios_base::binary);
-	
+		const int pitch, std::ostream& file) {
 	if (file.fail())
 		return;
 	
@@ -427,9 +426,7 @@ void StateSaver::saveState(const SaveState &state,
 	}
 }
 
-bool StateSaver::loadState(SaveState &state, const std::string &filename) {
-	std::ifstream file(filename.c_str(), std::ios_base::binary);
-	
+bool loadStateI(SaveState &state, std::istream& file) {
 	if (file.fail() || file.get() != 0)
 		return false;
 	
@@ -465,4 +462,36 @@ bool StateSaver::loadState(SaveState &state, const std::string &filename) {
 	return true;
 }
 
+} // anon namespace
+
+namespace gambatte {
+
+	
+void StateSaver::saveState(const SaveState &state,
+		const uint_least32_t *const videoBuf,
+		const int pitch, const std::string &filename) {
+	std::ofstream file(filename.c_str(), std::ios_base::binary);
+	saveStateI(state, videoBuf, pitch, file);
+}
+
+bool StateSaver::loadState(SaveState &state, const std::string &filename) {
+	std::ifstream file(filename.c_str(), std::ios_base::binary);
+	return loadStateI(state, file);
+}
+
+void StateSaver::saveState(const SaveState &state,
+		std::vector<char>& data) {
+	std::ostringstream file;
+	saveStateI(state, NULL, 160, file);
+	std::string out = file.str();
+	data.resize(out.size());
+	std::copy(out.begin(), out.end(), data.begin());
+}
+
+bool StateSaver::loadState(SaveState &state, const std::vector<char>& data) {
+	std::string in(data.begin(), data.end());
+	std::istringstream file(in);
+	return loadStateI(state, file);
+}
+
 }
diff --git a/libgambatte/src/statesaver.h b/libgambatte/src/statesaver.h
index 2c58a42..f902403 100644
--- a/libgambatte/src/statesaver.h
+++ b/libgambatte/src/statesaver.h
@@ -19,8 +19,13 @@
 #ifndef STATESAVER_H
 #define STATESAVER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "gbint.h"
 #include <string>
+#include <vector>
 
 namespace gambatte {
 
@@ -38,6 +43,8 @@ public:
 	static void saveState(const SaveState &state,
 			const uint_least32_t *videoBuf, int pitch, const std::string &filename);
 	static bool loadState(SaveState &state, const std::string &filename);
+	static void saveState(const SaveState &state, std::vector<char>& data);
+	static bool loadState(SaveState &state, const std::vector<char>& data);
 };
 
 }
diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp
index 44ce31c..56b6e72 100644
--- a/libgambatte/src/tima.cpp
+++ b/libgambatte/src/tima.cpp
@@ -19,6 +19,10 @@
 #include "tima.h"
 #include "savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 static const unsigned char timaClock[4] = { 10, 4, 6, 8 };
 
 namespace gambatte {
@@ -50,12 +54,12 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr
 			          ? tmatime_
 			          : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
 		:
-			static_cast<unsigned long>(DISABLED_TIME)
+			static_cast<unsigned>(DISABLED_TIME)
 	);
 }
 
-void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) {
-	const unsigned long dec = oldCc - newCc;
+void Tima::resetCc(const unsigned oldCc, const unsigned newCc, const TimaInterruptRequester timaIrq) {
+	const unsigned dec = oldCc - newCc;
 	
 	if (tac_ & 0x04) {
 		updateIrq(oldCc, timaIrq);
@@ -69,8 +73,8 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T
 	}
 }
 
-void Tima::updateTima(const unsigned long cycleCounter) {
-	const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
+void Tima::updateTima(const unsigned cycleCounter) {
+	const unsigned ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
 
 	lastUpdate_ += ticks << timaClock[tac_ & 3];
 
@@ -81,7 +85,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
 		tima_ = tma_;
 	}
 
-	unsigned long tmp = tima_ + ticks;
+	unsigned tmp = tima_ + ticks;
 
 	while (tmp > 0x100)
 		tmp -= 0x100 - tma_;
@@ -101,7 +105,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
 	tima_ = tmp;
 }
 
-void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTima(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
 	if (tac_ & 0x04) {
 		updateIrq(cycleCounter, timaIrq);
 		updateTima(cycleCounter);
@@ -115,7 +119,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const
 	tima_ = data;
 }
 
-void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTma(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
 	if (tac_ & 0x04) {
 		updateIrq(cycleCounter, timaIrq);
 		updateTima(cycleCounter);
@@ -124,9 +128,9 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T
 	tma_ = data;
 }
 
-void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTac(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
 	if (tac_ ^ data) {
-		unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
+		unsigned nextIrqEventTime = timaIrq.nextIrqEventTime();
 		
 		if (tac_ & 0x04) {
 			updateIrq(cycleCounter, timaIrq);
@@ -156,7 +160,7 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T
 	tac_ = data;
 }
 
-unsigned Tima::tima(unsigned long cycleCounter) {
+unsigned Tima::tima(unsigned cycleCounter) {
 	if (tac_ & 0x04)
 		updateTima(cycleCounter);
 
@@ -168,4 +172,13 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
 	timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
 }
 
+void Tima::loadOrSave(loadsave& state)
+{
+	state(lastUpdate_);
+	state(tmatime_);
+	state(tima_);
+	state(tma_);
+	state(tac_);
+}
+
 }
diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h
index 48e9ccd..2857934 100644
--- a/libgambatte/src/tima.h
+++ b/libgambatte/src/tima.h
@@ -19,6 +19,10 @@
 #ifndef TIMA_H
 #define TIMA_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "interruptrequester.h"
 
 namespace gambatte {
@@ -29,37 +33,39 @@ class TimaInterruptRequester {
 public:
 	explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {}
 	void flagIrq() const { intreq.flagIrq(4); }
-	unsigned long nextIrqEventTime() const { return intreq.eventTime(TIMA); }
-	void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime<TIMA>(time); }
+	unsigned nextIrqEventTime() const { return intreq.eventTime(TIMA); }
+	void setNextIrqEventTime(const unsigned time) const { intreq.setEventTime<TIMA>(time); }
 };
 
 class Tima {
-	unsigned long lastUpdate_;
-	unsigned long tmatime_;
+	unsigned lastUpdate_;
+	unsigned tmatime_;
 	
 	unsigned char tima_;
 	unsigned char tma_;
 	unsigned char tac_;
 	
-	void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) {
+	void updateIrq(const unsigned cc, const TimaInterruptRequester timaIrq) {
 		while (cc >= timaIrq.nextIrqEventTime())
 			doIrqEvent(timaIrq);
 	}
 	
-	void updateTima(unsigned long cc);
+	void updateTima(unsigned cc);
 	
 public:
 	Tima();
 	void saveState(SaveState &) const;
 	void loadState(const SaveState &, TimaInterruptRequester timaIrq);
-	void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq);
+	void resetCc(unsigned oldCc, unsigned newCc, TimaInterruptRequester timaIrq);
 	
-	void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
-	void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
-	void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq);
-	unsigned tima(unsigned long cc);
+	void setTima(unsigned tima, unsigned cc, TimaInterruptRequester timaIrq);
+	void setTma(unsigned tma, unsigned cc, TimaInterruptRequester timaIrq);
+	void setTac(unsigned tac, unsigned cc, TimaInterruptRequester timaIrq);
+	unsigned tima(unsigned cc);
 	
 	void doIrqEvent(TimaInterruptRequester timaIrq);
+
+	void loadOrSave(loadsave& state);
 };
 
 }
diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
index 58fc6c1..b0e5c4e 100644
--- a/libgambatte/src/video.cpp
+++ b/libgambatte/src/video.cpp
@@ -21,19 +21,23 @@
 #include <cstring>
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
-void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
+void LCD::setDmgPalette(uint_least32_t *const palette, const uint_least32_t *const dmgColors, const unsigned data) {
 	palette[0] = dmgColors[data      & 3];
 	palette[1] = dmgColors[data >> 2 & 3];
 	palette[2] = dmgColors[data >> 4 & 3];
 	palette[3] = dmgColors[data >> 6 & 3];
 }
 
-static unsigned long gbcToRgb32(const unsigned bgr15) {
-	const unsigned long r = bgr15       & 0x1F;
-	const unsigned long g = bgr15 >>  5 & 0x1F;
-	const unsigned long b = bgr15 >> 10 & 0x1F;
+static uint_least32_t gbcToRgb32(const unsigned bgr15) {
+	const uint_least32_t r = bgr15       & 0x1F;
+	const uint_least32_t g = bgr15 >>  5 & 0x1F;
+	const uint_least32_t b = bgr15 >> 10 & 0x1F;
 
 	return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
 }
@@ -80,7 +84,6 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
 		setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
 
 	reset(oamram, false);
-	setVideoBuffer(0, 160);
 }
 
 void LCD::reset(const unsigned char *const oamram, const bool cgb) {
@@ -89,7 +92,7 @@ void LCD::reset(const unsigned char *const oamram, const bool cgb) {
 	refreshPalettes();
 }
 
-static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
+static unsigned mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned  cycleCounter) {
 	if (!(statReg & 0x20))
 		return DISABLED_TIME;
 	
@@ -107,16 +110,16 @@ static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &l
 	return cycleCounter + next;
 }
 
-static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
+static inline unsigned m0IrqTimeFromXpos166Time(const unsigned xpos166Time, const bool cgb, const bool ds) {
 	return xpos166Time + cgb - ds;
 }
 
-static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
+static inline unsigned hdmaTimeFromM0Time(const unsigned m0Time, const bool ds) {
 	return m0Time + 1 - ds;
 }
 
-static unsigned long nextHdmaTime(const unsigned long lastM0Time,
-		const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
+static unsigned nextHdmaTime(const unsigned lastM0Time,
+		const unsigned nextM0Time, const unsigned cycleCounter, const bool ds) {
 	return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
 	                    ? hdmaTimeFromM0Time(lastM0Time, ds)
 	                    : hdmaTimeFromM0Time(nextM0Time, ds);
@@ -152,18 +155,18 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
 		lycIrq.reschedule(ppu.lyCounter(), ppu.now());
 		
 		eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
-							? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
+							? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
 		eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
-							? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
+							? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
 		eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
 		eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
 		eventTimes_.setm<LYC_IRQ>(lycIrq.time());
 		eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
 		eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
-		eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
+		eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned>(DISABLED_TIME));
 		eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
 				? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
-				: static_cast<unsigned long>(DISABLED_TIME));
+				: static_cast<unsigned>(DISABLED_TIME));
 	} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
 		eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
 	
@@ -212,7 +215,7 @@ struct Blend {
 };
 
 template<typename T>
-static void clear(T *buf, const unsigned long color, const int dpitch) {
+static void clear(T *buf, const uint_least32_t color, const int dpitch) {
 	unsigned lines = 144;
 
 	while (lines--) {
@@ -223,11 +226,11 @@ static void clear(T *buf, const unsigned long color, const int dpitch) {
 
 }
 
-void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
+void LCD::updateScreen(const bool blanklcd, const unsigned cycleCounter, uint_least32_t* vbuffer, unsigned vpitch) {
 	update(cycleCounter);
 	
 	if (blanklcd && ppu.frameBuf().fb()) {
-		const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
+		const uint_least32_t color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
 		clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
 	}
 
@@ -245,14 +248,15 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
 		} else
 			osdElement.reset();
 	}
+	ppu.frameBuf().blit(vbuffer, vpitch);
 }
 
-void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
+void LCD::resetCc(const unsigned oldCc, const unsigned newCc) {
 	update(oldCc);
 	ppu.resetCc(oldCc, newCc);
 	
 	if (ppu.lcdc() & 0x80) {
-		const unsigned long dec = oldCc - newCc;
+		const unsigned dec = oldCc - newCc;
 		
 		nextM0Time_.invalidatePredictedNextM0Time();
 		lycIrq.reschedule(ppu.lyCounter(), newCc);
@@ -266,7 +270,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
 	}
 }
 
-void LCD::speedChange(const unsigned long cycleCounter) {
+void LCD::speedChange(const unsigned cycleCounter) {
 	update(cycleCounter);
 	ppu.speedChange(cycleCounter);
 	
@@ -290,13 +294,13 @@ void LCD::speedChange(const unsigned long cycleCounter) {
 	}
 }
 
-static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
-		const unsigned long lastM0Time, const unsigned long nextM0Time)
+static inline unsigned m0TimeOfCurrentLine(const unsigned nextLyTime,
+		const unsigned lastM0Time, const unsigned nextM0Time)
 {
 	return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
 }
 
-unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
+unsigned LCD::m0TimeOfCurrentLine(const unsigned cc) {
 	if (cc >= nextM0Time_.predictedNextM0Time()) {
 		update(cc);
 		nextM0Time_.predictNextM0Time(ppu);
@@ -306,7 +310,7 @@ unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
 }
 
 static bool isHdmaPeriod(const LyCounter &lyCounter,
-		const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
+		const unsigned m0TimeOfCurrentLy, const unsigned cycleCounter)
 {
 	const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
 	
@@ -314,7 +318,7 @@ static bool isHdmaPeriod(const LyCounter &lyCounter,
 			&& cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
 }
 
-void LCD::enableHdma(const unsigned long cycleCounter) {
+void LCD::enableHdma(const unsigned cycleCounter) {
 	if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
 		update(cycleCounter);
 		nextM0Time_.predictNextM0Time(ppu);
@@ -330,14 +334,14 @@ void LCD::enableHdma(const unsigned long cycleCounter) {
 	eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
 }
 
-void LCD::disableHdma(const unsigned long cycleCounter) {
+void LCD::disableHdma(const unsigned cycleCounter) {
 	if (cycleCounter >= eventTimes_.nextEventTime())
 		update(cycleCounter);
 	
 	eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
 }
 
-bool LCD::vramAccessible(const unsigned long cycleCounter) {
+bool LCD::vramAccessible(const unsigned cycleCounter) {
 	if (cycleCounter >= eventTimes_.nextEventTime())
 		update(cycleCounter);
 	
@@ -346,7 +350,7 @@ bool LCD::vramAccessible(const unsigned long cycleCounter) {
 			|| cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
 }
 
-bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
+bool LCD::cgbpAccessible(const unsigned cycleCounter) {
 	if (cycleCounter >= eventTimes_.nextEventTime())
 		update(cycleCounter);
 	
@@ -356,27 +360,27 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
 }
 
 static void doCgbColorChange(unsigned char *const pdata,
-		unsigned long *const palette, unsigned index, const unsigned data) {
+		uint_least32_t *const palette, unsigned index, const unsigned data) {
 	pdata[index] = data;
 	index >>= 1;
 	palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
 }
 
-void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
 	if (cgbpAccessible(cycleCounter)) {
 		update(cycleCounter);
 		doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
 	}
 }
 
-void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
 	if (cgbpAccessible(cycleCounter)) {
 		update(cycleCounter);
 		doCgbColorChange(objpData, ppu.spPalette(), index, data);
 	}
 }
 
-bool LCD::oamReadable(const unsigned long cycleCounter) {
+bool LCD::oamReadable(const unsigned cycleCounter) {
 	if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
 		return true;
 	
@@ -389,7 +393,7 @@ bool LCD::oamReadable(const unsigned long cycleCounter) {
 	return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
 }
 
-bool LCD::oamWritable(const unsigned long cycleCounter) {
+bool LCD::oamWritable(const unsigned cycleCounter) {
 	if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
 		return true;
 	
@@ -417,13 +421,13 @@ void LCD::mode3CyclesChange() {
 	}
 }
 
-void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::wxChange(const unsigned newValue, const unsigned cycleCounter) {
 	update(cycleCounter + isDoubleSpeed() + 1);
 	ppu.setWx(newValue);
 	mode3CyclesChange();
 }
 
-void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::wyChange(const unsigned newValue, const unsigned cycleCounter) {
 	update(cycleCounter + 1);
 	ppu.setWy(newValue);
 // 	mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
@@ -438,18 +442,18 @@ void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
 	}
 }
 
-void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
+void LCD::scxChange(const unsigned newScx, const unsigned cycleCounter) {
 	update(cycleCounter + ppu.cgb() + isDoubleSpeed());
 	ppu.setScx(newScx);
 	mode3CyclesChange();
 }
 
-void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::scyChange(const unsigned newValue, const unsigned cycleCounter) {
 	update(cycleCounter + ppu.cgb() + isDoubleSpeed());
 	ppu.setScy(newValue);
 }
 
-void LCD::oamChange(const unsigned long cycleCounter) {
+void LCD::oamChange(const unsigned cycleCounter) {
 	if (ppu.lcdc() & 0x80) {
 		update(cycleCounter);
 		ppu.oamChange(cycleCounter);
@@ -457,7 +461,7 @@ void LCD::oamChange(const unsigned long cycleCounter) {
 	}
 }
 
-void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
+void LCD::oamChange(const unsigned char *const oamram, const unsigned cycleCounter) {
 	update(cycleCounter);
 	ppu.oamChange(oamram, cycleCounter);
 	
@@ -465,7 +469,7 @@ void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycle
 		eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
 }
 
-void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
+void LCD::lcdcChange(const unsigned data, const unsigned cycleCounter) {
 	const unsigned oldLcdc = ppu.lcdc();
 	update(cycleCounter);
 	
@@ -522,7 +526,7 @@ void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
 		ppu.setLcdc(data, cycleCounter);
 }
 
-void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) {
+void LCD::lcdstatChange(const unsigned data, const unsigned cycleCounter) {
 	if (cycleCounter >= eventTimes_.nextEventTime())
 		update(cycleCounter);
 
@@ -584,7 +588,7 @@ void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) {
 	m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
 }
 
-void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) {
+void LCD::lycRegChange(const unsigned data, const unsigned cycleCounter) {
 	if (data == lycIrq.lycReg())
 		return;
 
@@ -628,7 +632,7 @@ void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) {
 	}
 }
 
-unsigned LCD::getStat(const unsigned lycReg, const unsigned long cycleCounter) {
+unsigned LCD::getStat(const unsigned lycReg, const unsigned cycleCounter) {
 	unsigned stat = 0;
 
 	if (ppu.lcdc() & 0x80) {
@@ -672,7 +676,7 @@ inline void LCD::doMode2IrqEvent() {
 	m2IrqStatReg_ = statReg;
 	
 	if (!(statReg & 0x08)) {
-		unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
+		unsigned nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
 		
 		if (ly == 0) {
 			nextTime -= 4;
@@ -726,7 +730,7 @@ inline void LCD::event() {
 			
 			eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
 					? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
-					: static_cast<unsigned long>(DISABLED_TIME));
+					: static_cast<unsigned>(DISABLED_TIME));
 			break;
 		
 		case ONESHOT_LCDSTATIRQ:
@@ -750,7 +754,7 @@ inline void LCD::event() {
 	}
 }
 
-void LCD::update(const unsigned long cycleCounter) {
+void LCD::update(const unsigned cycleCounter) {
 	if (!(ppu.lcdc() & 0x80))
 		return;
 	
@@ -762,15 +766,11 @@ void LCD::update(const unsigned long cycleCounter) {
 	ppu.update(cycleCounter);
 }
 
-void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
-	ppu.setFrameBuf(videoBuf, pitch);
-}
-
-void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
+void LCD::setDmgPaletteColor(const unsigned index, const uint_least32_t rgb32) {
 	dmgColorsRgb32[index] = rgb32;
 }
 
-void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
+void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const uint_least32_t rgb32) {
 	if (palNum > 2 || colorNum > 3)
 		return;
 
@@ -778,4 +778,18 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con
 	refreshPalettes();
 }
 
+void LCD::loadOrSave(loadsave& state) {
+	ppu.loadOrSave(state);
+	state(dmgColorsRgb32, 12);
+	state(bgpData, 64);
+	state(objpData, 64);
+	eventTimes_.loadOrSave(state);
+	m0Irq_.loadOrSave(state);
+	lycIrq.loadOrSave(state);
+	nextM0Time_.loadOrSave(state);
+	state(statReg);
+	state(m2IrqStatReg_);
+	state(m1IrqStatReg_);
+}
+
 }
diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
index e7802f1..9fe9454 100644
--- a/libgambatte/src/video.h
+++ b/libgambatte/src/video.h
@@ -19,6 +19,10 @@
 #ifndef VIDEO_H
 #define VIDEO_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "video/ppu.h"
 #include "video/lyc_irq.h"
 #include "video/next_m0_time.h"
@@ -36,7 +40,7 @@ public:
 	explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {}
 	void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); }
 	void flagIrq(const unsigned bit) const { intreq->flagIrq(bit); }
-	void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
+	void setNextEventTime(const unsigned time) const { intreq->setEventTime<VIDEO>(time); }
 };
 
 class M0Irq {
@@ -50,15 +54,21 @@ public:
 		statReg_ = statReg;
 		 lycReg_ =  lycReg;
 	}
-	
+
+	void loadOrSave(loadsave& state)
+	{
+		state(statReg_);
+		state(lycReg_);
+	}
+
 	void statRegChange(const unsigned statReg,
-			const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
+			const unsigned nextM0IrqTime, const unsigned cc, const bool cgb) {
 		if (nextM0IrqTime - cc > cgb * 2U)
 			statReg_ = statReg;
 	}
 	
 	void lycRegChange(const unsigned lycReg,
-			const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
+			const unsigned nextM0IrqTime, const unsigned cc, const bool ds, const bool cgb) {
 		if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
 			lycReg_ = lycReg;
 	}
@@ -94,7 +104,7 @@ class LCD {
 		VideoInterruptRequester memEventRequester_;
 		
 		void setMemEvent() {
-			const unsigned long nmet = nextMemEventTime();
+			const unsigned nmet = nextMemEventTime();
 			eventMin_.setValue<MEM_EVENT>(nmet);
 			memEventRequester_.setNextEventTime(nmet);
 		}
@@ -103,23 +113,28 @@ class LCD {
 		explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
 		
 		Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
-		unsigned long nextEventTime() const { return eventMin_.minValue(); }
-		unsigned long operator()(const Event e) const { return eventMin_.value(e); }
-		template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
-		void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
+		unsigned nextEventTime() const { return eventMin_.minValue(); }
+		unsigned operator()(const Event e) const { return eventMin_.value(e); }
+		template<Event e> void set(const unsigned time) { eventMin_.setValue<e>(time); }
+		void set(const Event e, const unsigned time) { eventMin_.setValue(e, time); }
 		
 		MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
-		unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
-		unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
-		template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
-		void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
+		unsigned nextMemEventTime() const { return memEventMin_.minValue(); }
+		unsigned operator()(const MemEvent e) const { return memEventMin_.value(e); }
+		template<MemEvent e> void setm(const unsigned time) { memEventMin_.setValue<e>(time); setMemEvent(); }
+		void set(const MemEvent e, const unsigned time) { memEventMin_.setValue(e, time); setMemEvent(); }
 		
 		void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
 		void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
+
+		void loadOrSave(loadsave& state) {
+			eventMin_.loadOrSave(state);
+			memEventMin_.loadOrSave(state);
+		}
 	};
 	
 	PPU ppu;
-	unsigned long dmgColorsRgb32[3 * 4];
+	uint_least32_t dmgColorsRgb32[3 * 4];
 	unsigned char  bgpData[8 * 8];
 	unsigned char objpData[8 * 8];
 
@@ -134,8 +149,8 @@ class LCD {
 	unsigned char m2IrqStatReg_;
 	unsigned char m1IrqStatReg_;
 
-	static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data);
-	void setDmgPaletteColor(unsigned index, unsigned long rgb32);
+	static void setDmgPalette(uint_least32_t *palette, const uint_least32_t *dmgColors, unsigned data);
+	void setDmgPaletteColor(unsigned index, uint_least32_t rgb32);
 
 	void refreshPalettes();
 	void setDBuffer();
@@ -143,12 +158,12 @@ class LCD {
 	void doMode2IrqEvent();
 	void event();
 
-	unsigned long m0TimeOfCurrentLine(unsigned long cc);
-	bool cgbpAccessible(unsigned long cycleCounter);
+	unsigned m0TimeOfCurrentLine(unsigned cc);
+	bool cgbpAccessible(unsigned cycleCounter);
 	
 	void mode3CyclesChange();
-	void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
-	void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
+	void doCgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter);
+	void doCgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter);
 
 public:
 	LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
@@ -156,65 +171,66 @@ public:
 	void setStatePtrs(SaveState &state);
 	void saveState(SaveState &state) const;
 	void loadState(const SaveState &state, const unsigned char *oamram);
-	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
-	void setVideoBuffer(uint_least32_t *videoBuf, int pitch);
+	void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
+
+	void loadOrSave(loadsave& state);
 
 	void setOsdElement(std::auto_ptr<OsdElement> osdElement) { this->osdElement = osdElement; }
 
-	void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
+	void dmgBgPaletteChange(const unsigned data, const unsigned cycleCounter) {
 		update(cycleCounter);
 		bgpData[0] = data;
 		setDmgPalette(ppu.bgPalette(), dmgColorsRgb32, data);
 	}
 
-	void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
+	void dmgSpPalette1Change(const unsigned data, const unsigned cycleCounter) {
 		update(cycleCounter);
 		objpData[0] = data;
 		setDmgPalette(ppu.spPalette(), dmgColorsRgb32 + 4, data);
 	}
 
-	void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
+	void dmgSpPalette2Change(const unsigned data, const unsigned cycleCounter) {
 		update(cycleCounter);
 		objpData[1] = data;
 		setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, data);
 	}
 
-	void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+	void cgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
 		if (bgpData[index] != data)
 			doCgbBgColorChange(index, data, cycleCounter);
 	}
 
-	void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+	void cgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
 		if (objpData[index] != data)
 			doCgbSpColorChange(index, data, cycleCounter);
 	}
 
-	unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
+	unsigned cgbBgColorRead(const unsigned index, const unsigned cycleCounter) {
 		return ppu.cgb() & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF;
 	}
 
-	unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
+	unsigned cgbSpColorRead(const unsigned index, const unsigned cycleCounter) {
 		return ppu.cgb() & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF;
 	}
 
-	void updateScreen(bool blanklcd, unsigned long cc);
-	void resetCc(unsigned long oldCC, unsigned long newCc);
-	void speedChange(unsigned long cycleCounter);
-	bool vramAccessible(unsigned long cycleCounter);
-	bool oamReadable(unsigned long cycleCounter);
-	bool oamWritable(unsigned long cycleCounter);
-	void wxChange(unsigned newValue, unsigned long cycleCounter);
-	void wyChange(unsigned newValue, unsigned long cycleCounter);
-	void oamChange(unsigned long cycleCounter);
-	void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
-	void scxChange(unsigned newScx, unsigned long cycleCounter);
-	void scyChange(unsigned newValue, unsigned long cycleCounter);
+	void updateScreen(bool blanklcd, unsigned cc, uint_least32_t* vbuffer, unsigned vpitch);
+	void resetCc(unsigned oldCC, unsigned newCc);
+	void speedChange(unsigned cycleCounter);
+	bool vramAccessible(unsigned cycleCounter);
+	bool oamReadable(unsigned cycleCounter);
+	bool oamWritable(unsigned cycleCounter);
+	void wxChange(unsigned newValue, unsigned cycleCounter);
+	void wyChange(unsigned newValue, unsigned cycleCounter);
+	void oamChange(unsigned cycleCounter);
+	void oamChange(const unsigned char *oamram, unsigned cycleCounter);
+	void scxChange(unsigned newScx, unsigned cycleCounter);
+	void scyChange(unsigned newValue, unsigned cycleCounter);
 
-	void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
+	void vramChange(const unsigned cycleCounter) { update(cycleCounter); }
 
-	unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
+	unsigned getStat(unsigned lycReg, unsigned cycleCounter);
 
-	unsigned getLyReg(const unsigned long cycleCounter) {
+	unsigned getLyReg(const unsigned cycleCounter) {
 		unsigned lyReg = 0;
 
 		if (ppu.lcdc() & 0x80) {
@@ -232,17 +248,17 @@ public:
 		return lyReg;
 	}
 
-	unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
+	unsigned nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
 
-	void lcdcChange(unsigned data, unsigned long cycleCounter);
-	void lcdstatChange(unsigned data, unsigned long cycleCounter);
-	void lycRegChange(unsigned data, unsigned long cycleCounter);
+	void lcdcChange(unsigned data, unsigned cycleCounter);
+	void lcdstatChange(unsigned data, unsigned cycleCounter);
+	void lycRegChange(unsigned data, unsigned cycleCounter);
 	
-	void enableHdma(unsigned long cycleCounter);
-	void disableHdma(unsigned long cycleCounter);
+	void enableHdma(unsigned cycleCounter);
+	void disableHdma(unsigned cycleCounter);
 	bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
 	
-	void update(unsigned long cycleCounter);
+	void update(unsigned cycleCounter);
 	
 	bool isCgb() const { return ppu.cgb(); }
 	bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp
index 5c8828d..15037f6 100644
--- a/libgambatte/src/video/ly_counter.cpp
+++ b/libgambatte/src/video/ly_counter.cpp
@@ -19,6 +19,10 @@
 #include "ly_counter.h"
 #include "../savestate.h"
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 LyCounter::LyCounter()
@@ -37,8 +41,8 @@ void LyCounter::doEvent() {
 	time_ = time_ + lineTime_;
 }
 
-unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
-	unsigned long tmp = time_ + (lineCycle << ds);
+unsigned LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned cycleCounter) const {
+	unsigned tmp = time_ + (lineCycle << ds);
 	
 	if (tmp - cycleCounter > lineTime_)
 		tmp -= lineTime_;
@@ -46,8 +50,8 @@ unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned
 	return tmp;
 }
 
-unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
-	unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
+unsigned LyCounter::nextFrameCycle(const unsigned frameCycle, const unsigned cycleCounter) const {
+	unsigned tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
 	
 	if (tmp - cycleCounter > 70224U << ds)
 		tmp -= 70224U << ds;
@@ -55,7 +59,7 @@ unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const un
 	return tmp;
 }
 
-void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
+void LyCounter::reset(const unsigned videoCycles, const unsigned lastUpdate) {
 	ly_ = videoCycles / 456;
 	time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
 }
diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h
index 49ff015..5c044c9 100644
--- a/libgambatte/src/video/ly_counter.h
+++ b/libgambatte/src/video/ly_counter.h
@@ -18,13 +18,18 @@
  ***************************************************************************/
 #ifndef LY_COUNTER_H
 #define LY_COUNTER_H
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
 
 namespace gambatte {
 
 struct SaveState;
 
 class LyCounter {
-	unsigned long time_;
+	unsigned time_;
 	unsigned short lineTime_;
 	unsigned char ly_;
 	bool ds;
@@ -34,21 +39,28 @@ public:
 	void doEvent();
 	bool isDoubleSpeed() const { return ds; }
 	
-	unsigned long frameCycles(const unsigned long cc) const {
+	unsigned frameCycles(const unsigned cc) const {
 		return ly_ * 456ul + lineCycles(cc);
 	}
 	
-	unsigned lineCycles(const unsigned long cc) const {
+	unsigned lineCycles(const unsigned cc) const {
 		return 456u - ((time_ - cc) >> isDoubleSpeed());
 	}
 	
 	unsigned lineTime() const { return lineTime_; }
 	unsigned ly() const { return ly_; }
-	unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
-	unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
-	void reset(unsigned long videoCycles, unsigned long lastUpdate);
+	unsigned nextLineCycle(unsigned lineCycle, unsigned cycleCounter) const;
+	unsigned nextFrameCycle(unsigned frameCycle, unsigned cycleCounter) const;
+	void reset(unsigned videoCycles, unsigned lastUpdate);
 	void setDoubleSpeed(bool ds_in);
-	unsigned long time() const { return time_; }
+	unsigned time() const { return time_; }
+
+	void loadOrSave(loadsave& state) {
+		state(time_);
+		state(lineTime_);
+		state(ly_);
+		state(ds);
+	}
 };
 
 }
diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp
index 2b1b380..9a92044 100644
--- a/libgambatte/src/video/lyc_irq.cpp
+++ b/libgambatte/src/video/lyc_irq.cpp
@@ -22,6 +22,10 @@
 #include "savestate.h"
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 LycIrq::LycIrq() :
@@ -34,14 +38,14 @@ LycIrq::LycIrq() :
 {
 }
 
-static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
+static unsigned schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
 	return (statReg & 0x40) && lycReg < 154
 		? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
-		: static_cast<unsigned long>(DISABLED_TIME);
+		: static_cast<unsigned>(DISABLED_TIME);
 }
 
-void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
-	const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
+void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
+	const unsigned timeSrc = schedule(statReg, lycReg, lyCounter, cc);
 	statRegSrc_ = statReg;
 	lycRegSrc_ = lycReg;
 	time_ = std::min(time_, timeSrc);
@@ -89,7 +93,7 @@ void LycIrq::saveState(SaveState &state) const {
 	state.ppu.lyc = lycReg_;
 }
 
-void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
+void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned cc) {
 	time_ = std::min(schedule(statReg_   , lycReg_   , lyCounter, cc),
 	                 schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
 }
diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h
index 019914d..93a064f 100644
--- a/libgambatte/src/video/lyc_irq.h
+++ b/libgambatte/src/video/lyc_irq.h
@@ -19,20 +19,26 @@
 #ifndef VIDEO_LYC_IRQ_H
 #define VIDEO_LYC_IRQ_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
 namespace gambatte {
 
 struct SaveState;
 class LyCounter;
 
 class LycIrq {
-	unsigned long time_;
+	unsigned time_;
  	unsigned char lycRegSrc_;
  	unsigned char statRegSrc_;
 	unsigned char lycReg_;
 	unsigned char statReg_;
 	bool cgb_;
 	
-	void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
+	void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned cc);
 	
 public:
 	LycIrq();
@@ -40,18 +46,27 @@ public:
 	unsigned lycReg() const { return lycRegSrc_; }
 	void loadState(const SaveState &state);
 	void saveState(SaveState &state) const;
-	unsigned long time() const { return time_; }
+	unsigned time() const { return time_; }
 	void setCgb(const bool cgb) { cgb_ = cgb; }
 	void lcdReset();
-	void reschedule(const LyCounter & lyCounter, unsigned long cc);
+	void reschedule(const LyCounter & lyCounter, unsigned cc);
 	
-	void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
+	void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned cc) {
 		regChange(statReg, lycRegSrc_, lyCounter, cc);
 	}
 	
-	void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
+	void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned cc) {
 		regChange(statRegSrc_, lycReg, lyCounter, cc);
 	}
+
+	void loadOrSave(loadsave& state) {
+		state(time_);
+		state(lycRegSrc_);
+		state(statRegSrc_);
+		state(lycReg_);
+		state(statReg_);
+		state(cgb_);
+	}
 };
 
 }
diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h
index 148f471..1b0c338 100644
--- a/libgambatte/src/video/next_m0_time.h
+++ b/libgambatte/src/video/next_m0_time.h
@@ -1,6 +1,12 @@
 #ifndef NEXT_M0_TIME_H_
 #define NEXT_M0_TIME_H_
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
 namespace gambatte {
 
 class NextM0Time {
@@ -11,6 +17,10 @@ public:
 	void predictNextM0Time(const class PPU &v);
 	void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
 	unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
+
+	void loadOrSave(loadsave& state) {
+		state(predictedNextM0Time_);
+	}
 };
 
 }
diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp
index cd8c057..8844692 100644
--- a/libgambatte/src/video/ppu.cpp
+++ b/libgambatte/src/video/ppu.cpp
@@ -22,6 +22,10 @@
 #include <cstring>
 #include <cstddef>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace {
 
 using namespace gambatte;
@@ -426,7 +430,7 @@ namespace M3Loop {
 						
 						const unsigned attrib = p.spriteList[i].attrib;
 						unsigned spword       = p.spwordList[i];
-						const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4);
+						const uint_least32_t *const spPalette = p.spPalette + (attrib >> 2 & 4);
 						
 						if (!(attrib & 0x80)) {
 							switch (n) {
@@ -588,7 +592,7 @@ namespace M3Loop {
 				uint_least32_t *const dst = dbufline + (xpos - 8);
 				const unsigned tileword = p.ntileword;
 				const unsigned attrib   = p.nattrib;
-				const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4;
+				const uint_least32_t *const bgPalette = p.bgPalette + (attrib & 7) * 4;
 				
 				dst[0] = bgPalette[tileword       & 3];
 				dst[1] = bgPalette[tileword >>  2 & 3];
@@ -624,7 +628,7 @@ namespace M3Loop {
 						const unsigned id      = p.spriteList[i].oampos;
 						const unsigned sattrib = p.spriteList[i].attrib;
 						unsigned spword        = p.spwordList[i];
-						const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4;
+						const uint_least32_t *const spPalette = p.spPalette + (sattrib & 7) * 4;
 						
 						if (!((attrib | sattrib) & bgenmask)) {
 							switch (n) {
@@ -781,7 +785,7 @@ namespace M3Loop {
 		}
 		
 		const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3;
-		unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
+		uint_least32_t pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
 		int i = static_cast<int>(p.nextSprite) - 1;
 		
 		if (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8) {
@@ -840,8 +844,8 @@ namespace M3Loop {
 			plotPixel(p);
 	}
 	
-	static unsigned long nextM2Time(const PPUPriv &p) {
-		unsigned long nextm2 = p.lyCounter.isDoubleSpeed()
+	static unsigned nextM2Time(const PPUPriv &p) {
+		unsigned nextm2 = p.lyCounter.isDoubleSpeed()
 				? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2
 				: p.lyCounter.time() +  weMasterCheckPriorToLyIncLineCycle(p.cgb)                     - 456    ;
 
@@ -854,11 +858,11 @@ namespace M3Loop {
 	static void xpos168(PPUPriv &p) {
 		p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed());
 		
-		const unsigned long nextm2 = nextM2Time(p);
+		const unsigned nextm2 = nextM2Time(p);
 		
 		p.cycles = p.now >= nextm2
-				?  (static_cast<long>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
-				: -(static_cast<long>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
+				?  (static_cast<signed>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
+				: -(static_cast<signed>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
 				
 		nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p);
 	}
@@ -1542,8 +1546,8 @@ std::size_t upperBound(const T a[], const K e) {
 
 struct CycleState {
 	const PPUState *state;
-	long cycle;
-	operator long() const { return cycle; }
+	signed cycle;
+	operator signed() const { return cycle; }
 };
 
 static const PPUState * decodeM3LoopState(const unsigned state) {
@@ -1573,8 +1577,8 @@ static const PPUState * decodeM3LoopState(const unsigned state) {
 	return 0;
 }
 
-static long cyclesUntilM0Upperbound(const PPUPriv &p) {
-	long cycles = 168 - p.xpos + 6;
+static signed cyclesUntilM0Upperbound(const PPUPriv &p) {
+	signed cycles = 168 - p.xpos + 6;
 	
 	for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i)
 		cycles += 11;
@@ -1614,17 +1618,17 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) {
 
 void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
 	const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state);
-	const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL);
+	const signed videoCycles = std::min(ss.ppu.videoCycles, 70223u);
 	const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7;
-	const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0
+	const signed vcycs = videoCycles - ds * M2_DS_OFFSET < 0
 	                 ? videoCycles - ds * M2_DS_OFFSET + 70224
 	                 : videoCycles - ds * M2_DS_OFFSET;
-	const long lineCycles = static_cast<unsigned long>(vcycs) % 456;
+	const signed lineCycles = static_cast<unsigned>(vcycs) % 456;
 	
 	p_.now = ss.cpu.cycleCounter;
 	p_.lcdc = ss.mem.ioamhram.get()[0x140];
 	p_.lyCounter.setDoubleSpeed(ds);
-	p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter);
+	p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223u), ss.cpu.cycleCounter);
 	p_.spriteMapper.loadState(ss, oamram);
 	p_.winYPos = ss.ppu.winYPos;
 	p_.scy = ss.mem.ioamhram.get()[0x142];
@@ -1648,10 +1652,10 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
 	loadSpriteList(p_, ss);
 	
 	if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168
-			&& lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<long>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
+			&& lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<signed>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
 		p_.nextCallPtr = m3loopState;
 		p_.cycles = -1;
-	} else if (vcycs < 143 * 456L + static_cast<long>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
+	} else if (vcycs < 143 * 456L + static_cast<signed>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
 		const struct CycleState lineCycleStates[] = {
 			{    &M3Start::f0_, m3StartLineCycle(p_.cgb) },
 			{    &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES },
@@ -1681,9 +1685,9 @@ void PPU::reset(const unsigned char *const oamram, const bool cgb) {
 	p_.spriteMapper.reset(oamram, cgb);
 }
 
-void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
-	const unsigned long dec = oldCc - newCc;
-	const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
+void PPU::resetCc(const unsigned oldCc, const unsigned newCc) {
+	const unsigned dec = oldCc - newCc;
+	const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
 	
 	p_.now -= dec;
 	p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time;
@@ -1691,8 +1695,8 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
 	p_.spriteMapper.resetCycleCounter(oldCc, newCc);
 }
 
-void PPU::speedChange(const unsigned long cycleCounter) {
-	const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
+void PPU::speedChange(const unsigned cycleCounter) {
+	const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
 	
 	p_.spriteMapper.preSpeedChange(cycleCounter);
 	p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed());
@@ -1707,11 +1711,11 @@ void PPU::speedChange(const unsigned long cycleCounter) {
 	}
 }
 
-unsigned long PPU::predictedNextXposTime(const unsigned xpos) const {
+unsigned PPU::predictedNextXposTime(const unsigned xpos) const {
 	return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed());
 }
 
-void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
+void PPU::setLcdc(const unsigned lcdc, const unsigned cc) {
 	if ((p_.lcdc ^ lcdc) & lcdc & 0x80) {
 		p_.now = cc;
 		p_.lastM0Time = 0;
@@ -1741,7 +1745,7 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
 	p_.lcdc = lcdc;
 }
 
-void PPU::update(const unsigned long cc) {
+void PPU::update(const unsigned cc) {
 	const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed();
 	
 	p_.now += cycles << p_.lyCounter.isDoubleSpeed();
@@ -1753,4 +1757,69 @@ void PPU::update(const unsigned long cc) {
 	}
 }
 
+void PPUPriv::loadOrSave(loadsave& state)
+{
+	state(bgPalette, 32);
+	state(spPalette, 32);
+
+	state.startEnumeration();
+	state.enumerate<const PPUState*>(nextCallPtr, NULL, 0);
+	state.enumerate<const PPUState*>(nextCallPtr, &M2::Ly0::f0_, 1);
+	state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f0_, 2);
+	state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f1_, 3);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f0_, 4);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f1_, 5);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f0_, 6);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f1_, 7);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f2_, 8);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f3_, 9);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f4_, 10);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f5_, 11);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
+	state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
+	state.endEnumeration();
+	
+	state(now);
+	state(lastM0Time);
+	state(cycles);
+	state(tileword);
+	state(ntileword);
+	lyCounter.loadOrSave(state);
+	spriteMapper.loadOrSave(state);
+	framebuf.loadOrSave(state);
+
+	for(size_t i = 0; i < 11; i++)
+		spriteList[i].loadOrSave(state);
+	state(spwordList, 11);
+	state(lcdc);
+	state(scy);
+	state(scx);
+	state(wy);
+	state(wy2);
+	state(wx);
+	state(winDrawState);
+	state(wscx);
+	state(winYPos);
+	state(reg0);
+	state(reg1);
+	state(attrib);
+	state(nattrib);
+	state(nextSprite);
+	state(currentSprite);
+	state(xpos);
+	state(endx);
+	state(cgb);
+	state(weMaster);
+}
+
 }
diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h
index f48c922..dfd7fb5 100644
--- a/libgambatte/src/video/ppu.h
+++ b/libgambatte/src/video/ppu.h
@@ -19,26 +19,45 @@
 #ifndef PPU_H
 #define PPU_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
+#include <cstring>
 #include "video/ly_counter.h"
 #include "video/sprite_mapper.h"
 #include "gbint.h"
+#include "../loadsave.h"
 
 namespace gambatte {
 
 class PPUFrameBuf {
-	uint_least32_t *buf_;
+	mutable uint_least32_t buf_[160*144];
 	uint_least32_t *fbline_;
-	int pitch_;
 	
 	static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
 	
 public:
-	PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
+	PPUFrameBuf() : fbline_(nullfbline()) { memset(buf_, 0, sizeof(buf_)); }
 	uint_least32_t * fb() const { return buf_; }
 	uint_least32_t * fbline() const { return fbline_; }
-	int pitch() const { return pitch_; }
-	void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
-	void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
+	int pitch() const { return 160; }
+	void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + ly * 160ULL : nullfbline(); }
+	void blit(uint_least32_t *const buf, const int pitch) const {
+		for(unsigned i = 0; i < 144; i++)
+			memcpy(buf + i * static_cast<signed>(pitch), buf_ + i * 160, 160 * sizeof(buf[0]));
+	}
+	void loadOrSave(loadsave& state) {
+		state(buf_, 160*144);
+		bool var = (fbline_ != nullfbline());
+		state(var);
+		if(var) {
+			unsigned x = fbline_ - buf_;
+			state(x);
+			fbline_ = buf_ + x;
+		} else
+			fbline_ = nullfbline();
+	}
 };
 
 struct PPUState {
@@ -49,15 +68,15 @@ struct PPUState {
 
 // The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
 struct PPUPriv {
-	unsigned long bgPalette[8 * 4];
-	unsigned long spPalette[8 * 4];
+	uint_least32_t bgPalette[8 * 4];
+	uint_least32_t spPalette[8 * 4];
 
 	const unsigned char *const vram;
 	const PPUState *nextCallPtr;
 
-	unsigned long now;
-	unsigned long lastM0Time;
-	long cycles;
+	unsigned now;
+	unsigned lastM0Time;
+	signed cycles;
 
 	unsigned tileword;
 	unsigned ntileword;
@@ -66,7 +85,15 @@ struct PPUPriv {
 	SpriteMapper spriteMapper;
 	PPUFrameBuf framebuf;
 	
-	struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
+	struct Sprite { 
+		unsigned char spx, oampos, line, attrib;
+		void loadOrSave(loadsave& state) {
+			state(spx);
+			state(oampos);
+			state(line);
+			state(attrib);
+		}
+	} spriteList[11];
 	unsigned short spwordList[11];
 
 	unsigned char lcdc;
@@ -91,6 +118,7 @@ struct PPUPriv {
 	bool weMaster;
 	
 	PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
+	void loadOrSave(loadsave& state);
 };
 
 class PPU {
@@ -101,34 +129,38 @@ public:
 	{
 	}
 	
-	unsigned long * bgPalette() { return p_.bgPalette; }
+	uint_least32_t * bgPalette() { return p_.bgPalette; }
 	bool cgb() const { return p_.cgb; }
 	void doLyCountEvent() { p_.lyCounter.doEvent(); }
-	unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
+	unsigned doSpriteMapEvent(unsigned time) { return p_.spriteMapper.doEvent(time); }
 	const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
-	bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
-	unsigned long lastM0Time() const { return p_.lastM0Time; }
+	bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
+	unsigned lastM0Time() const { return p_.lastM0Time; }
 	unsigned lcdc() const { return p_.lcdc; }
 	void loadState(const SaveState &state, const unsigned char *oamram);
 	const LyCounter & lyCounter() const { return p_.lyCounter; }
-	unsigned long now() const { return p_.now; }
-	void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
-	void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
-	unsigned long predictedNextXposTime(unsigned xpos) const;
+	unsigned now() const { return p_.now; }
+	void oamChange(unsigned cc) { p_.spriteMapper.oamChange(cc); }
+	void oamChange(const unsigned char *oamram, unsigned cc) { p_.spriteMapper.oamChange(oamram, cc); }
+	unsigned predictedNextXposTime(unsigned xpos) const;
 	void reset(const unsigned char *oamram, bool cgb);
-	void resetCc(unsigned long oldCc, unsigned long newCc);
+	void resetCc(unsigned oldCc, unsigned newCc);
 	void saveState(SaveState &ss) const;
-	void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
-	void setLcdc(unsigned lcdc, unsigned long cc);
+	void flipDisplay(uint_least32_t *buf, unsigned pitch) { p_.framebuf.blit(buf, pitch); }
+	void setLcdc(unsigned lcdc, unsigned cc);
 	void setScx(const unsigned scx) { p_.scx = scx; }
 	void setScy(const unsigned scy) { p_.scy = scy; }
 	void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
 	void setWx(const unsigned wx) { p_.wx = wx; }
 	void setWy(const unsigned wy) { p_.wy = wy; }
 	void updateWy2() { p_.wy2 = p_.wy; }
-	void speedChange(unsigned long cycleCounter);
-	unsigned long * spPalette() { return p_.spPalette; }
-	void update(unsigned long cc);
+	void speedChange(unsigned cycleCounter);
+	uint_least32_t * spPalette() { return p_.spPalette; }
+	void update(unsigned cc);
+
+	void loadOrSave(loadsave& state) {
+		p_.loadOrSave(state);
+	}
 };
 
 }
diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp
index a68d14f..512bf41 100644
--- a/libgambatte/src/video/sprite_mapper.cpp
+++ b/libgambatte/src/video/sprite_mapper.cpp
@@ -23,6 +23,10 @@
 #include <cstring>
 #include <algorithm>
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 namespace gambatte {
 
 SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
@@ -47,7 +51,7 @@ void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const boo
 	}
 }
 
-static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
+static unsigned toPosCycles(const unsigned cc, const LyCounter &lyCounter) {
 	unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
 
 	if (lc >= 456)
@@ -56,7 +60,7 @@ static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter)
 	return lc;
 }
 
-void SpriteMapper::OamReader::update(const unsigned long cc) {
+void SpriteMapper::OamReader::update(const unsigned cc) {
 	if (cc > lu) {
 		if (changed()) {
 			const unsigned lulc = toPosCycles(lu, lyCounter);
@@ -100,7 +104,7 @@ void SpriteMapper::OamReader::update(const unsigned long cc) {
 	}
 }
 
-void SpriteMapper::OamReader::change(const unsigned long cc) {
+void SpriteMapper::OamReader::change(const unsigned cc) {
 	update(cc);
 	lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
 }
@@ -117,7 +121,7 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char
 	change(lu);
 }
 
-void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
+void SpriteMapper::OamReader::enableDisplay(const unsigned cc) {
 	std::memset(buf, 0x00, sizeof(buf));
 	std::fill(szbuf, szbuf + 40, false);
 	lu = cc + (80 << lyCounter.isDoubleSpeed());
@@ -130,6 +134,7 @@ SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
 	nextM0Time_(nextM0Time),
 	oamReader(lyCounter, oamram)
 {
+	memset(spritemap, 0, sizeof(spritemap));
 	clearMap();
 }
 
@@ -180,10 +185,10 @@ void SpriteMapper::sortLine(const unsigned ly) const {
 	insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
 }
 
-unsigned long SpriteMapper::doEvent(const unsigned long time) {
+unsigned SpriteMapper::doEvent(const unsigned time) {
 	oamReader.update(time);
 	mapSprites();
-	return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
+	return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned>(DISABLED_TIME);
 }
 
 }
diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h
index b103b25..b0bbd3d 100644
--- a/libgambatte/src/video/sprite_mapper.h
+++ b/libgambatte/src/video/sprite_mapper.h
@@ -19,8 +19,13 @@
 #ifndef SPRITE_MAPPER_H
 #define SPRITE_MAPPER_H
 
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+//	- Make it rerecording-friendly.
+
 #include "ly_counter.h"
 #include "../savestate.h"
+#include "../loadsave.h"
 
 namespace gambatte {
 class NextM0Time;
@@ -35,7 +40,7 @@ class SpriteMapper {
 
 	private:
 		const unsigned char *oamram;
-		unsigned long lu;
+		unsigned lu;
 		unsigned char lastChange;
 		bool largeSpritesSrc;
 		bool cgb_;
@@ -43,20 +48,30 @@ class SpriteMapper {
 	public:
 		OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
 		void reset(const unsigned char *oamram, bool cgb);
-		void change(unsigned long cc);
-		void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
+		void change(unsigned cc);
+		void change(const unsigned char *oamram, unsigned cc) { change(cc); this->oamram = oamram; }
 		bool changed() const { return lastChange != 0xFF; }
 		bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
 		const unsigned char *oam() const { return oamram; }
-		void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
+		void resetCycleCounter(const unsigned oldCc, const unsigned newCc) { lu -= oldCc - newCc; }
 		void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
-		void update(unsigned long cc);
+		void update(unsigned cc);
 		const unsigned char *spritePosBuf() const { return buf; }
 		void setStatePtrs(SaveState &state);
-		void enableDisplay(unsigned long cc);
+		void enableDisplay(unsigned cc);
 		void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; }
 		void loadState(const SaveState &ss, const unsigned char *oamram);
-		bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
+		bool inactivePeriodAfterDisplayEnable(const unsigned cc) const { return cc < lu; }
+
+		void loadOrSave(loadsave& state) {
+			state(buf, 80);
+			for(unsigned i = 0; i < 40; i++)
+				state(szbuf[i]);
+			state(lu);
+			state(lastChange);
+			state(largeSpritesSrc);
+			state(cgb_);
+		}
 	};
 
 	enum { NEED_SORTING_MASK = 0x80 };
@@ -89,22 +104,22 @@ public:
 	             const LyCounter &lyCounter,
 	             const unsigned char *oamram_in);
 	void reset(const unsigned char *oamram, bool cgb);
-	unsigned long doEvent(unsigned long time);
+	unsigned doEvent(unsigned time);
 	bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
 	unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
-	void oamChange(unsigned long cc) { oamReader.change(cc); }
-	void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
+	void oamChange(unsigned cc) { oamReader.change(cc); }
+	void oamChange(const unsigned char *oamram, unsigned cc) { oamReader.change(oamram, cc); }
 	const unsigned char *oamram() const { return oamReader.oam(); }
 	const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
-	void  preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
-	void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
+	void  preSpeedChange(const unsigned cc) { oamReader.update(cc); }
+	void postSpeedChange(const unsigned cc) { oamReader.change(cc); }
 
-	void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
+	void resetCycleCounter(const unsigned oldCc, const unsigned newCc) {
 		oamReader.update(oldCc);
 		oamReader.resetCycleCounter(oldCc, newCc);
 	}
 
-	static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
+	static unsigned schedule(const LyCounter &lyCounter, const unsigned cycleCounter) {
 		return lyCounter.nextLineCycle(80, cycleCounter);
 	}
 
@@ -118,10 +133,16 @@ public:
 	}
 
 	void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
-	void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
+	void enableDisplay(unsigned cc) { oamReader.enableDisplay(cc); }
 	void saveState(SaveState &state) const { oamReader.saveState(state); }
 	void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); }
-	bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
+	bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
+
+	void loadOrSave(loadsave& state) {
+		state(spritemap, 1440);
+		state(num, 144);
+		oamReader.loadOrSave(state);
+	}
 };
 
 }
-- 
1.7.9.48.g85da4d

