diff --git a/bsnes/nall/directory.hpp b/bsnes/nall/directory.hpp index 85e9acaf..d6ab5cc7 100755 --- a/bsnes/nall/directory.hpp +++ b/bsnes/nall/directory.hpp @@ -46,7 +46,7 @@ struct directory { } FindClose(handle); } - sort(&list[0], list.size()); + if(list.size() > 0) sort(&list[0], list.size()); return list; } @@ -70,7 +70,7 @@ struct directory { } FindClose(handle); } - sort(&list[0], list.size()); + if(list.size() > 0) sort(&list[0], list.size()); return list; } @@ -94,7 +94,7 @@ struct directory { } closedir(dp); } - sort(&list[0], list.size()); + if(list.size() > 0) sort(&list[0], list.size()); return list; } @@ -112,7 +112,7 @@ struct directory { } closedir(dp); } - sort(&list[0], list.size()); + if(list.size() > 0) sort(&list[0], list.size()); return list; } diff --git a/bsnes/nall/function.hpp b/bsnes/nall/function.hpp index acf39b61..e35f5bc4 100755 --- a/bsnes/nall/function.hpp +++ b/bsnes/nall/function.hpp @@ -1,103 +1,56 @@ #ifndef NALL_FUNCTION_HPP #define NALL_FUNCTION_HPP -#include -#include -#include - namespace nall { template class function; - template - class function { - private: - struct base1 { virtual void func1(P...) {} }; - struct base2 { virtual void func2(P...) {} }; - struct derived : base1, virtual base2 {}; + template class function { + struct container { + virtual R operator()(P... p) const = 0; + virtual container* copy() const = 0; + } *callback; - struct data_t { - R (*callback)(const data_t&, P...); - R (*callback_global)(P...); - struct { - R (derived::*callback_member)(P...); - void *callback_object; - }; - std::function callback_lambda; - } data; + struct global : container { + R (*function)(P...); + R operator()(P... p) const { return function(std::forward

(p)...); } + container* copy() const { return new global(function); } + global(R (*function_)(P...)) : function(function_) {} + }; - static R callback_global(const data_t &data, P... p) { - return data.callback_global(std::forward

(p)...); - } + template struct member : container { + R (C::*function)(P...); + C *object; + R operator()(P... p) const { return (object->*function)(std::forward

(p)...); } + container* copy() const { return new member(function, object); } + member(R (C::*function_)(P...), C *object_) : function(function_), object(object_) {} + }; - template - static R callback_member(const data_t &data, P... p) { - return (((C*)data.callback_object)->*((R (C::*&)(P...))data.callback_member))(std::forward

(p)...); - } - - static R callback_lambda(const data_t &data, P... p) { - return data.callback_lambda(std::forward

(p)...); - } + template struct lambda : container { + L *object; + R operator()(P... p) const { return (*object)(std::forward

(p)...); } + container* copy() const { return new lambda(*object); } + lambda(const L& object_) { object = new L(object_); } + ~lambda() { delete object; } + }; public: - R operator()(P... p) const { return data.callback(data, std::forward

(p)...); } - operator bool() const { return data.callback; } - void reset() { data.callback = 0; } + operator bool() const { return callback; } + R operator()(P... p) const { return (*callback)(std::forward

(p)...); } function& operator=(const function &source) { - data.callback = source.data.callback; - data.callback_global = source.data.callback_global; - data.callback_member = source.data.callback_member; - data.callback_object = source.data.callback_object; - data.callback_lambda = source.data.callback_lambda; + if(callback) { delete callback; callback = 0; } + if(source.callback) callback = source.callback->copy(); return *this; } - function(const function &source) { - operator=(source); - } - - //no pointer - function() { - data.callback = 0; - } - - //symbolic link pointer (nall/dl.hpp::sym, etc) - function(void *callback) { - data.callback = callback ? &callback_global : 0; - data.callback_global = (R (*)(P...))callback; - } - - //global function pointer - function(R (*callback)(P...)) { - data.callback = &callback_global; - data.callback_global = callback; - } - - //member function pointer - template - function(R (C::*callback)(P...), C *object) { - static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); - data.callback = &callback_member; - (R (C::*&)(P...))data.callback_member = callback; - data.callback_object = object; - } - - //const member function pointer - template - function(R (C::*callback)(P...) const, C *object) { - static_assert(sizeof data.callback_member >= sizeof callback, "callback_member is too small"); - data.callback = &callback_member; - (R (C::*&)(P...))data.callback_member = (R (C::*&)(P...))callback; - data.callback_object = object; - } - - //lambda function pointer - template - function(const T &callback) { - static_assert(std::is_same::type>::value, "lambda mismatch"); - data.callback = &callback_lambda; - data.callback_lambda = callback; - } + function(const function &source) { operator=(source); } + function() : callback(0) {} + function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); } + function(R (*function)(P...)) { callback = new global(function); } + template function(R (C::*function)(P...), C *object) { callback = new member(function, object); } + template function(R (C::*function)(P...) const, C *object) { callback = new member((R (C::*)(P...))function, object); } + template function(const L& object) { callback = new lambda(object); } + ~function() { if(callback) delete callback; } }; } diff --git a/bsnes/nall/serializer.hpp b/bsnes/nall/serializer.hpp index 9f816dfe..ff2337ab 100755 --- a/bsnes/nall/serializer.hpp +++ b/bsnes/nall/serializer.hpp @@ -112,6 +112,7 @@ namespace nall { imode = Size; idata = 0; isize = 0; + icapacity = 0; } serializer(unsigned capacity) { diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 9bab3d11..ec84b7b3 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "070.03"; + static const char Version[] = "070.04"; static const unsigned SerializerVersion = 13; } } diff --git a/bsnes/ui-phoenix/cartridge/cartridge.cpp b/bsnes/ui-phoenix/cartridge/cartridge.cpp index 39b88d24..45ea88bd 100755 --- a/bsnes/ui-phoenix/cartridge/cartridge.cpp +++ b/bsnes/ui-phoenix/cartridge/cartridge.cpp @@ -76,8 +76,8 @@ void Cartridge::unload() { saveMemory(SNES::memory::stBram, slotBName, ".srm"); saveMemory(SNES::memory::gbram, slotAName, ".sav"); saveMemory(SNES::memory::gbrtc, slotAName, ".rtc"); - baseName = slotAName = slotBName = ""; utility.cartridgeUnloaded(); + baseName = slotAName = slotBName = ""; } bool Cartridge::loadCartridge(SNES::MappedRAM &memory, string &XML, const char *filename) { diff --git a/bsnes/ui-phoenix/general/file-browser.cpp b/bsnes/ui-phoenix/general/file-browser.cpp index 99ca9fdc..ba847748 100755 --- a/bsnes/ui-phoenix/general/file-browser.cpp +++ b/bsnes/ui-phoenix/general/file-browser.cpp @@ -2,7 +2,7 @@ FileBrowser fileBrowser; void FileBrowser::create() { application.windows.append(this); - Window::create(0, 0, 256, 256, "Load Cartridge"); + Window::create(0, 0, 256, 256); setDefaultFont(application.proportionalFont); unsigned x = 5, y = 5, height = Style::TextBoxHeight; @@ -32,24 +32,31 @@ void FileBrowser::fileOpen(FileBrowser::Mode requestedMode, function 0) { + descEdit.setText(slotLoadDescription(position())); + descEdit.setEnabled(true); + } + } +} + +void StateManager::refresh() { + for(unsigned i = 0; i < 32; i++) { + stateList.setItem(i, string( + strunsigned<2, ' '>(i + 1), "\t", + slotLoadDescription(i) + )); + } + stateList.resizeColumnsToContent(); +} + +void StateManager::load() { + stateList.reset(); + for(unsigned i = 0; i < 32; i++) { + slot[i] = serializer(); + stateList.addItem(""); + } + + string filename = string(cartridge.baseName, ".bsa"); + file fp; + if(fp.open(string(cartridge.baseName, ".bsa"), file::mode_read)) { + if(fp.readl(4) == 0x31415342) { + if(fp.readl(4) == SNES::Info::SerializerVersion) { + for(unsigned i = 0; i < 32; i++) { + if(fp.read() == false) continue; + uint8_t *data = new uint8_t[SNES::system.serialize_size()]; + fp.read(data, SNES::system.serialize_size()); + slot[i] = serializer(data, SNES::system.serialize_size()); + delete[] data; + } + } + } + } + + refresh(); +} + +void StateManager::save() { + bool hasSave = false; + for(unsigned i = 0; i < 32; i++) { + if(slot[i].capacity() > 0) hasSave = true; + } + + if(hasSave == false) { + unlink(string(cartridge.baseName, ".bsa")); + } else { + file fp; + if(fp.open(string(cartridge.baseName, ".bsa"), file::mode_write)) { + fp.writel(0x31415342, 4); //'BSA1' + fp.writel(SNES::Info::SerializerVersion, 4); + + for(unsigned i = 0; i < 32; i++) { + if(slot[i].capacity() == 0) { + fp.write(false); + } else { + fp.write(true); + fp.write(slot[i].data(), slot[i].capacity()); + } + } + } + } +} + +void StateManager::slotLoad() { + if(auto position = stateList.selection()) { + serializer s(slot[position()].data(), slot[position()].capacity()); + SNES::system.unserialize(s); + } +} + +void StateManager::slotSave() { + if(auto position = stateList.selection()) { + SNES::system.runtosave(); + slot[position()] = SNES::system.serialize(); + } + refresh(); + synchronize(); + descEdit.setFocused(); +} + +void StateManager::slotErase() { + if(auto position = stateList.selection()) { + slot[position()] = serializer(); + } + refresh(); + synchronize(); +} + +string StateManager::slotLoadDescription(unsigned i) { + if(slot[i].capacity() == 0) return "(empty)"; + char text[512]; + strlcpy(text, (const char*)slot[i].data() + HeaderLength, 512); + return text; +} + +void StateManager::slotSaveDescription() { + if(auto position = stateList.selection()) { + string text = descEdit.text(); + if(slot[position()].capacity() > 0) { + strlcpy((char*)slot[position()].data() + HeaderLength, (const char*)text, 512); + } + } + refresh(); +} diff --git a/bsnes/ui-phoenix/tools/state-manager.hpp b/bsnes/ui-phoenix/tools/state-manager.hpp new file mode 100755 index 00000000..84103172 --- /dev/null +++ b/bsnes/ui-phoenix/tools/state-manager.hpp @@ -0,0 +1,28 @@ +struct StateManager : Window { + ListBox stateList; + Label descLabel; + TextBox descEdit; + Button loadButton; + Button saveButton; + Button eraseButton; + + void create(); + void synchronize(); + void refresh(); + void load(); + void save(); + void slotLoad(); + void slotSave(); + void slotErase(); + string slotLoadDescription(unsigned slot); + void slotSaveDescription(); + +private: + enum : unsigned { + HeaderLength = 28, + DescriptionLength = 512, + }; + serializer slot[32]; +}; + +extern StateManager stateManager; diff --git a/bsnes/ui-phoenix/tools/tools.cpp b/bsnes/ui-phoenix/tools/tools.cpp index 5e37026e..5ec3021d 100755 --- a/bsnes/ui-phoenix/tools/tools.cpp +++ b/bsnes/ui-phoenix/tools/tools.cpp @@ -1,2 +1,3 @@ #include "../base.hpp" #include "cheat-editor.cpp" +#include "state-manager.cpp" diff --git a/bsnes/ui-phoenix/tools/tools.hpp b/bsnes/ui-phoenix/tools/tools.hpp index 97db5ccf..e7834f88 100755 --- a/bsnes/ui-phoenix/tools/tools.hpp +++ b/bsnes/ui-phoenix/tools/tools.hpp @@ -1 +1,2 @@ #include "cheat-editor.hpp" +#include "state-manager.hpp" diff --git a/bsnes/ui-phoenix/utility/utility.cpp b/bsnes/ui-phoenix/utility/utility.cpp index a54c56e1..788a88de 100755 --- a/bsnes/ui-phoenix/utility/utility.cpp +++ b/bsnes/ui-phoenix/utility/utility.cpp @@ -60,6 +60,7 @@ void Utility::setShader() { void Utility::cartridgeLoaded() { SNES::system.power(); cheatEditor.load(cartridge.baseName); + stateManager.load(); mainWindow.synchronize(); utility.setTitle(notdir(cartridge.baseName)); utility.showMessage(string("Loaded ", notdir(cartridge.baseName))); @@ -68,6 +69,7 @@ void Utility::cartridgeLoaded() { void Utility::cartridgeUnloaded() { SNES::cartridge.unload(); cheatEditor.save(cartridge.baseName); + stateManager.save(); mainWindow.synchronize(); }