diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
index 442fe2f2c2..dbfaeb27bc 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -158,27 +158,53 @@ namespace BizHawk.Emulation.Consoles.GB
public void SaveStateText(System.IO.TextWriter writer)
{
-
+ var temp = SaveStateBinary();
+ temp.SaveAsHex(writer);
}
public void LoadStateText(System.IO.TextReader reader)
{
-
+ string hex = reader.ReadLine();
+ byte[] state = new byte[hex.Length / 2];
+ state.ReadFromHex(hex);
+ LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void SaveStateBinary(System.IO.BinaryWriter writer)
{
+ uint nlen = 0;
+ IntPtr ndata = IntPtr.Zero;
+ if (!LibGambatte.gambatte_savestate(GambatteState, VideoBuffer, 160, ref ndata, ref nlen))
+ throw new Exception("Gambatte failed to save the savestate!");
+
+ if (nlen == 0)
+ throw new Exception("Gambatte returned a 0-length savestate?");
+
+ byte[] data = new byte[nlen];
+ System.Runtime.InteropServices.Marshal.Copy(ndata, data, 0, (int)nlen);
+ LibGambatte.gambatte_savestate_destroy(ndata);
+
+ writer.Write((int)nlen);
+ writer.Write(data);
}
public void LoadStateBinary(System.IO.BinaryReader reader)
{
+ int length = reader.ReadInt32();
+ byte[] data = reader.ReadBytes(length);
+ if (!LibGambatte.gambatte_loadstate(GambatteState, data, (uint)length))
+ throw new Exception("Gambatte failed to load the savestate!");
}
public byte[] SaveStateBinary()
{
- return new byte[0];
+ MemoryStream ms = new MemoryStream();
+ BinaryWriter bw = new BinaryWriter(ms);
+ SaveStateBinary(bw);
+ bw.Flush();
+ return ms.ToArray();
}
diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
index 89386f61d5..3dd7414c04 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -161,23 +161,33 @@ namespace BizHawk.Emulation.Consoles.GB
public static extern void gambatte_savesavedata(IntPtr core);
///
- /// Saves emulator state to the state slot selected with gambatte_selectstate().
- /// The data will be stored in the directory given by gambatte_setsavedir().
+ /// Saves emulator state to the state to a byte array
///
/// opaque state pointer
/// 160x144 RGB32 (native endian) video frame buffer or 0. Used for saving a thumbnail.
/// distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
- ///
- [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool gambatte_savestate(IntPtr core, int[] videobuf, int pitch);
-
- ///
- /// Loads emulator state from the state slot selected with selectState().
- ///
- /// opaque state pointer
+ /// private savestate data returned by the core
+ /// the length of the data in bytes
/// success
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool gambatte_loadstate(IntPtr core);
+ public static extern bool gambatte_savestate(IntPtr core, int[] videobuf, int pitch, ref IntPtr data, ref uint len);
+
+ ///
+ /// destroy data returned by gambatte_savestate() to avoid memory leaks
+ ///
+ /// pointer from gambatte_savestate()
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void gambatte_savestate_destroy(IntPtr data);
+
+ ///
+ /// Loads emulator state from the byte array
+ ///
+ /// opaque state pointer
+ /// savestate data
+ /// length of the savestate data in bytes
+ /// success
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool gambatte_loadstate(IntPtr core, byte[] data, uint len);
///
/// Saves emulator state to the file given by 'filepath'.
@@ -187,8 +197,8 @@ namespace BizHawk.Emulation.Consoles.GB
/// distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
///
/// success
- [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool gambatte_savestate_file(IntPtr core, int[] videobuf, int pitch, string filepath);
+ //[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ //public static extern bool gambatte_savestate_file(IntPtr core, int[] videobuf, int pitch, string filepath);
///
/// Loads emulator state from the file given by 'filepath'.
@@ -196,8 +206,8 @@ namespace BizHawk.Emulation.Consoles.GB
/// opaque state pointer
///
/// success
- [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool gambatte_loadstate_file(IntPtr core, string filepath);
+ //[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ //public static extern bool gambatte_loadstate_file(IntPtr core, string filepath);
///
/// Selects which state slot to save state to or load state from.
diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index efc28016fa..993899d717 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -22,6 +22,7 @@
#include "inputgetter.h"
#include "gbint.h"
#include
+#include
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
@@ -101,12 +102,12 @@ public:
* @param pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
* @return success
*/
- bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch);
+ //bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch);
/** Loads emulator state from the state slot selected with selectState().
* @return success
*/
- bool loadState();
+ //bool loadState();
/** Saves emulator state to the file given by 'filepath'.
*
@@ -114,12 +115,12 @@ public:
* @param pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
* @return success
*/
- bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch, const std::string &filepath);
+ bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch, std::ostream &file);
/** Loads emulator state from the file given by 'filepath'.
* @return success
*/
- bool loadState(const std::string &filepath);
+ bool loadState(std::istream &file);
/** 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).
diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp
index 37a8f49417..3d6e4f34d8 100644
--- a/libgambatte/src/cinterface.cpp
+++ b/libgambatte/src/cinterface.cpp
@@ -1,5 +1,6 @@
#include "cinterface.h"
#include "gambatte.h"
+#include
using namespace gambatte;
@@ -88,6 +89,7 @@ __declspec(dllexport) void gambatte_savesavedata(void *core)
g->saveSavedata();
}
+/*
__declspec(dllexport) int gambatte_savestate(void *core, const unsigned long *videobuf, int pitch)
{
GB *g = (GB *) core;
@@ -99,19 +101,37 @@ __declspec(dllexport) int gambatte_loadstate(void *core)
GB *g = (GB *) core;
return g->loadState();
}
+*/
-__declspec(dllexport) int gambatte_savestate_file(void *core, const unsigned long *videobuf, int pitch, const char *filepath)
+__declspec(dllexport) int gambatte_savestate(void *core, const unsigned long *videobuf, int pitch, char **data, unsigned *len)
{
GB *g = (GB *) core;
- return g->saveState(videobuf, pitch, std::string(filepath));
+
+ std::ostringstream os = std::ostringstream(std::ios_base::binary | std::ios_base::out);
+ if (!g->saveState(videobuf, pitch, os))
+ return 0;
+
+ os.flush();
+ std::string s = os.str();
+ char *ret = (char *) std::malloc(s.length());
+ std::memcpy(ret, s.data(), s.length());
+ *len = s.length();
+ *data = ret;
+ return 1;
}
-__declspec(dllexport) int gambatte_loadstate_file(void *core, const char *filepath)
+__declspec(dllexport) void gambatte_savestate_destroy(char *data)
+{
+ std::free(data);
+}
+
+__declspec(dllexport) int gambatte_loadstate(void *core, const char *data, unsigned len)
{
GB *g = (GB *) core;
- return g->loadState(std::string(filepath));
+ return g->loadState(std::istringstream(std::string(data, len), std::ios_base::binary | std::ios_base::in));
}
+/*
__declspec(dllexport) void gambatte_selectstate(void *core, int n)
{
GB *g = (GB *) core;
@@ -123,6 +143,7 @@ __declspec(dllexport) int gambatte_currentstate(void *core)
GB *g = (GB *) core;
return g->currentState();
}
+*/
static char horriblebuff[64];
__declspec(dllexport) const char *gambatte_romtitle(void *core)
diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h
index 33400004d8..b6c1cd66ef 100644
--- a/libgambatte/src/cinterface.h
+++ b/libgambatte/src/cinterface.h
@@ -1,6 +1,8 @@
#ifndef CINTERFACE_H
#define CINTERFACE_H
+// these are all documented on the C# side
+
extern "C"
{
__declspec(dllexport) void *gambatte_create();
@@ -24,17 +26,18 @@ extern "C"
__declspec(dllexport) void gambatte_savesavedata(void *core);
- __declspec(dllexport) int gambatte_savestate(void *core, const unsigned long *videobuf, int pitch);
+ //__declspec(dllexport) int gambatte_savestate(void *core, const unsigned long *videobuf, int pitch);
- __declspec(dllexport) int gambatte_loadstate(void *core);
+ //__declspec(dllexport) int gambatte_loadstate(void *core);
- __declspec(dllexport) int gambatte_savestate_file(void *core, const unsigned long *videobuf, int pitch, const char *filepath);
+ __declspec(dllexport) int gambatte_savestate(void *core, const unsigned long *videobuf, int pitch, char **data, unsigned *len);
+ __declspec(dllexport) void gambatte_savestate_destroy(char *data);
- __declspec(dllexport) int gambatte_loadstate_file(void *core, const char *filepath);
+ __declspec(dllexport) int gambatte_loadstate(void *core, const char *data, unsigned len);
- __declspec(dllexport) void gambatte_selectstate(void *core, int n);
+ //__declspec(dllexport) void gambatte_selectstate(void *core, int n);
- __declspec(dllexport) int gambatte_currentstate(void *core);
+ //__declspec(dllexport) int gambatte_currentstate(void *core);
__declspec(dllexport) const char *gambatte_romtitle(void *core);
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index 27765287aa..9302ebda28 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -125,14 +125,14 @@ void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32)
p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32);
}
-bool GB::loadState(const std::string &filepath) {
+bool GB::loadState(std::istream &file) {
if (p_->cpu.loaded()) {
p_->cpu.saveSavedata();
SaveState state;
p_->cpu.setStatePtrs(state);
- if (StateSaver::loadState(state, filepath)) {
+ if (StateSaver::loadState(state, file)) {
p_->cpu.loadState(state);
return true;
}
@@ -140,7 +140,7 @@ bool GB::loadState(const std::string &filepath) {
return false;
}
-
+/*
bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pitch) {
if (saveState(videoBuf, pitch, statePath(p_->cpu.saveBasePath(), p_->stateNo))) {
p_->cpu.setOsdElement(newStateSavedOsdElement(p_->stateNo));
@@ -158,13 +158,13 @@ bool GB::loadState() {
return false;
}
-
-bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pitch, const std::string &filepath) {
+*/
+bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pitch, std::ostream &file) {
if (p_->cpu.loaded()) {
SaveState state;
p_->cpu.setStatePtrs(state);
p_->cpu.saveState(state);
- return StateSaver::saveState(state, videoBuf, pitch, filepath);
+ return StateSaver::saveState(state, videoBuf, pitch, file);
}
return false;
diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp
index 4564eeb922..26ab47d6e8 100644
--- a/libgambatte/src/statesaver.cpp
+++ b/libgambatte/src/statesaver.cpp
@@ -22,7 +22,8 @@
#include
#include
#include
-#include
+#include
+#include
namespace {
@@ -41,8 +42,8 @@ enum AsciiChar {
struct Saver {
const char *label;
- void (*save)(std::ofstream &file, const SaveState &state);
- void (*load)(std::ifstream &file, SaveState &state);
+ void (*save)(std::ostream &file, const SaveState &state);
+ void (*load)(std::istream &file, SaveState &state);
unsigned char labelsize;
};
@@ -50,27 +51,27 @@ 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 +79,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 long 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(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(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,7 +110,7 @@ static unsigned long get24(std::ifstream &file) {
return tmp << 8 | (file.get() & 0xFF);
}
-static unsigned long read(std::ifstream &file) {
+static unsigned long read(std::istream &file) {
unsigned long size = get24(file);
if (size > 4) {
@@ -129,23 +130,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 long &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 +161,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 +194,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 +203,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 +212,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 +221,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 +374,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) {
@@ -411,8 +412,8 @@ namespace gambatte {
bool 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);
+ const int pitch, std::ostream &file) {
+ //std::ostream file(filename.c_str(), std::ios_base::binary);
if (file.fail())
return false;
@@ -429,8 +430,8 @@ bool StateSaver::saveState(const SaveState &state,
return !file.fail();
}
-bool StateSaver::loadState(SaveState &state, const std::string &filename) {
- std::ifstream file(filename.c_str(), std::ios_base::binary);
+bool StateSaver::loadState(SaveState &state, std::istream &file) {
+ //std::istream file(filename.c_str(), std::ios_base::binary);
if (file.fail() || file.get() != 0)
return false;
diff --git a/libgambatte/src/statesaver.h b/libgambatte/src/statesaver.h
index 6491bac409..dec12fc998 100644
--- a/libgambatte/src/statesaver.h
+++ b/libgambatte/src/statesaver.h
@@ -36,8 +36,8 @@ public:
enum { SS_HEIGHT = 144 >> SS_SHIFT };
static bool saveState(const SaveState &state,
- const uint_least32_t *videoBuf, int pitch, const std::string &filename);
- static bool loadState(SaveState &state, const std::string &filename);
+ const uint_least32_t *videoBuf, int pitch, std::ostream &file);
+ static bool loadState(SaveState &state, std::istream &file);
};
}