mirror of https://github.com/bsnes-emu/bsnes.git
Update to v070r09 release.
byuu says: - removed native OS dialog option, I don't plan to add all the code required to make it optional everywhere - cheat database supported, although it's external now. Either in the .bsnes folder or with the binary, named cheats.xml - cheats.xml is external so that binaries can be much smaller, important for multiple profiles - added "find codes" button to cheat editor (need to gray it out permanently if cheats.xml isn't found) - added cheat database add window, works the same as bsnes/Qt, but it will also alert you if you run out of empty cheat slots upon import - note: I should rename that ok button to "Import" - hooked up callbacks for multitap/mouse/SS/justifier input - added mapping for mouse axes and buttons - used a simplified approach that only lets you map left/middle/right buttons, but doesn't need a separate popup window or fake controls - moved capture mouse command to tools menu - different from Qt where you'd click inside the main window, meant to be safer from accidental capture, escape still releases capture - made a skeleton for GUI hotkey support, but the only hotkey is escape
This commit is contained in:
parent
5286481d8d
commit
8a53e9ed22
|
@ -1,7 +1,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "070.08";
|
||||
static const char Version[] = "070.09";
|
||||
static const unsigned SerializerVersion = 13;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ void Configuration::create() {
|
|||
attach(input.driver = "", "input.driver");
|
||||
|
||||
attach(settings.focusPolicy = 0, "settings.focusPolicy");
|
||||
attach(settings.useNativeDialogs = false, "settings.useNativeDialogs");
|
||||
|
||||
attach(controller.port1 = 1, "controller.port1");
|
||||
attach(controller.port2 = 1, "controller.port2");
|
||||
|
|
|
@ -38,7 +38,6 @@ struct Configuration : public configuration {
|
|||
|
||||
struct Settings {
|
||||
unsigned focusPolicy;
|
||||
bool useNativeDialogs;
|
||||
} settings;
|
||||
|
||||
struct Controller {
|
||||
|
|
|
@ -41,8 +41,6 @@ void MainWindow::create() {
|
|||
if(config.controller.port2 == 3) systemPort2Mouse.setChecked();
|
||||
if(config.controller.port2 == 4) systemPort2SuperScope.setChecked();
|
||||
if(config.controller.port2 == 5) systemPort2Justifiers.setChecked();
|
||||
systemCaptureMouse.create(system, "Capture Mouse");
|
||||
systemCaptureMouse.setEnabled(false);
|
||||
|
||||
settings.create(*this, "Settings");
|
||||
settingsVideoMode.create(settings, "Video Mode");
|
||||
|
@ -92,7 +90,9 @@ void MainWindow::create() {
|
|||
toolsStateLoad3.create(toolsStateLoad, "Slot 3");
|
||||
toolsStateLoad4.create(toolsStateLoad, "Slot 4");
|
||||
toolsStateLoad5.create(toolsStateLoad, "Slot 5");
|
||||
toolsSeparator.create(tools);
|
||||
toolsSeparator1.create(tools);
|
||||
toolsCaptureMouse.create(tools, "Capture Mouse");
|
||||
toolsSeparator2.create(tools);
|
||||
toolsCheatEditor.create(tools, "Cheat Editor ...");
|
||||
toolsStateManager.create(tools, "State Manager ...");
|
||||
|
||||
|
@ -104,7 +104,12 @@ void MainWindow::create() {
|
|||
setMenuVisible(true);
|
||||
setStatusVisible(true);
|
||||
|
||||
systemLoadCartridge.onTick = []() { utility.loadCartridgeNormal(); };
|
||||
systemLoadCartridge.onTick = []() {
|
||||
fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) {
|
||||
cartridge.loadNormal(filename);
|
||||
});
|
||||
};
|
||||
|
||||
systemLoadCartridgeBsxSlotted.onTick = []() { singleSlotLoader.loadCartridgeBsxSlotted(); };
|
||||
systemLoadCartridgeBsx.onTick = []() { singleSlotLoader.loadCartridgeBsx(); };
|
||||
systemLoadCartridgeSufamiTurbo.onTick = []() { doubleSlotLoader.loadCartridgeSufamiTurbo(); };
|
||||
|
@ -180,6 +185,8 @@ void MainWindow::create() {
|
|||
toolsStateLoad4.onTick = []() { utility.loadState(4); };
|
||||
toolsStateLoad5.onTick = []() { utility.loadState(5); };
|
||||
|
||||
toolsCaptureMouse.onTick = []() { input.acquire(); };
|
||||
|
||||
toolsCheatEditor.onTick = []() { cheatEditor.setVisible(); };
|
||||
toolsStateManager.onTick = []() { stateManager.setVisible(); };
|
||||
|
||||
|
@ -207,10 +214,12 @@ void MainWindow::synchronize() {
|
|||
systemReset.setEnabled(false);
|
||||
toolsStateSave.setEnabled(false);
|
||||
toolsStateLoad.setEnabled(false);
|
||||
toolsCaptureMouse.setEnabled(false);
|
||||
} else {
|
||||
systemPower.setEnabled(true);
|
||||
systemReset.setEnabled(true);
|
||||
toolsStateSave.setEnabled(true);
|
||||
toolsStateLoad.setEnabled(true);
|
||||
toolsCaptureMouse.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ struct MainWindow : Window {
|
|||
MenuRadioItem systemPort2Mouse;
|
||||
MenuRadioItem systemPort2SuperScope;
|
||||
MenuRadioItem systemPort2Justifiers;
|
||||
MenuItem systemCaptureMouse;
|
||||
Menu settings;
|
||||
Menu settingsVideoMode;
|
||||
MenuRadioItem settingsVideoMode1x;
|
||||
|
@ -58,7 +57,9 @@ struct MainWindow : Window {
|
|||
MenuItem toolsStateLoad3;
|
||||
MenuItem toolsStateLoad4;
|
||||
MenuItem toolsStateLoad5;
|
||||
MenuSeparator toolsSeparator;
|
||||
MenuSeparator toolsSeparator1;
|
||||
MenuItem toolsCaptureMouse;
|
||||
MenuSeparator toolsSeparator2;
|
||||
MenuItem toolsCheatEditor;
|
||||
MenuItem toolsStateManager;
|
||||
Menu help;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
void InputMapper::poll_hotkeys(unsigned scancode, int16_t value) {
|
||||
if(value == 0) return;
|
||||
if(scancode == keyboard(0)[Keyboard::Escape]) input.unacquire();
|
||||
if(mainWindow.focused() == false) return;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include "../base.hpp"
|
||||
#include "hotkeys.cpp"
|
||||
InputMapper inputMapper;
|
||||
|
||||
void InputMapper::AbstractInput::bind() {
|
||||
|
@ -8,6 +9,8 @@ void InputMapper::AbstractInput::bind() {
|
|||
else if(strend(mapping, ".Right")) type = Type::HatRight;
|
||||
else if(strend(mapping, ".Lo")) type = Type::AxisLo;
|
||||
else if(strend(mapping, ".Hi")) type = Type::AxisHi;
|
||||
else if(strbegin(mapping, "MS") && strend(mapping, "axis")) type = Type::MouseAxis;
|
||||
else if(strbegin(mapping, "MS")) type = Type::MouseButton;
|
||||
else type = Type::Button;
|
||||
|
||||
string mappingValue = mapping;
|
||||
|
@ -15,10 +18,22 @@ void InputMapper::AbstractInput::bind() {
|
|||
scancode = Scancode::decode(mappingValue);
|
||||
}
|
||||
|
||||
int16_t InputMapper::AnalogInput::poll() {
|
||||
int16_t value = inputMapper.state[inputMapper.activeState][scancode];
|
||||
switch(type) {
|
||||
case AbstractInput::Type::MouseAxis: {
|
||||
if(input.acquired() == false) return 0;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t InputMapper::DigitalInput::poll() {
|
||||
int16_t value = inputMapper.state[inputMapper.activeState][scancode];
|
||||
switch(type) {
|
||||
case AbstractInput::Type::Button: return (bool)value;
|
||||
case AbstractInput::Type::MouseButton: return (bool)value & input.acquired();
|
||||
case AbstractInput::Type::HatUp: return (bool)(value & Joypad::HatUp);
|
||||
case AbstractInput::Type::HatDown: return (bool)(value & Joypad::HatDown);
|
||||
case AbstractInput::Type::HatLeft: return (bool)(value & Joypad::HatLeft);
|
||||
|
@ -102,6 +117,16 @@ void InputMapper::Mouse::create(const char *deviceName, const char *configName)
|
|||
right.mapping = "MS0::Button2";
|
||||
}
|
||||
|
||||
int16_t InputMapper::Mouse::poll(unsigned id) {
|
||||
switch(id) {
|
||||
case SNES::Input::MouseID::X: return x.poll();
|
||||
case SNES::Input::MouseID::Y: return y.poll();
|
||||
case SNES::Input::MouseID::Left: return left.poll();
|
||||
case SNES::Input::MouseID::Right: return right.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputMapper::SuperScope::create(const char *deviceName, const char *configName) {
|
||||
name = deviceName;
|
||||
x.name = "X-axis"; y.name = "Y-axis";
|
||||
|
@ -124,6 +149,18 @@ void InputMapper::SuperScope::create(const char *deviceName, const char *configN
|
|||
pause.mapping = "KB0::P";
|
||||
}
|
||||
|
||||
int16_t InputMapper::SuperScope::poll(unsigned id) {
|
||||
switch(id) {
|
||||
case SNES::Input::SuperScopeID::X: return x.poll();
|
||||
case SNES::Input::SuperScopeID::Y: return y.poll();
|
||||
case SNES::Input::SuperScopeID::Trigger: return trigger.poll();
|
||||
case SNES::Input::SuperScopeID::Cursor: return cursor.poll();
|
||||
case SNES::Input::SuperScopeID::Turbo: return turbo.poll();
|
||||
case SNES::Input::SuperScopeID::Pause: return pause.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputMapper::Justifier::create(const char *deviceName, const char *configName) {
|
||||
name = deviceName;
|
||||
x.name = "X-axis"; y.name = "Y-axis";
|
||||
|
@ -144,6 +181,16 @@ void InputMapper::Justifier::create(const char *deviceName, const char *configNa
|
|||
}
|
||||
}
|
||||
|
||||
int16_t InputMapper::Justifier::poll(unsigned id) {
|
||||
switch(id) {
|
||||
case SNES::Input::JustifierID::X: return x.poll();
|
||||
case SNES::Input::JustifierID::Y: return y.poll();
|
||||
case SNES::Input::JustifierID::Trigger: return trigger.poll();
|
||||
case SNES::Input::JustifierID::Start: return start.poll();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputMapper::create() {
|
||||
activeState = 0;
|
||||
|
||||
|
@ -199,6 +246,7 @@ void InputMapper::poll() {
|
|||
|
||||
for(unsigned i = 0; i < Scancode::Limit; i++) {
|
||||
if(state[0][i] != state[1][i]) {
|
||||
poll_hotkeys(i, state[activeState][i]);
|
||||
inputSettings.inputEvent(i, state[activeState][i]);
|
||||
}
|
||||
}
|
||||
|
@ -207,6 +255,28 @@ void InputMapper::poll() {
|
|||
int16_t InputMapper::poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
if(port == 0) {
|
||||
if(device == SNES::Input::Device::Joypad) return port1.gamepad.poll(id);
|
||||
if(device == SNES::Input::Device::Multitap) switch(index) {
|
||||
case 0: return port1.multitapA.poll(id);
|
||||
case 1: return port1.multitapB.poll(id);
|
||||
case 2: return port1.multitapC.poll(id);
|
||||
case 3: return port1.multitapD.poll(id);
|
||||
}
|
||||
if(device == SNES::Input::Device::Mouse) return port1.mouse.poll(id);
|
||||
} else {
|
||||
if(device == SNES::Input::Device::Joypad) return port2.gamepad.poll(id);
|
||||
if(device == SNES::Input::Device::Multitap) switch(index) {
|
||||
case 0: return port2.multitapA.poll(id);
|
||||
case 1: return port2.multitapB.poll(id);
|
||||
case 2: return port2.multitapC.poll(id);
|
||||
case 3: return port2.multitapD.poll(id);
|
||||
}
|
||||
if(device == SNES::Input::Device::Mouse) return port2.mouse.poll(id);
|
||||
if(device == SNES::Input::Device::SuperScope) return port2.superScope.poll(id);
|
||||
if(device == SNES::Input::Device::Justifier) return port2.justifierA.poll(id);
|
||||
if(device == SNES::Input::Device::Justifiers) switch(index) {
|
||||
case 0: return port2.justifierA.poll(id);
|
||||
case 1: return port2.justifierB.poll(id);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct InputMapper {
|
||||
struct AbstractInput {
|
||||
enum class Type : unsigned { Button, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type;
|
||||
enum class Type : unsigned { Button, MouseAxis, MouseButton, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type;
|
||||
string name;
|
||||
string mapping;
|
||||
unsigned scancode;
|
||||
|
@ -8,6 +8,7 @@ struct InputMapper {
|
|||
};
|
||||
|
||||
struct AnalogInput : AbstractInput {
|
||||
int16_t poll();
|
||||
};
|
||||
|
||||
struct DigitalInput : AbstractInput {
|
||||
|
@ -23,25 +24,28 @@ struct InputMapper {
|
|||
DigitalInput b, a, y, x;
|
||||
DigitalInput l, r, select, start;
|
||||
void create(const char *deviceName, const char *configName);
|
||||
int16_t poll(unsigned index);
|
||||
int16_t poll(unsigned id);
|
||||
};
|
||||
|
||||
struct Mouse : Controller {
|
||||
AnalogInput x, y;
|
||||
DigitalInput left, right;
|
||||
void create(const char *deviceName, const char *configName);
|
||||
int16_t poll(unsigned id);
|
||||
};
|
||||
|
||||
struct SuperScope : Controller {
|
||||
AnalogInput x, y;
|
||||
DigitalInput trigger, cursor, turbo, pause;
|
||||
void create(const char *deviceName, const char *configName);
|
||||
int16_t poll(unsigned id);
|
||||
};
|
||||
|
||||
struct Justifier : Controller {
|
||||
AnalogInput x, y;
|
||||
DigitalInput trigger, start;
|
||||
void create(const char *deviceName, const char *configName);
|
||||
int16_t poll(unsigned id);
|
||||
};
|
||||
|
||||
struct ControllerPort : array<Controller*> {
|
||||
|
@ -76,6 +80,7 @@ struct InputMapper {
|
|||
void bind();
|
||||
void poll();
|
||||
int16_t poll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
||||
void poll_hotkeys(unsigned scancode, int16_t value);
|
||||
int16_t value(unsigned scancode);
|
||||
};
|
||||
|
||||
|
|
|
@ -27,12 +27,6 @@ void AdvancedSettings::create() {
|
|||
if(config.settings.focusPolicy == 1) focusPolicyIgnore.setChecked();
|
||||
if(config.settings.focusPolicy == 2) focusPolicyAllow.setChecked();
|
||||
|
||||
miscellaneousLabel.create(*this, x, y, 595, Style::LabelHeight, "Miscellaneous :."); y += Style::LabelHeight + 5;
|
||||
miscellaneousLabel.setFont(application.proportionalFontBold);
|
||||
|
||||
useNativeDialogs.create(*this, x, y, 595, Style::CheckBoxHeight, "Use native OS dialogs"); y += Style::CheckBoxHeight + 5;
|
||||
useNativeDialogs.setChecked(config.settings.useNativeDialogs);
|
||||
|
||||
setGeometry(160, 160, 605, y);
|
||||
|
||||
lstring list;
|
||||
|
@ -76,6 +70,4 @@ void AdvancedSettings::create() {
|
|||
focusPolicyPause.onTick = []() { config.settings.focusPolicy = 0; };
|
||||
focusPolicyIgnore.onTick = []() { config.settings.focusPolicy = 1; };
|
||||
focusPolicyAllow.onTick = []() { config.settings.focusPolicy = 2; };
|
||||
|
||||
useNativeDialogs.onTick = []() { config.settings.useNativeDialogs = advancedSettings.useNativeDialogs.checked(); };
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ struct AdvancedSettings : Window {
|
|||
RadioBox focusPolicyPause;
|
||||
RadioBox focusPolicyIgnore;
|
||||
RadioBox focusPolicyAllow;
|
||||
Label miscellaneousLabel;
|
||||
CheckBox useNativeDialogs;
|
||||
|
||||
void create();
|
||||
};
|
||||
|
|
|
@ -8,6 +8,9 @@ void InputSettings::create() {
|
|||
setFont(application.proportionalFontBold);
|
||||
setStatusVisible();
|
||||
|
||||
activeInput = 0;
|
||||
activeMouse = 0;
|
||||
|
||||
unsigned x = 5, y = 5, height = Style::ButtonHeight;
|
||||
|
||||
portLabel.create(*this, x, y, 50, Style::ComboBoxHeight, "Port:");
|
||||
|
@ -21,22 +24,40 @@ void InputSettings::create() {
|
|||
mappingList.setHeaderVisible();
|
||||
mappingList.setFocused();
|
||||
|
||||
mouseXaxis.create(*this, x, y, 100, height, "Mouse X-axis");
|
||||
mouseXaxis.setVisible(false);
|
||||
mouseYaxis.create(*this, x + 105, y, 100, height, "Mouse Y-axis");
|
||||
mouseYaxis.setVisible(false);
|
||||
mouseLeft.create(*this, x, y, 100, height, "Mouse Left");
|
||||
mouseLeft.setVisible(false);
|
||||
mouseMiddle.create(*this, x + 105, y, 100, height, "Mouse Middle");
|
||||
mouseMiddle.setVisible(false);
|
||||
mouseRight.create(*this, x + 105 + 105, y, 100, height, "Mouse Right");
|
||||
mouseRight.setVisible(false);
|
||||
clearAllButton.create(*this, 515 - 85 - 85, y, 80, height, "Clear All");
|
||||
clearButton.create(*this, 515 - 85, y, 80, height, "Clear");
|
||||
y += height + 5;
|
||||
|
||||
setGeometry(160, 160, 515, y);
|
||||
|
||||
portChanged();
|
||||
portBox.onChange = { &InputSettings::portChanged, this };
|
||||
deviceBox.onChange = { &InputSettings::deviceChanged, this };
|
||||
refreshDevices();
|
||||
portBox.onChange = { &InputSettings::refreshDevices, this };
|
||||
deviceBox.onChange = { &InputSettings::refreshMappings, this };
|
||||
mappingList.onActivate = { &InputSettings::assignInput, this };
|
||||
|
||||
mouseXaxis.onTick = []() { inputSettings.setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Xaxis])); };
|
||||
mouseYaxis.onTick = []() { inputSettings.setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Yaxis])); };
|
||||
mouseLeft.onTick = []() { inputSettings.setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button0])); };
|
||||
mouseMiddle.onTick = []() { inputSettings.setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button1])); };
|
||||
mouseRight.onTick = []() { inputSettings.setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button2])); };
|
||||
|
||||
clearAllButton.onTick = { &InputSettings::clearAll, this };
|
||||
clearButton.onTick = { &InputSettings::clearSelected, this };
|
||||
|
||||
onClose = []() { inputSettings.endAssignment(); return true; };
|
||||
}
|
||||
|
||||
void InputSettings::portChanged() {
|
||||
void InputSettings::refreshDevices() {
|
||||
deviceBox.reset();
|
||||
InputMapper::ControllerPort &port = (
|
||||
portBox.selection() == 0
|
||||
|
@ -47,10 +68,10 @@ void InputSettings::portChanged() {
|
|||
for(unsigned i = 0; i < port.size(); i++) {
|
||||
deviceBox.addItem(port[i]->name);
|
||||
}
|
||||
deviceChanged();
|
||||
refreshMappings();
|
||||
}
|
||||
|
||||
void InputSettings::deviceChanged() {
|
||||
void InputSettings::refreshMappings() {
|
||||
mappingList.reset();
|
||||
InputMapper::ControllerPort &port = (
|
||||
portBox.selection() == 0
|
||||
|
@ -82,18 +103,40 @@ void InputSettings::assignInput() {
|
|||
inputMapper.poll(); //flush any pending keypresses
|
||||
activeInput = controller[position()];
|
||||
setStatusText(string("Set assignment for [", activeInput->name, "] ..."));
|
||||
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
|
||||
mouseLeft.setVisible(false);
|
||||
mouseMiddle.setVisible(false);
|
||||
mouseRight.setVisible(false);
|
||||
mouseXaxis.setVisible(true);
|
||||
mouseYaxis.setVisible(true);
|
||||
} else {
|
||||
mouseXaxis.setVisible(false);
|
||||
mouseYaxis.setVisible(false);
|
||||
mouseLeft.setVisible(true);
|
||||
mouseMiddle.setVisible(true);
|
||||
mouseRight.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettings::setMapping(const char *mapping) {
|
||||
activeInput->mapping = mapping;
|
||||
activeInput = 0;
|
||||
inputMapper.bind();
|
||||
setStatusText("");
|
||||
endAssignment();
|
||||
}
|
||||
|
||||
void InputSettings::endAssignment() {
|
||||
activeInput = 0;
|
||||
portBox.setEnabled(true);
|
||||
deviceBox.setEnabled(true);
|
||||
mappingList.setEnabled(true);
|
||||
deviceChanged();
|
||||
setStatusText("");
|
||||
mouseXaxis.setVisible(false);
|
||||
mouseYaxis.setVisible(false);
|
||||
mouseLeft.setVisible(false);
|
||||
mouseMiddle.setVisible(false);
|
||||
mouseRight.setVisible(false);
|
||||
refreshMappings();
|
||||
}
|
||||
|
||||
void InputSettings::inputEvent(uint16_t scancode, int16_t value) {
|
||||
|
@ -106,6 +149,8 @@ void InputSettings::inputEvent(uint16_t scancode, int16_t value) {
|
|||
} else if(dynamic_cast<InputMapper::DigitalInput*>(activeInput)) {
|
||||
if(Keyboard::isAnyKey(scancode) && value) {
|
||||
setMapping(mapping);
|
||||
} else if(Mouse::isAnyButton(scancode) && value) {
|
||||
activeMouse = Mouse::numberDecode(scancode);
|
||||
} else if(Joypad::isAnyHat(scancode) && value) {
|
||||
if(value == Joypad::HatUp) setMapping(string(mapping, ".Up"));
|
||||
else if(value == Joypad::HatDown) setMapping(string(mapping, ".Down"));
|
||||
|
@ -156,7 +201,8 @@ void InputSettings::clearAll() {
|
|||
|
||||
for(unsigned i = 0; i < controller.size(); i++) controller[i]->mapping = "";
|
||||
inputMapper.bind();
|
||||
deviceChanged();
|
||||
refreshMappings();
|
||||
endAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +217,8 @@ void InputSettings::clearSelected() {
|
|||
|
||||
controller[position()]->mapping = "";
|
||||
inputMapper.bind();
|
||||
deviceChanged();
|
||||
refreshMappings();
|
||||
endAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@ struct InputSettings : Window {
|
|||
Label deviceLabel;
|
||||
ComboBox deviceBox;
|
||||
ListBox mappingList;
|
||||
Button mouseXaxis;
|
||||
Button mouseYaxis;
|
||||
Button mouseLeft;
|
||||
Button mouseMiddle;
|
||||
Button mouseRight;
|
||||
Button clearAllButton;
|
||||
Button clearButton;
|
||||
|
||||
|
@ -16,11 +21,13 @@ private:
|
|||
bool joypadsCalibrated;
|
||||
bool joypadsCalibrating;
|
||||
int16_t joypadCalibration[Joypad::Count][Joypad::Axes];
|
||||
unsigned activeMouse;
|
||||
|
||||
void portChanged();
|
||||
void deviceChanged();
|
||||
void refreshDevices();
|
||||
void refreshMappings();
|
||||
void setMapping(const char *mapping);
|
||||
void assignInput();
|
||||
void endAssignment();
|
||||
|
||||
void clearAll();
|
||||
void clearSelected();
|
||||
|
|
|
@ -95,6 +95,7 @@ void CheatEditor::create() {
|
|||
descLabel.create(*this, x, y, 80, Style::TextBoxHeight, "Description:");
|
||||
descEdit.create (*this, x + 80, y, 420, Style::TextBoxHeight); y+= Style::TextBoxHeight + 5;
|
||||
|
||||
findButton.create(*this, x, y, 100, height, "Find Codes ...");
|
||||
clearAllButton.create(*this, x + 505 - 85 - 85, y, 80, height, "Clear All");
|
||||
clearButton.create(*this, x + 505 - 85, y, 80, height, "Clear"); y += height + 5;
|
||||
|
||||
|
@ -104,11 +105,48 @@ void CheatEditor::create() {
|
|||
cheatList.onChange = { &CheatEditor::synchronize, this };
|
||||
cheatList.onTick = { &CheatEditor::toggle, this };
|
||||
codeEdit.onChange = descEdit.onChange = { &CheatEditor::bind, this };
|
||||
findButton.onTick = { &CheatEditor::findCodes, this };
|
||||
clearAllButton.onTick = { &CheatEditor::clearAll, this };
|
||||
clearButton.onTick = { &CheatEditor::clear, this };
|
||||
|
||||
onClose = []() {
|
||||
cheatEditor.databaseWindow.setVisible(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
//databaseWindow
|
||||
application.windows.append(&databaseWindow);
|
||||
databaseWindow.create(0, 0, 256, 256);
|
||||
databaseWindow.setDefaultFont(application.proportionalFont);
|
||||
|
||||
x = 5, y = 5;
|
||||
|
||||
databaseList.create(databaseWindow, x, y, 600, 360); y += 365;
|
||||
databaseList.setCheckable(true);
|
||||
|
||||
databaseSelectAll.create(databaseWindow, x, y, 100, height, "Select All");
|
||||
databaseUnselectAll.create(databaseWindow, x + 105, y, 100, height, "Unselect All");
|
||||
databaseOk.create(databaseWindow, 605 - 80, y, 80, height, "Ok"); y += height + 5;
|
||||
|
||||
databaseWindow.setGeometry(192, 192, 610, y);
|
||||
|
||||
databaseSelectAll.onTick = []() {
|
||||
for(unsigned i = 0; i < cheatEditor.databaseCode.size(); i++) {
|
||||
cheatEditor.databaseList.setChecked(i, true);
|
||||
}
|
||||
};
|
||||
|
||||
databaseUnselectAll.onTick = []() {
|
||||
for(unsigned i = 0; i < cheatEditor.databaseCode.size(); i++) {
|
||||
cheatEditor.databaseList.setChecked(i, false);
|
||||
}
|
||||
};
|
||||
|
||||
databaseOk.onTick = { &CheatEditor::addDatabaseCodes, this };
|
||||
}
|
||||
|
||||
void CheatEditor::synchronize() {
|
||||
findButton.setEnabled(SNES::cartridge.loaded());
|
||||
clearAllButton.setEnabled(SNES::cartridge.loaded());
|
||||
if(auto position = cheatList.selection()) {
|
||||
codeEdit.setText(cheatText[position()][1]);
|
||||
|
@ -155,6 +193,77 @@ void CheatEditor::bind() {
|
|||
}
|
||||
}
|
||||
|
||||
void CheatEditor::findCodes() {
|
||||
string data;
|
||||
data.readfile(string(config.path.user, "cheats.xml"));
|
||||
if(data == "") data.readfile(string(config.path.base, "cheats.xml"));
|
||||
if(auto position = strpos(data, SNES::cartridge.sha256())) {
|
||||
auto startPosition = strpos((const char*)data + position(), ">");
|
||||
auto endPosition = strpos((const char*)data + position(), "</cartridge>");
|
||||
string xmlData = string(
|
||||
"<cartridge>\n",
|
||||
substr((const char*)data + position() + 1, startPosition(), endPosition() - startPosition() - 1),
|
||||
"</cartridge>\n"
|
||||
);
|
||||
|
||||
databaseWindow.setTitle("");
|
||||
databaseList.reset();
|
||||
databaseCode.reset();
|
||||
|
||||
xml_element document = xml_parse(xmlData);
|
||||
foreach(root, document.element) {
|
||||
if(root.name == "cartridge") foreach(node, root.element) {
|
||||
if(node.name == "name") databaseWindow.setTitle(node.parse());
|
||||
else if(node.name == "cheat") {
|
||||
string description, code;
|
||||
foreach(element, node.element) {
|
||||
if(element.name == "description") description = element.parse();
|
||||
else if(element.name == "code") code.append(string(element.parse(), "+"));
|
||||
}
|
||||
code.rtrim_once("+");
|
||||
code.append("\t");
|
||||
code.append(description);
|
||||
databaseList.addItem(description);
|
||||
databaseCode.append(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
databaseWindow.setVisible(true);
|
||||
} else {
|
||||
MessageWindow::information(cheatEditor, "Sorry, no cheat codes found for this cartridge.");
|
||||
}
|
||||
}
|
||||
|
||||
optional<unsigned> CheatEditor::findUnusedSlot() {
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
if(cheatText[i][CheatCode] == "" && cheatText[i][CheatDesc] == "") return { true, i };
|
||||
}
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
void CheatEditor::addDatabaseCodes() {
|
||||
for(unsigned n = 0; n < databaseCode.size(); n++) {
|
||||
if(databaseList.checked(n)) {
|
||||
if(auto position = findUnusedSlot()) {
|
||||
lstring part;
|
||||
part.split("\t", databaseCode[n]);
|
||||
SNES::cheat[position()].enabled = false;
|
||||
SNES::cheat[position()] = part[0];
|
||||
cheatList.setChecked(position(), false);
|
||||
cheatText[position()][CheatCode] = part[0];
|
||||
cheatText[position()][CheatDesc] = part[1];
|
||||
} else {
|
||||
MessageWindow::warning(databaseWindow, "Ran out of empty slots for cheat codes.\nNot all cheat codes were added.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
databaseWindow.setVisible(false);
|
||||
refresh();
|
||||
synchronize();
|
||||
}
|
||||
|
||||
void CheatEditor::clearAll() {
|
||||
if(MessageWindow::question(cheatEditor, "Permanently erase all entered cheat codes?", MessageWindow::Buttons::YesNo) == MessageWindow::Response::Yes) {
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
|
|
|
@ -4,9 +4,17 @@ struct CheatEditor : Window {
|
|||
TextBox codeEdit;
|
||||
Label descLabel;
|
||||
TextBox descEdit;
|
||||
Button findButton;
|
||||
Button clearAllButton;
|
||||
Button clearButton;
|
||||
|
||||
Window databaseWindow;
|
||||
ListBox databaseList;
|
||||
lstring databaseCode;
|
||||
Button databaseSelectAll;
|
||||
Button databaseUnselectAll;
|
||||
Button databaseOk;
|
||||
|
||||
void load(string filename);
|
||||
void save(string filename);
|
||||
void create();
|
||||
|
@ -18,6 +26,9 @@ private:
|
|||
void refresh();
|
||||
void toggle(unsigned row);
|
||||
void bind();
|
||||
void findCodes();
|
||||
optional<unsigned> findUnusedSlot();
|
||||
void addDatabaseCodes();
|
||||
void clearAll();
|
||||
void clear();
|
||||
};
|
||||
|
|
|
@ -91,20 +91,6 @@ void Utility::cartridgeUnloaded() {
|
|||
mainWindow.synchronize();
|
||||
}
|
||||
|
||||
void Utility::loadCartridgeNormal() {
|
||||
if(config.settings.useNativeDialogs == false) {
|
||||
fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) {
|
||||
cartridge.loadNormal(filename);
|
||||
});
|
||||
} else {
|
||||
string filename = OS::fileOpen(mainWindow, "SNES cartridges\t*.sfc\nAll files\t*", config.path.current);
|
||||
if(filename != "") {
|
||||
cartridge.loadNormal(filename);
|
||||
SNES::system.power();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Utility::saveState(unsigned slot) {
|
||||
string filename = { cartridge.baseName, "-", slot, ".bst" };
|
||||
SNES::system.runtosave();
|
||||
|
|
|
@ -11,7 +11,6 @@ struct Utility : property<Utility> {
|
|||
void cartridgeLoaded();
|
||||
void cartridgeUnloaded();
|
||||
|
||||
void loadCartridgeNormal();
|
||||
void saveState(unsigned slot);
|
||||
void loadState(unsigned slot);
|
||||
|
||||
|
|
Loading…
Reference in New Issue