mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-07-31 17:30:13 +02:00
Update to v106r37 release.
byuu says: Changelog: - bsnes: cheat code “enabled” option changed to “enable” - bsnes: connected “Cancel” action on add/edit cheat code window - hiro: improved BrowserDialog::selectFolder() behavior - can choose “Select” inside of a target folder when no items are selected - bsnes: implemented state manager - bsnes: save a recovery state before loading a state, quitting, or changing drivers - bsnes: input settings, hotkey settings, cheat editor, state manager entries are now batchable - this allows bulk clearing/deleting of entries - bsnes: cheat code list now auto-sorts alphabetically instead of using up/down move arrows I know most people will probably prefer to order cheat codes the way they want, but the issue is that the state manager can't really work this way. Each state is a file on disk. So yes, we could store a states-manifest.bml to track the order of the states, or try to insert numbers into the filenames and do bulk filesystem rename operations on sorting, but then we would run into oddities when users delete state files manually. And really, manual sorting is just clumsy. If you really want a specific ordering, you can prefix cheats/states with numeric indices instead.
This commit is contained in:
@@ -13,7 +13,7 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.36";
|
static const string Version = "106.37";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
@@ -10,20 +10,20 @@ auto InputManager::bindHotkeys() -> void {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
||||||
program->saveState(stateSlot);
|
program->saveState({"Quick/Slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Load State").onPress([&] {
|
hotkeys.append(InputHotkey("Load State").onPress([&] {
|
||||||
program->loadState(stateSlot);
|
program->loadState({"Quick/Slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Increment State Slot").onPress([&] {
|
hotkeys.append(InputHotkey("Increment State Slot").onPress([&] {
|
||||||
if(--stateSlot < 1) stateSlot = 5;
|
if(--stateSlot < 1) stateSlot = 9;
|
||||||
program->showMessage({"Selected state slot ", stateSlot});
|
program->showMessage({"Selected state slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Decrement State Slot").onPress([&] {
|
hotkeys.append(InputHotkey("Decrement State Slot").onPress([&] {
|
||||||
if(++stateSlot > 5) stateSlot = 1;
|
if(++stateSlot > 9) stateSlot = 1;
|
||||||
program->showMessage({"Selected state slot ", stateSlot});
|
program->showMessage({"Selected state slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@@ -106,21 +106,31 @@ Presentation::Presentation() {
|
|||||||
|
|
||||||
toolsMenu.setText("Tools").setVisible(false);
|
toolsMenu.setText("Tools").setVisible(false);
|
||||||
saveState.setText("Save State");
|
saveState.setText("Save State");
|
||||||
saveState1.setText("Slot 1").onActivate([&] { program->saveState(1); });
|
saveState1.setText("Slot 1").onActivate([&] { program->saveState("Quick/Slot 1"); });
|
||||||
saveState2.setText("Slot 2").onActivate([&] { program->saveState(2); });
|
saveState2.setText("Slot 2").onActivate([&] { program->saveState("Quick/Slot 2"); });
|
||||||
saveState3.setText("Slot 3").onActivate([&] { program->saveState(3); });
|
saveState3.setText("Slot 3").onActivate([&] { program->saveState("Quick/Slot 3"); });
|
||||||
saveState4.setText("Slot 4").onActivate([&] { program->saveState(4); });
|
saveState4.setText("Slot 4").onActivate([&] { program->saveState("Quick/Slot 4"); });
|
||||||
saveState5.setText("Slot 5").onActivate([&] { program->saveState(5); });
|
saveState5.setText("Slot 5").onActivate([&] { program->saveState("Quick/Slot 5"); });
|
||||||
|
saveState6.setText("Slot 6").onActivate([&] { program->saveState("Quick/Slot 6"); });
|
||||||
|
saveState7.setText("Slot 7").onActivate([&] { program->saveState("Quick/Slot 7"); });
|
||||||
|
saveState8.setText("Slot 8").onActivate([&] { program->saveState("Quick/Slot 8"); });
|
||||||
|
saveState9.setText("Slot 9").onActivate([&] { program->saveState("Quick/Slot 9"); });
|
||||||
loadState.setText("Load State");
|
loadState.setText("Load State");
|
||||||
loadState1.setText("Slot 1").onActivate([&] { program->loadState(1); });
|
loadState1.setText("Slot 1").onActivate([&] { program->loadState("Quick/Slot 1"); });
|
||||||
loadState2.setText("Slot 2").onActivate([&] { program->loadState(2); });
|
loadState2.setText("Slot 2").onActivate([&] { program->loadState("Quick/Slot 2"); });
|
||||||
loadState3.setText("Slot 3").onActivate([&] { program->loadState(3); });
|
loadState3.setText("Slot 3").onActivate([&] { program->loadState("Quick/Slot 3"); });
|
||||||
loadState4.setText("Slot 4").onActivate([&] { program->loadState(4); });
|
loadState4.setText("Slot 4").onActivate([&] { program->loadState("Quick/Slot 4"); });
|
||||||
loadState5.setText("Slot 5").onActivate([&] { program->loadState(5); });
|
loadState5.setText("Slot 5").onActivate([&] { program->loadState("Quick/Slot 5"); });
|
||||||
|
loadState6.setText("Slot 6").onActivate([&] { program->loadState("Quick/Slot 6"); });
|
||||||
|
loadState7.setText("Slot 7").onActivate([&] { program->loadState("Quick/Slot 7"); });
|
||||||
|
loadState8.setText("Slot 8").onActivate([&] { program->loadState("Quick/Slot 8"); });
|
||||||
|
loadState9.setText("Slot 9").onActivate([&] { program->loadState("Quick/Slot 9"); });
|
||||||
|
loadState0.setText("Recovery Slot").onActivate([&] { program->loadState("Quick/Recovery Slot"); });
|
||||||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||||
if(pauseEmulation.checked()) audio->clear();
|
if(pauseEmulation.checked()) audio->clear();
|
||||||
});
|
});
|
||||||
cheatEditor.setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
cheatEditor.setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
||||||
|
stateManager.setText("State Manager ...").onActivate([&] { toolsWindow->show(1); });
|
||||||
|
|
||||||
helpMenu.setText("Help");
|
helpMenu.setText("Help");
|
||||||
about.setText("About ...").onActivate([&] {
|
about.setText("About ...").onActivate([&] {
|
||||||
|
@@ -58,15 +58,26 @@ struct Presentation : Window {
|
|||||||
MenuItem saveState3{&saveState};
|
MenuItem saveState3{&saveState};
|
||||||
MenuItem saveState4{&saveState};
|
MenuItem saveState4{&saveState};
|
||||||
MenuItem saveState5{&saveState};
|
MenuItem saveState5{&saveState};
|
||||||
|
MenuItem saveState6{&saveState};
|
||||||
|
MenuItem saveState7{&saveState};
|
||||||
|
MenuItem saveState8{&saveState};
|
||||||
|
MenuItem saveState9{&saveState};
|
||||||
Menu loadState{&toolsMenu};
|
Menu loadState{&toolsMenu};
|
||||||
MenuItem loadState1{&loadState};
|
MenuItem loadState1{&loadState};
|
||||||
MenuItem loadState2{&loadState};
|
MenuItem loadState2{&loadState};
|
||||||
MenuItem loadState3{&loadState};
|
MenuItem loadState3{&loadState};
|
||||||
MenuItem loadState4{&loadState};
|
MenuItem loadState4{&loadState};
|
||||||
MenuItem loadState5{&loadState};
|
MenuItem loadState5{&loadState};
|
||||||
|
MenuItem loadState6{&loadState};
|
||||||
|
MenuItem loadState7{&loadState};
|
||||||
|
MenuItem loadState8{&loadState};
|
||||||
|
MenuItem loadState9{&loadState};
|
||||||
|
MenuSeparator loadStateSeparator{&loadState};
|
||||||
|
MenuItem loadState0{&loadState};
|
||||||
MenuCheckItem pauseEmulation{&toolsMenu};
|
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||||
MenuSeparator toolsSeparator{&toolsMenu};
|
MenuSeparator toolsSeparator{&toolsMenu};
|
||||||
MenuItem cheatEditor{&toolsMenu};
|
MenuItem cheatEditor{&toolsMenu};
|
||||||
|
MenuItem stateManager{&toolsMenu};
|
||||||
Menu helpMenu{&menuBar};
|
Menu helpMenu{&menuBar};
|
||||||
MenuItem about{&helpMenu};
|
MenuItem about{&helpMenu};
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ auto Program::load() -> void {
|
|||||||
presentation->pauseEmulation.setChecked(false);
|
presentation->pauseEmulation.setChecked(false);
|
||||||
presentation->resizeViewport();
|
presentation->resizeViewport();
|
||||||
toolsWindow->cheatEditor.loadCheats();
|
toolsWindow->cheatEditor.loadCheats();
|
||||||
|
toolsWindow->stateManager.loadStates();
|
||||||
|
|
||||||
string locations = superNintendo.location;
|
string locations = superNintendo.location;
|
||||||
if(auto location = gameBoy.location) locations.append("|", location);
|
if(auto location = gameBoy.location) locations.append("|", location);
|
||||||
@@ -96,6 +97,7 @@ auto Program::unload() -> void {
|
|||||||
if(!emulator->loaded()) return;
|
if(!emulator->loaded()) return;
|
||||||
toolsWindow->cheatEditor.saveCheats();
|
toolsWindow->cheatEditor.saveCheats();
|
||||||
toolsWindow->setVisible(false);
|
toolsWindow->setVisible(false);
|
||||||
|
saveRecoveryState();
|
||||||
emulator->unload();
|
emulator->unload();
|
||||||
superNintendo = {};
|
superNintendo = {};
|
||||||
gameBoy = {};
|
gameBoy = {};
|
||||||
|
@@ -2,7 +2,7 @@ auto Program::path(string type, string location, string extension) -> string {
|
|||||||
auto pathname = Location::path(location);
|
auto pathname = Location::path(location);
|
||||||
auto filename = Location::file(location);
|
auto filename = Location::file(location);
|
||||||
auto prefix = Location::prefix(filename);
|
auto prefix = Location::prefix(filename);
|
||||||
auto suffix = Location::suffix(filename);
|
auto suffix = extension;
|
||||||
|
|
||||||
if(type == "Games") {
|
if(type == "Games") {
|
||||||
if(auto path = settings["Path/Games"].text()) {
|
if(auto path = settings["Path/Games"].text()) {
|
||||||
@@ -34,9 +34,5 @@ auto Program::path(string type, string location, string extension) -> string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(extension) {
|
|
||||||
suffix = extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {pathname, prefix, suffix};
|
return {pathname, prefix, suffix};
|
||||||
}
|
}
|
||||||
|
@@ -58,6 +58,7 @@ Program::Program(string_vector arguments) {
|
|||||||
new SettingsWindow;
|
new SettingsWindow;
|
||||||
new CheatDatabase;
|
new CheatDatabase;
|
||||||
new CheatWindow;
|
new CheatWindow;
|
||||||
|
new StateWindow;
|
||||||
new ToolsWindow;
|
new ToolsWindow;
|
||||||
new AboutWindow;
|
new AboutWindow;
|
||||||
|
|
||||||
|
@@ -24,8 +24,10 @@ struct Program : Emulator::Platform {
|
|||||||
auto path(string type, string location, string extension = "") -> string;
|
auto path(string type, string location, string extension = "") -> string;
|
||||||
|
|
||||||
//state.cpp
|
//state.cpp
|
||||||
auto loadState(uint slot) -> bool;
|
auto statePath() -> string;
|
||||||
auto saveState(uint slot) -> bool;
|
auto loadState(string filename) -> bool;
|
||||||
|
auto saveState(string filename) -> bool;
|
||||||
|
auto saveRecoveryState() -> bool;
|
||||||
|
|
||||||
//utility.cpp
|
//utility.cpp
|
||||||
auto initializeVideoDriver() -> void;
|
auto initializeVideoDriver() -> void;
|
||||||
|
@@ -1,18 +1,36 @@
|
|||||||
auto Program::loadState(uint slot) -> bool {
|
auto Program::statePath() -> string {
|
||||||
if(!emulator->loaded()) return false;
|
if(!emulator->loaded()) return "";
|
||||||
auto location = path("States", superNintendo.location, {".bs", slot});
|
return path("States", superNintendo.location, ".bst/");
|
||||||
if(!file::exists(location)) return showMessage({"Slot ", slot, " state does not exist"}), false;
|
|
||||||
auto memory = file::read(location);
|
|
||||||
serializer s{memory.data(), memory.size()};
|
|
||||||
if(!emulator->unserialize(s)) return showMessage({"Slot ", slot, " state is incompatible"}), false;
|
|
||||||
return showMessage({"Loaded state from slot ", slot}), true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::saveState(uint slot) -> bool {
|
auto Program::loadState(string filename) -> bool {
|
||||||
if(!emulator->loaded()) return false;
|
if(!emulator->loaded()) return false;
|
||||||
auto location = path("States", superNintendo.location, {".bs", slot});
|
string location = {statePath(), filename, ".bst"};
|
||||||
serializer s = emulator->serialize();
|
string prefix = Location::prefix(location);
|
||||||
if(!s.size()) return showMessage({"Failed to save state to slot ", slot}), false;
|
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
||||||
if(!file::write(location, s.data(), s.size())) return showMessage({"Unable to write state to slot ", slot}), false;
|
if(filename != "Quick/Recovery Slot") saveRecoveryState();
|
||||||
return showMessage({"Saved state to slot ", slot}), true;
|
auto memory = file::read(location);
|
||||||
|
serializer s{memory.data(), memory.size()};
|
||||||
|
if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false;
|
||||||
|
return showMessage({"Loaded [", prefix, "]"}), true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Program::saveState(string filename) -> bool {
|
||||||
|
if(!emulator->loaded()) return false;
|
||||||
|
directory::create({statePath(), "Quick/"});
|
||||||
|
string location = {statePath(), filename, ".bst"};
|
||||||
|
string prefix = Location::prefix(location);
|
||||||
|
serializer s = emulator->serialize();
|
||||||
|
if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false;
|
||||||
|
if(!file::write(location, s.data(), s.size())) return showMessage({"Unable to write [", prefix, "] to disk"}), false;
|
||||||
|
return showMessage({"Saved [", prefix, "]"}), true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Program::saveRecoveryState() -> bool {
|
||||||
|
if(!emulator->loaded()) return false;
|
||||||
|
directory::create({statePath(), "Quick/"});
|
||||||
|
serializer s = emulator->serialize();
|
||||||
|
if(!s.size()) return false;
|
||||||
|
if(!file::write({statePath(), "Quick/Recovery Slot.bst"}, s.data(), s.size())) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
"It is highly recommended you unload your game first to avoid data loss.\n"
|
"It is highly recommended you unload your game first to avoid data loss.\n"
|
||||||
"Do you wish to proceed with the video driver change now anyway?"
|
"Do you wish to proceed with the video driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->initializeVideoDriver();
|
program->initializeVideoDriver();
|
||||||
@@ -61,6 +62,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
"It is highly recommended you unload your game first to avoid data loss.\n"
|
"It is highly recommended you unload your game first to avoid data loss.\n"
|
||||||
"Do you wish to proceed with the audio driver change now anyway?"
|
"Do you wish to proceed with the audio driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->initializeAudioDriver();
|
program->initializeAudioDriver();
|
||||||
@@ -99,6 +101,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
"It is highly recommended you unload your game first to avoid data loss.\n"
|
"It is highly recommended you unload your game first to avoid data loss.\n"
|
||||||
"Do you wish to proceed with the input driver change now anyway?"
|
"Do you wish to proceed with the input driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
|
program->saveRecoveryState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->initializeInputDriver();
|
program->initializeInputDriver();
|
||||||
|
@@ -3,19 +3,23 @@ HotkeySettings::HotkeySettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
setText("Hotkeys");
|
setText("Hotkeys");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
mappingList.onActivate([&] { assignMapping(); });
|
mappingList.setBatchable();
|
||||||
mappingList.onChange([&] { eraseButton.setEnabled((bool)mappingList.selected()); });
|
mappingList.onActivate([&] {
|
||||||
resetButton.setText("Reset").onActivate([&] {
|
if(assignButton.enabled()) assignButton.doActivate();
|
||||||
if(MessageDialog("Are you sure you want to erase all hotkey mappings?").setParent(*settingsWindow).question() == "Yes") {
|
|
||||||
for(auto& mapping : inputManager->hotkeys) mapping.unbind();
|
|
||||||
refreshMappings();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
eraseButton.setText("Erase").onActivate([&] {
|
mappingList.onChange([&] {
|
||||||
if(auto item = mappingList.selected()) {
|
auto batched = mappingList.batched();
|
||||||
|
assignButton.setEnabled(batched.size() == 1);
|
||||||
|
clearButton.setEnabled(batched.size() >= 1);
|
||||||
|
});
|
||||||
|
assignButton.setText("Assign").onActivate([&] {
|
||||||
|
assignMapping();
|
||||||
|
});
|
||||||
|
clearButton.setText("Clear").onActivate([&] {
|
||||||
|
for(auto item : mappingList.batched()) {
|
||||||
inputManager->hotkeys[item.offset()].unbind();
|
inputManager->hotkeys[item.offset()].unbind();
|
||||||
refreshMappings();
|
|
||||||
}
|
}
|
||||||
|
refreshMappings();
|
||||||
});
|
});
|
||||||
|
|
||||||
reloadMappings();
|
reloadMappings();
|
||||||
@@ -67,6 +71,7 @@ auto HotkeySettings::inputEvent(shared_pointer<HID::Device> device, uint group,
|
|||||||
timer.setEnabled(false);
|
timer.setEnabled(false);
|
||||||
settingsWindow->statusBar.setText();
|
settingsWindow->statusBar.setText();
|
||||||
settingsWindow->layout.setEnabled();
|
settingsWindow->layout.setEnabled();
|
||||||
|
settingsWindow->doSize();
|
||||||
}).setInterval(200).setEnabled();
|
}).setInterval(200).setEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,36 +20,35 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
portList.onChange([&] { reloadDevices(); });
|
portList.onChange([&] { reloadDevices(); });
|
||||||
deviceLabel.setText("Device:");
|
deviceLabel.setText("Device:");
|
||||||
deviceList.onChange([&] { reloadMappings(); });
|
deviceList.onChange([&] { reloadMappings(); });
|
||||||
mappingList.onActivate([&] { assignMapping(); });
|
mappingList.setBatchable();
|
||||||
|
mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); });
|
||||||
mappingList.onChange([&] { updateControls(); });
|
mappingList.onChange([&] { updateControls(); });
|
||||||
assignMouse1.onActivate([&] { assignMouseInput(0); });
|
assignMouse1.onActivate([&] { assignMouseInput(0); });
|
||||||
assignMouse2.onActivate([&] { assignMouseInput(1); });
|
assignMouse2.onActivate([&] { assignMouseInput(1); });
|
||||||
assignMouse3.onActivate([&] { assignMouseInput(2); });
|
assignMouse3.onActivate([&] { assignMouseInput(2); });
|
||||||
resetButton.setText("Reset").onActivate([&] {
|
assignButton.setText("Assign").onActivate([&] {
|
||||||
if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsWindow).question() == "Yes") {
|
assignMapping();
|
||||||
for(auto& mapping : activeDevice().mappings) mapping.unbind();
|
|
||||||
refreshMappings();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
eraseButton.setText("Erase").onActivate([&] {
|
clearButton.setText("Clear").onActivate([&] {
|
||||||
if(auto mapping = mappingList.selected()) {
|
for(auto mapping : mappingList.batched()) {
|
||||||
activeDevice().mappings[mapping.offset()].unbind();
|
activeDevice().mappings[mapping.offset()].unbind();
|
||||||
refreshMappings();
|
|
||||||
}
|
}
|
||||||
|
refreshMappings();
|
||||||
});
|
});
|
||||||
|
|
||||||
reloadPorts();
|
reloadPorts();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InputSettings::updateControls() -> void {
|
auto InputSettings::updateControls() -> void {
|
||||||
eraseButton.setEnabled((bool)mappingList.selected());
|
auto batched = mappingList.batched();
|
||||||
|
assignButton.setEnabled(batched.size() == 1);
|
||||||
|
clearButton.setEnabled(batched.size() >= 1);
|
||||||
assignMouse1.setVisible(false);
|
assignMouse1.setVisible(false);
|
||||||
assignMouse2.setVisible(false);
|
assignMouse2.setVisible(false);
|
||||||
assignMouse3.setVisible(false);
|
assignMouse3.setVisible(false);
|
||||||
|
|
||||||
if(auto mapping = mappingList.selected()) {
|
if(batched.size() == 1) {
|
||||||
auto& input = activeDevice().mappings[mapping.offset()];
|
auto& input = activeDevice().mappings[batched.left().offset()];
|
||||||
|
|
||||||
if(input.isDigital()) {
|
if(input.isDigital()) {
|
||||||
assignMouse1.setVisible().setText("Mouse Left");
|
assignMouse1.setVisible().setText("Mouse Left");
|
||||||
assignMouse2.setVisible().setText("Mouse Middle");
|
assignMouse2.setVisible().setText("Mouse Middle");
|
||||||
@@ -118,7 +117,7 @@ auto InputSettings::refreshMappings() -> void {
|
|||||||
auto InputSettings::assignMapping() -> void {
|
auto InputSettings::assignMapping() -> void {
|
||||||
inputManager->poll(); //clear any pending events first
|
inputManager->poll(); //clear any pending events first
|
||||||
|
|
||||||
if(auto mapping = mappingList.selected()) {
|
for(auto mapping : mappingList.batched()) {
|
||||||
activeMapping = activeDevice().mappings[mapping.offset()];
|
activeMapping = activeDevice().mappings[mapping.offset()];
|
||||||
settingsWindow->layout.setEnabled(false);
|
settingsWindow->layout.setEnabled(false);
|
||||||
settingsWindow->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
|
settingsWindow->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
|
||||||
@@ -150,6 +149,7 @@ auto InputSettings::inputEvent(shared_pointer<HID::Device> device, uint group, u
|
|||||||
timer.setEnabled(false);
|
timer.setEnabled(false);
|
||||||
settingsWindow->statusBar.setText();
|
settingsWindow->statusBar.setText();
|
||||||
settingsWindow->layout.setEnabled();
|
settingsWindow->layout.setEnabled();
|
||||||
|
settingsWindow->doSize();
|
||||||
}).setInterval(200).setEnabled();
|
}).setInterval(200).setEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,8 +38,8 @@ public:
|
|||||||
Button assignMouse2{&controlLayout, Size{100, 0}};
|
Button assignMouse2{&controlLayout, Size{100, 0}};
|
||||||
Button assignMouse3{&controlLayout, Size{100, 0}};
|
Button assignMouse3{&controlLayout, Size{100, 0}};
|
||||||
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
||||||
Button resetButton{&controlLayout, Size{80, 0}};
|
Button assignButton{&controlLayout, Size{80, 0}};
|
||||||
Button eraseButton{&controlLayout, Size{80, 0}};
|
Button clearButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HotkeySettings : TabFrameItem {
|
struct HotkeySettings : TabFrameItem {
|
||||||
@@ -57,8 +57,8 @@ public:
|
|||||||
TableView mappingList{&layout, Size{~0, ~0}};
|
TableView mappingList{&layout, Size{~0, ~0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
Widget controlSpacer{&controlLayout, Size{~0, 0}};
|
||||||
Button resetButton{&controlLayout, Size{80, 0}};
|
Button assignButton{&controlLayout, Size{80, 0}};
|
||||||
Button eraseButton{&controlLayout, Size{80, 0}};
|
Button clearButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathSettings : TabFrameItem {
|
struct PathSettings : TabFrameItem {
|
||||||
|
@@ -44,11 +44,10 @@ auto CheatDatabase::findCheats() -> void {
|
|||||||
auto CheatDatabase::addCheats() -> void {
|
auto CheatDatabase::addCheats() -> void {
|
||||||
for(auto item : cheatList.items()) {
|
for(auto item : cheatList.items()) {
|
||||||
if(item.checked()) {
|
if(item.checked()) {
|
||||||
toolsWindow->cheatEditor.addCheat(item.text(), item.property("code"));
|
toolsWindow->cheatEditor.addCheat({item.text(), item.property("code"), false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
toolsWindow->cheatEditor.synchronizeCodes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -58,29 +57,30 @@ CheatWindow::CheatWindow() {
|
|||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
nameLabel.setText("Name:");
|
nameLabel.setText("Name:");
|
||||||
|
nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
|
||||||
nameValue.onChange([&] { doChange(); });
|
nameValue.onChange([&] { doChange(); });
|
||||||
codeLabel.setText("Code:");
|
codeLabel.setText("Code:");
|
||||||
|
codeValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
|
||||||
codeValue.onChange([&] { doChange(); });
|
codeValue.onChange([&] { doChange(); });
|
||||||
enabledOption.setText("Enabled");
|
enableOption.setText("Enable");
|
||||||
acceptButton.onActivate([&] { doAccept(); });
|
acceptButton.onActivate([&] { doAccept(); });
|
||||||
cancelButton.setText("Cancel");
|
cancelButton.setText("Cancel").onActivate([&] { setVisible(false); });
|
||||||
|
|
||||||
setSize({480, layout.minimumSize().height()});
|
setSize({400, layout.minimumSize().height()});
|
||||||
setDismissable();
|
setDismissable();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CheatWindow::show(TableViewItem item) -> void {
|
auto CheatWindow::show(Cheat cheat) -> void {
|
||||||
this->item = item;
|
nameValue.setText(cheat.name);
|
||||||
nameValue.setText(item.cell(1).text());
|
codeValue.setText(cheat.code);
|
||||||
codeValue.setText(item.property("code"));
|
enableOption.setChecked(cheat.enable);
|
||||||
enabledOption.setChecked(item.cell(0).checked());
|
|
||||||
doChange();
|
doChange();
|
||||||
setTitle(!item ? "Add Cheat Code" : "Edit Cheat Code");
|
setTitle(!cheat.name ? "Add Cheat" : "Edit Cheat");
|
||||||
setCentered(*toolsWindow);
|
setCentered(*toolsWindow);
|
||||||
setVisible();
|
setVisible();
|
||||||
setFocused();
|
setFocused();
|
||||||
nameValue.setFocused();
|
nameValue.setFocused();
|
||||||
acceptButton.setText(!item ? "Add" : "Edit");
|
acceptButton.setText(!cheat.name ? "Add" : "Edit");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CheatWindow::doChange() -> void {
|
auto CheatWindow::doChange() -> void {
|
||||||
@@ -91,12 +91,11 @@ auto CheatWindow::doChange() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto CheatWindow::doAccept() -> void {
|
auto CheatWindow::doAccept() -> void {
|
||||||
if(item) {
|
Cheat cheat = {nameValue.text().strip(), codeValue.text().strip(), enableOption.checked()};
|
||||||
item.cell(0).setChecked(enabledOption.checked());
|
if(acceptButton.text() == "Add") {
|
||||||
item.cell(1).setText(nameValue.text());
|
toolsWindow->cheatEditor.addCheat(cheat);
|
||||||
item.setProperty("code", codeValue.text());
|
|
||||||
} else {
|
} else {
|
||||||
toolsWindow->cheatEditor.addCheat(nameValue.text(), codeValue.text(), enabledOption.checked());
|
toolsWindow->cheatEditor.editCheat(cheat);
|
||||||
}
|
}
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
@@ -108,111 +107,108 @@ CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) {
|
|||||||
setText("Cheat Editor");
|
setText("Cheat Editor");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
cheatList.onActivate([&] { modifyButton.doActivate(); });
|
cheatList.setBatchable();
|
||||||
|
cheatList.onActivate([&] {
|
||||||
|
editButton.doActivate();
|
||||||
|
});
|
||||||
cheatList.onChange([&] {
|
cheatList.onChange([&] {
|
||||||
auto selected = cheatList.selected();
|
auto batched = cheatList.batched();
|
||||||
upButton.setEnabled((bool)selected && selected.offset() != 0);
|
editButton.setEnabled(batched.size() == 1);
|
||||||
downButton.setEnabled((bool)selected && selected.offset() != cheatList.itemCount() - 1);
|
removeButton.setEnabled(batched.size() >= 1);
|
||||||
modifyButton.setEnabled((bool)selected);
|
|
||||||
removeButton.setEnabled((bool)selected);
|
|
||||||
});
|
});
|
||||||
cheatList.onToggle([&](TableViewCell) { synchronizeCodes(); });
|
cheatList.onToggle([&](TableViewCell) {
|
||||||
upButton.setIcon(Icon::Go::Up).onActivate([&] {
|
synchronizeCodes();
|
||||||
if(auto item = cheatList.selected()) {
|
|
||||||
swap(item.offset(), item.offset() - 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
downButton.setIcon(Icon::Go::Down).onActivate([&] {
|
|
||||||
if(auto item = cheatList.selected()) {
|
|
||||||
swap(item.offset(), item.offset() + 1);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
findCheatsButton.setText("Find Cheats ...").onActivate([&] {
|
findCheatsButton.setText("Find Cheats ...").onActivate([&] {
|
||||||
cheatDatabase->findCheats();
|
cheatDatabase->findCheats();
|
||||||
});
|
});
|
||||||
resetButton.setText("Reset").onActivate([&] {
|
addButton.setText("Add").onActivate([&] {
|
||||||
if(MessageDialog("Are you sure you want to permanently erase all cheat codes?").setParent(*toolsWindow).question() == "Yes") {
|
|
||||||
cheatList.reset().append(TableViewHeader()
|
|
||||||
.append(TableViewColumn())
|
|
||||||
.append(TableViewColumn().setExpandable())
|
|
||||||
.setVisible(false)
|
|
||||||
);
|
|
||||||
synchronizeCodes();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
appendButton.setText("Add").onActivate([&] {
|
|
||||||
cheatWindow->show();
|
cheatWindow->show();
|
||||||
});
|
});
|
||||||
modifyButton.setText("Edit").onActivate([&] {
|
editButton.setText("Edit").onActivate([&] {
|
||||||
if(auto item = cheatList.selected()) {
|
if(auto item = cheatList.selected()) {
|
||||||
cheatWindow->show(item);
|
cheatWindow->show(cheats[item.offset()]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
removeButton.setText("Remove").onActivate([&] {
|
removeButton.setText("Remove").onActivate([&] {
|
||||||
if(auto item = cheatList.selected()) {
|
removeCheats();
|
||||||
cheatList.remove(item).doChange();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//do not display "Find Cheats" button if there is no cheat database available
|
//do not display "Find Cheats" button if there is no cheat database available
|
||||||
if(!file::exists(locate("cheats.bml"))) findCheatsButton.setVisible(false);
|
if(!file::exists(locate("cheats.bml"))) findCheatsButton.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CheatEditor::swap(uint x, uint y) -> void {
|
auto CheatEditor::refresh() -> void {
|
||||||
auto itemX = cheatList.item(x);
|
cheatList.reset();
|
||||||
auto itemY = cheatList.item(y);
|
cheatList.append(TableViewHeader().setVisible(false)
|
||||||
auto enabled = itemX.cell(0).checked();
|
|
||||||
auto name = itemX.cell(1).text();
|
|
||||||
auto code = itemX.property("code");
|
|
||||||
itemX.cell(0).setChecked(itemY.cell(0).checked());
|
|
||||||
itemX.cell(1).setText(itemY.cell(1).text());
|
|
||||||
itemX.setProperty("code", itemY.property("code"));
|
|
||||||
itemY.cell(0).setChecked(enabled);
|
|
||||||
itemY.cell(1).setText(name);
|
|
||||||
itemY.setProperty("code", code);
|
|
||||||
itemY.setSelected();
|
|
||||||
cheatList.doChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CheatEditor::synchronizeCodes() -> void {
|
|
||||||
string_vector codes;
|
|
||||||
for(auto item : cheatList.items()) {
|
|
||||||
if(item.cell(0).checked()) codes.append(item.property("code"));
|
|
||||||
}
|
|
||||||
emulator->cheatSet(codes);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CheatEditor::addCheat(string name, string code, bool enabled) -> void {
|
|
||||||
cheatList.append(TableViewItem()
|
|
||||||
.append(TableViewCell().setChecked(enabled))
|
|
||||||
.append(TableViewCell().setText(name))
|
|
||||||
.setProperty("code", code)
|
|
||||||
).resizeColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CheatEditor::loadCheats() -> void {
|
|
||||||
cheatList.reset().append(TableViewHeader()
|
|
||||||
.append(TableViewColumn())
|
.append(TableViewColumn())
|
||||||
.append(TableViewColumn().setExpandable())
|
.append(TableViewColumn().setExpandable())
|
||||||
.setVisible(false)
|
|
||||||
);
|
);
|
||||||
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
for(auto& cheat : cheats) {
|
||||||
auto document = BML::unserialize(string::read(location));
|
cheatList.append(TableViewItem()
|
||||||
for(auto cheat : document.find("cheat")) {
|
.append(TableViewCell().setCheckable().setChecked(cheat.enable))
|
||||||
addCheat(cheat["name"].text(), cheat["code"].text(), (bool)cheat["enabled"]);
|
.append(TableViewCell().setText(cheat.name))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cheatList.resizeColumns().doChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CheatEditor::addCheat(Cheat cheat) -> void {
|
||||||
|
cheats.append(cheat);
|
||||||
|
cheats.sort();
|
||||||
|
refresh();
|
||||||
|
for(uint index : range(cheats)) {
|
||||||
|
if(cheats[index] == cheat) { cheatList.item(index).setSelected(); break; }
|
||||||
}
|
}
|
||||||
cheatList.doChange();
|
cheatList.doChange();
|
||||||
synchronizeCodes();
|
synchronizeCodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CheatEditor::editCheat(Cheat cheat) -> void {
|
||||||
|
if(auto item = cheatList.selected()) {
|
||||||
|
cheats[item.offset()] = cheat;
|
||||||
|
cheats.sort();
|
||||||
|
refresh();
|
||||||
|
for(uint index : range(cheats)) {
|
||||||
|
if(cheats[index] == cheat) { cheatList.item(index).setSelected(); break; }
|
||||||
|
}
|
||||||
|
cheatList.doChange();
|
||||||
|
synchronizeCodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CheatEditor::removeCheats() -> void {
|
||||||
|
if(auto batched = cheatList.batched()) {
|
||||||
|
if(MessageDialog("Are you sure you want to permanently remove the selected cheat(s)?")
|
||||||
|
.setParent(*toolsWindow).question() == "Yes") {
|
||||||
|
for(uint index : rrange(batched)) cheats.remove(batched[index].offset());
|
||||||
|
cheats.sort();
|
||||||
|
refresh();
|
||||||
|
synchronizeCodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CheatEditor::loadCheats() -> void {
|
||||||
|
cheats.reset();
|
||||||
|
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
||||||
|
auto document = BML::unserialize(string::read(location));
|
||||||
|
for(auto cheat : document.find("cheat")) {
|
||||||
|
cheats.append({cheat["name"].text(), cheat["code"].text(), (bool)cheat["enable"]});
|
||||||
|
}
|
||||||
|
cheats.sort();
|
||||||
|
refresh();
|
||||||
|
synchronizeCodes();
|
||||||
|
}
|
||||||
|
|
||||||
auto CheatEditor::saveCheats() -> void {
|
auto CheatEditor::saveCheats() -> void {
|
||||||
string document;
|
string document;
|
||||||
for(auto item : cheatList.items()) {
|
for(auto cheat : cheats) {
|
||||||
document.append("cheat\n");
|
document.append("cheat\n");
|
||||||
document.append(" name: ", item.cell(1).text(), "\n");
|
document.append(" name: ", cheat.name, "\n");
|
||||||
document.append(" code: ", item.property("code"), "\n");
|
document.append(" code: ", cheat.code, "\n");
|
||||||
if(item.cell(0).checked())
|
if(cheat.enable)
|
||||||
document.append(" enabled\n");
|
document.append(" enable\n");
|
||||||
document.append("\n");
|
document.append("\n");
|
||||||
}
|
}
|
||||||
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
||||||
@@ -222,3 +218,11 @@ auto CheatEditor::saveCheats() -> void {
|
|||||||
file::remove(location);
|
file::remove(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CheatEditor::synchronizeCodes() -> void {
|
||||||
|
string_vector codes;
|
||||||
|
for(auto& cheat : cheats) {
|
||||||
|
if(cheat.enable) codes.append(cheat.code);
|
||||||
|
}
|
||||||
|
emulator->cheatSet(codes);
|
||||||
|
}
|
||||||
|
147
higan/target-bsnes/tools/state-manager.cpp
Normal file
147
higan/target-bsnes/tools/state-manager.cpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
StateWindow::StateWindow() {
|
||||||
|
stateWindow = this;
|
||||||
|
|
||||||
|
layout.setMargin(5);
|
||||||
|
nameLabel.setText("Name:");
|
||||||
|
nameValue.onActivate([&] {
|
||||||
|
if(acceptButton.enabled()) acceptButton.doActivate();
|
||||||
|
});
|
||||||
|
nameValue.onChange([&] {
|
||||||
|
doChange();
|
||||||
|
});
|
||||||
|
acceptButton.onActivate([&] {
|
||||||
|
doAccept();
|
||||||
|
});
|
||||||
|
cancelButton.setText("Cancel").onActivate([&] {
|
||||||
|
setVisible(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSize({400, layout.minimumSize().height()});
|
||||||
|
setDismissable();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateWindow::show(string name) -> void {
|
||||||
|
nameValue.setText(name).setProperty("input", name);
|
||||||
|
doChange();
|
||||||
|
setTitle(!name ? "Add State" : "Edit State");
|
||||||
|
setCentered(*toolsWindow);
|
||||||
|
setVisible();
|
||||||
|
setFocused();
|
||||||
|
nameValue.setFocused();
|
||||||
|
acceptButton.setText(!name ? "Add" : "Edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateWindow::doChange() -> void {
|
||||||
|
bool valid = true;
|
||||||
|
auto name = nameValue.text().strip();
|
||||||
|
if(!name) valid = false;
|
||||||
|
for(auto c : name) {
|
||||||
|
if(c == '\\'
|
||||||
|
|| c == '\"'
|
||||||
|
|| c == '\t'
|
||||||
|
|| c == '/'
|
||||||
|
|| c == ':'
|
||||||
|
|| c == '*'
|
||||||
|
|| c == '?'
|
||||||
|
|| c == '<'
|
||||||
|
|| c == '>'
|
||||||
|
|| c == '|') valid = false;
|
||||||
|
}
|
||||||
|
if(auto input = nameValue.property("input")) {
|
||||||
|
if(name != input && file::exists({program->statePath(), name, ".bst"})) valid = false;
|
||||||
|
}
|
||||||
|
nameValue.setBackgroundColor(valid ? Color{} : Color{255, 224, 224});
|
||||||
|
acceptButton.setEnabled(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateWindow::doAccept() -> void {
|
||||||
|
if(acceptButton.text() == "Add") {
|
||||||
|
toolsWindow->stateManager.createState(nameValue.text());
|
||||||
|
} else {
|
||||||
|
toolsWindow->stateManager.modifyState(nameValue.text());
|
||||||
|
}
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
|
setIcon(Icon::Application::FileManager);
|
||||||
|
setText("State Manager");
|
||||||
|
|
||||||
|
layout.setMargin(5);
|
||||||
|
stateList.setBatchable();
|
||||||
|
stateList.onActivate([&] {
|
||||||
|
editButton.doActivate();
|
||||||
|
});
|
||||||
|
stateList.onChange([&] {
|
||||||
|
auto batched = stateList.batched();
|
||||||
|
loadButton.setEnabled(batched.size() == 1);
|
||||||
|
editButton.setEnabled(batched.size() == 1);
|
||||||
|
removeButton.setEnabled(batched.size() >= 1);
|
||||||
|
});
|
||||||
|
loadButton.setText("Load").onActivate([&] {
|
||||||
|
if(auto item = stateList.selected()) {
|
||||||
|
program->loadState(item.cell(0).text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
saveButton.setText("Save").onActivate([&] {
|
||||||
|
stateWindow->show();
|
||||||
|
});
|
||||||
|
editButton.setText("Edit").onActivate([&] {
|
||||||
|
if(auto item = stateList.selected()) {
|
||||||
|
stateWindow->show(item.cell(0).text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removeButton.setText("Remove").onActivate([&] {
|
||||||
|
removeStates();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateManager::loadStates() -> void {
|
||||||
|
stateList.reset();
|
||||||
|
stateList.append(TableViewHeader().setVisible(false)
|
||||||
|
.append(TableViewColumn().setExpandable())
|
||||||
|
);
|
||||||
|
for(auto filename : directory::ifiles(program->statePath(), "*.bst")) {
|
||||||
|
stateList.append(TableViewItem()
|
||||||
|
.append(TableViewCell().setText(filename.trimRight(".bst", 1L)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
stateList.resizeColumns().doChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateManager::createState(string name) -> void {
|
||||||
|
program->saveState(name);
|
||||||
|
loadStates();
|
||||||
|
for(auto item : stateList.items()) {
|
||||||
|
if(item.cell(0).text() == name) item.setSelected();
|
||||||
|
}
|
||||||
|
stateList.doChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateManager::modifyState(string name) -> void {
|
||||||
|
if(auto item = stateList.selected()) {
|
||||||
|
string from = {program->statePath(), item.cell(0).text(), ".bst"};
|
||||||
|
string to = {program->statePath(), name, ".bst"};
|
||||||
|
if(from != to) {
|
||||||
|
file::rename(from, to);
|
||||||
|
loadStates();
|
||||||
|
for(auto item : stateList.items()) {
|
||||||
|
if(item.cell(0).text() == name) item.setSelected();
|
||||||
|
}
|
||||||
|
stateList.doChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto StateManager::removeStates() -> void {
|
||||||
|
if(auto batched = stateList.batched()) {
|
||||||
|
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
|
||||||
|
.setParent(*toolsWindow).question() == "Yes") {
|
||||||
|
for(auto item : batched) {
|
||||||
|
string location = {program->statePath(), item.cell(0).text(), ".bst"};
|
||||||
|
file::remove(location);
|
||||||
|
}
|
||||||
|
loadStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,9 @@
|
|||||||
#include "../bsnes.hpp"
|
#include "../bsnes.hpp"
|
||||||
#include "cheat-editor.cpp"
|
#include "cheat-editor.cpp"
|
||||||
|
#include "state-manager.cpp"
|
||||||
unique_pointer<CheatDatabase> cheatDatabase;
|
unique_pointer<CheatDatabase> cheatDatabase;
|
||||||
unique_pointer<CheatWindow> cheatWindow;
|
unique_pointer<CheatWindow> cheatWindow;
|
||||||
|
unique_pointer<StateWindow> stateWindow;
|
||||||
unique_pointer<ToolsWindow> toolsWindow;
|
unique_pointer<ToolsWindow> toolsWindow;
|
||||||
|
|
||||||
ToolsWindow::ToolsWindow() {
|
ToolsWindow::ToolsWindow() {
|
||||||
@@ -16,6 +18,7 @@ ToolsWindow::ToolsWindow() {
|
|||||||
|
|
||||||
onSize([&] {
|
onSize([&] {
|
||||||
cheatEditor.cheatList.resizeColumns();
|
cheatEditor.cheatList.resizeColumns();
|
||||||
|
stateManager.stateList.resizeColumns();
|
||||||
});
|
});
|
||||||
|
|
||||||
onClose([&] {
|
onClose([&] {
|
||||||
@@ -28,6 +31,7 @@ auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& {
|
|||||||
if(!visible) {
|
if(!visible) {
|
||||||
cheatDatabase->setVisible(false);
|
cheatDatabase->setVisible(false);
|
||||||
cheatWindow->setVisible(false);
|
cheatWindow->setVisible(false);
|
||||||
|
stateWindow->setVisible(false);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,17 @@
|
|||||||
|
struct Cheat {
|
||||||
|
auto operator==(const Cheat& compare) const -> bool {
|
||||||
|
return name == compare.name && code == compare.code && enable == compare.enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator<(const Cheat& compare) const -> bool {
|
||||||
|
return string::icompare(name, compare.name) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name;
|
||||||
|
string code;
|
||||||
|
bool enable;
|
||||||
|
};
|
||||||
|
|
||||||
struct CheatDatabase : Window {
|
struct CheatDatabase : Window {
|
||||||
CheatDatabase();
|
CheatDatabase();
|
||||||
auto findCheats() -> void;
|
auto findCheats() -> void;
|
||||||
@@ -15,13 +29,11 @@ public:
|
|||||||
|
|
||||||
struct CheatWindow : Window {
|
struct CheatWindow : Window {
|
||||||
CheatWindow();
|
CheatWindow();
|
||||||
auto show(TableViewItem item = {}) -> void;
|
auto show(Cheat cheat = {}) -> void;
|
||||||
auto doChange() -> void;
|
auto doChange() -> void;
|
||||||
auto doAccept() -> void;
|
auto doAccept() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TableViewItem item;
|
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
|
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
|
||||||
Label nameLabel{&nameLayout, Size{40, 0}};
|
Label nameLabel{&nameLayout, Size{40, 0}};
|
||||||
@@ -31,71 +43,68 @@ public:
|
|||||||
LineEdit codeValue{&codeLayout, Size{~0, 0}};
|
LineEdit codeValue{&codeLayout, Size{~0, 0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Widget spacer{&controlLayout, Size{40, 0}};
|
Widget spacer{&controlLayout, Size{40, 0}};
|
||||||
CheckLabel enabledOption{&controlLayout, Size{~0, 0}};
|
CheckLabel enableOption{&controlLayout, Size{~0, 0}};
|
||||||
Button acceptButton{&controlLayout, Size{80, 0}};
|
Button acceptButton{&controlLayout, Size{80, 0}};
|
||||||
Button cancelButton{&controlLayout, Size{80, 0}};
|
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CheatEditor : TabFrameItem {
|
struct CheatEditor : TabFrameItem {
|
||||||
CheatEditor(TabFrame*);
|
CheatEditor(TabFrame*);
|
||||||
auto swap(uint x, uint y) -> void;
|
auto refresh() -> void;
|
||||||
auto synchronizeCodes() -> void;
|
auto addCheat(Cheat cheat) -> void;
|
||||||
auto addCheat(string name, string code, bool enabled = false) -> void;
|
auto editCheat(Cheat cheat) -> void;
|
||||||
|
auto removeCheats() -> void;
|
||||||
auto loadCheats() -> void;
|
auto loadCheats() -> void;
|
||||||
auto saveCheats() -> void;
|
auto saveCheats() -> void;
|
||||||
|
auto synchronizeCodes() -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
vector<Cheat> cheats;
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TableView cheatList{&layout, Size{~0, ~0}};
|
TableView cheatList{&layout, Size{~0, ~0}};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Button upButton{&controlLayout, Size{0, 0}};
|
|
||||||
Button downButton{&controlLayout, Size{0, 0}};
|
|
||||||
Button findCheatsButton{&controlLayout, Size{120, 0}};
|
Button findCheatsButton{&controlLayout, Size{120, 0}};
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
Button resetButton{&controlLayout, Size{80, 0}};
|
Button addButton{&controlLayout, Size{80, 0}};
|
||||||
Button appendButton{&controlLayout, Size{80, 0}};
|
Button editButton{&controlLayout, Size{80, 0}};
|
||||||
Button modifyButton{&controlLayout, Size{80, 0}};
|
|
||||||
Button removeButton{&controlLayout, Size{80, 0}};
|
Button removeButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
struct StateWindow : Window {
|
||||||
struct CheatEditor : TabFrameItem {
|
StateWindow();
|
||||||
enum : uint { Slots = 128 };
|
auto show(string name = {}) -> void;
|
||||||
|
auto doChange() -> void;
|
||||||
CheatEditor(TabFrame*);
|
auto doAccept() -> void;
|
||||||
auto doChangeSelected() -> void;
|
|
||||||
auto doModify() -> void;
|
|
||||||
auto doRefresh() -> void;
|
|
||||||
auto doReset(bool force = false) -> void;
|
|
||||||
auto doErase() -> void;
|
|
||||||
auto synchronizeCodes() -> void;
|
|
||||||
auto addCode(bool enabled, string code, string description) -> bool;
|
|
||||||
auto loadCheats() -> void;
|
|
||||||
auto saveCheats() -> void;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Cheat {
|
|
||||||
bool enabled = false;
|
|
||||||
string code;
|
|
||||||
string description;
|
|
||||||
};
|
|
||||||
Cheat cheats[Slots];
|
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TableView cheatList{&layout, Size{~0, ~0}};
|
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
|
||||||
HorizontalLayout codeLayout{&layout, Size{~0, 0}};
|
Label nameLabel{&nameLayout, Size{40, 0}};
|
||||||
Label codeLabel{&codeLayout, Size{70, 0}};
|
LineEdit nameValue{&nameLayout, Size{~0, 0}};
|
||||||
LineEdit codeValue{&codeLayout, Size{~0, 0}};
|
|
||||||
HorizontalLayout descriptionLayout{&layout, Size{~0, 0}};
|
|
||||||
Label descriptionLabel{&descriptionLayout, Size{70, 0}};
|
|
||||||
LineEdit descriptionValue{&descriptionLayout, Size{~0, 0}};
|
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Button findCodesButton{&controlLayout, Size{120, 0}};
|
|
||||||
Widget spacer{&controlLayout, Size{~0, 0}};
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
Button resetButton{&controlLayout, Size{80, 0}};
|
Button acceptButton{&controlLayout, Size{80, 0}};
|
||||||
Button eraseButton{&controlLayout, Size{80, 0}};
|
Button cancelButton{&controlLayout, Size{80, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateManager : TabFrameItem {
|
||||||
|
StateManager(TabFrame*);
|
||||||
|
auto loadStates() -> void;
|
||||||
|
auto createState(string name) -> void;
|
||||||
|
auto modifyState(string name) -> void;
|
||||||
|
auto removeStates() -> void;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
TableView stateList{&layout, Size{~0, ~0}};
|
||||||
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
|
Button loadButton{&controlLayout, Size{80, 0}};
|
||||||
|
Widget spacer{&controlLayout, Size{~0, 0}};
|
||||||
|
Button saveButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button editButton{&controlLayout, Size{80, 0}};
|
||||||
|
Button removeButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
|
|
||||||
struct ToolsWindow : Window {
|
struct ToolsWindow : Window {
|
||||||
ToolsWindow();
|
ToolsWindow();
|
||||||
@@ -106,8 +115,10 @@ public:
|
|||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
TabFrame panel{&layout, Size{~0, ~0}};
|
TabFrame panel{&layout, Size{~0, ~0}};
|
||||||
CheatEditor cheatEditor{&panel};
|
CheatEditor cheatEditor{&panel};
|
||||||
|
StateManager stateManager{&panel};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unique_pointer<CheatDatabase> cheatDatabase;
|
extern unique_pointer<CheatDatabase> cheatDatabase;
|
||||||
extern unique_pointer<CheatWindow> cheatWindow;
|
extern unique_pointer<CheatWindow> cheatWindow;
|
||||||
|
extern unique_pointer<StateWindow> stateWindow;
|
||||||
extern unique_pointer<ToolsWindow> toolsWindow;
|
extern unique_pointer<ToolsWindow> toolsWindow;
|
||||||
|
@@ -65,9 +65,13 @@ auto BrowserDialogWindow::accept() -> void {
|
|||||||
response.selected.append(string{state.path, name});
|
response.selected.append(string{state.path, name});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state.action == "selectFolder" && batched) {
|
if(state.action == "selectFolder") {
|
||||||
string name = batched.left()->cell(0)->text();
|
if(batched) {
|
||||||
if(isFolder(name)) response.selected.append(string{state.path, name, "/"});
|
string name = batched.left()->cell(0)->text();
|
||||||
|
if(isFolder(name)) response.selected.append(string{state.path, name, "/"});
|
||||||
|
} else {
|
||||||
|
response.selected.append(state.path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(response.selected) window.setModal(false);
|
if(response.selected) window.setModal(false);
|
||||||
|
Reference in New Issue
Block a user