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); }; }