From 8cdcae462d7cd451760b404f8d760269f0b81ff7 Mon Sep 17 00:00:00 2001 From: goyuken Date: Sun, 9 Sep 2012 18:47:00 +0000 Subject: [PATCH] gambatte savestates. slightly modifies some parts of the core and the pinvoke hookup to produce memory arrays instead of disk files as savestates. does not work, for now. (sorry about the exceptions!) --- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 32 ++++++++- .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 40 +++++++----- libgambatte/include/gambatte.h | 9 +-- libgambatte/src/cinterface.cpp | 29 +++++++-- libgambatte/src/cinterface.h | 15 +++-- libgambatte/src/gambatte.cpp | 12 ++-- libgambatte/src/statesaver.cpp | 65 ++++++++++--------- libgambatte/src/statesaver.h | 4 +- 8 files changed, 134 insertions(+), 72 deletions(-) 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); }; }