From 382ae1e61e872609a4e208aa8f0952ae7181001b Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 17 Sep 2011 16:42:17 +1000 Subject: [PATCH] Update to v082r15 release. byuu says: 7.5 hours of power coding. Das Keyboard definitely helped (but didn't eliminate) RSI, neato. Okay, the NES resampler was using 315 / 88.8 by mistake, so the output rate was wrong, causing way more video/audio stuttering than necessary. STILL forgot the NES APU frame IRQ clear thing on $4015 reads, blah. Why do I always remember things right after uploading the WIPs? Recreated the input manager with a new design, works much nicer than the old one, a whole lot less duplicated code. Recreated the input settings window to work with the new multi-system emulation. All input settings are saved to their own configuration file, input.cfg. Going to batch folder for now. Okay, so the new input settings window ... basically there are now three drop-downs, and I'm not even trying to label them anymore. They are primary, secondary, tertiary selectors for the listed group below. Examples: "NES -> Controller Port 1 -> Gamepad" "SNES -> Controller Port 2 -> Super Scope" "User Interface -> Hotkeys -> Save States" I am aware that "Clear" gets disabled when assigning. I will work on that later, being lazy for now and disabling the entire window. Have to add the mouse binders back, too. Escape and modifiers are both mappable as individual keys now. If you want to clear, click the damn clear button :P Oh, and all input goes to all windows for now. That'll be fixed too when input focus stuff is re-added. --- bsnes/gameboy/cheat/cheat.cpp | 60 ++++-- bsnes/gameboy/memory/memory.cpp | 3 +- bsnes/nall/platform.hpp | 2 + bsnes/nes/cheat/cheat.cpp | 54 +++-- bsnes/nes/memory/memory.cpp | 9 +- bsnes/nes/nes.hpp | 2 +- bsnes/phoenix/core/core.cpp | 12 +- bsnes/phoenix/core/core.hpp | 4 +- bsnes/phoenix/core/layout/fixed-layout.cpp | 4 +- bsnes/phoenix/core/layout/fixed-layout.hpp | 2 +- .../phoenix/core/layout/horizontal-layout.cpp | 6 +- .../phoenix/core/layout/horizontal-layout.hpp | 2 +- bsnes/phoenix/core/layout/vertical-layout.cpp | 6 +- bsnes/phoenix/core/layout/vertical-layout.hpp | 2 +- bsnes/phoenix/qt/platform.moc | 2 +- bsnes/phoenix/qt/widget/combo-box.cpp | 7 +- bsnes/snes/cheat/cheat.cpp | 14 +- bsnes/snes/snes.hpp | 2 +- bsnes/ui/Makefile | 3 +- bsnes/ui/base.hpp | 2 + bsnes/ui/general/main-window.cpp | 9 +- bsnes/ui/general/main-window.hpp | 5 +- bsnes/ui/input/gameboy.cpp | 52 +++++ bsnes/ui/input/gameboy.hpp | 19 ++ bsnes/ui/input/input.cpp | 184 ++++++++++++++++++ bsnes/ui/input/input.hpp | 65 +++++++ bsnes/ui/input/nes.cpp | 60 ++++++ bsnes/ui/input/nes.hpp | 26 +++ bsnes/ui/input/snes.cpp | 73 +++++++ bsnes/ui/input/snes.hpp | 27 +++ bsnes/ui/input/user-interface.cpp | 53 +++++ bsnes/ui/input/user-interface.hpp | 21 ++ bsnes/ui/interface/gameboy.cpp | 13 +- bsnes/ui/interface/interface.cpp | 7 - bsnes/ui/interface/interface.hpp | 1 - bsnes/ui/interface/nes.cpp | 14 +- bsnes/ui/interface/snes.cpp | 18 +- bsnes/ui/main.cpp | 14 +- bsnes/ui/settings/input.cpp | 88 +++++++++ bsnes/ui/settings/input.hpp | 25 +++ bsnes/ui/settings/settings.cpp | 17 ++ bsnes/ui/settings/settings.hpp | 6 + bsnes/ui/utility/utility.cpp | 2 +- 43 files changed, 878 insertions(+), 119 deletions(-) create mode 100755 bsnes/ui/input/gameboy.cpp create mode 100755 bsnes/ui/input/gameboy.hpp create mode 100755 bsnes/ui/input/input.cpp create mode 100755 bsnes/ui/input/input.hpp create mode 100755 bsnes/ui/input/nes.cpp create mode 100755 bsnes/ui/input/nes.hpp create mode 100755 bsnes/ui/input/snes.cpp create mode 100755 bsnes/ui/input/snes.hpp create mode 100755 bsnes/ui/input/user-interface.cpp create mode 100755 bsnes/ui/input/user-interface.hpp create mode 100755 bsnes/ui/settings/input.cpp create mode 100755 bsnes/ui/settings/input.hpp diff --git a/bsnes/gameboy/cheat/cheat.cpp b/bsnes/gameboy/cheat/cheat.cpp index c7a83241..e4c06f3c 100755 --- a/bsnes/gameboy/cheat/cheat.cpp +++ b/bsnes/gameboy/cheat/cheat.cpp @@ -4,27 +4,56 @@ namespace GameBoy { Cheat cheat; -bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp) { +bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) { static bool initialize = false; - static uint8 asciiMap[256]; + static uint8 mapProActionReplay[256], mapGameGenie[256]; if(initialize == false) { initialize = true; - foreach(n, asciiMap) n = ~0; - asciiMap['0'] = 0; asciiMap['1'] = 1; asciiMap['2'] = 2; asciiMap['3'] = 3; - asciiMap['4'] = 4; asciiMap['5'] = 5; asciiMap['6'] = 6; asciiMap['7'] = 7; - asciiMap['8'] = 8; asciiMap['9'] = 9; asciiMap['A'] = 10; asciiMap['B'] = 11; - asciiMap['C'] = 12; asciiMap['D'] = 13; asciiMap['E'] = 14; asciiMap['F'] = 15; + + foreach(n, mapProActionReplay) n = ~0; + mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3; + mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7; + mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11; + mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15; + + foreach(n, mapGameGenie) n = ~0; + mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3; + mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7; + mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11; + mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15; } + string code = code_; + code.upper(); unsigned length = code.length(), bits = 0; - for(unsigned n = 0; n < length; n++) if(asciiMap[code[n]] > 15 && code[n] != '-') return false; + + if(code.wildcard("????:??")) { + code = { substr(code, 0, 4), substr(code, 5, 2) }; + for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 8) & 0xffff; + data = (bits >> 0) & 0xff; + comp = ~0; + return true; + } + + if(code.wildcard("????:??:??")) { + code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) }; + for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 16) & 0xffff; + data = (bits >> 8) & 0xff; + comp = (bits >> 0) & 0xff; + return true; + } if(code.wildcard("???" "-" "???")) { - string text = string(code).replace("-", ""); - for(unsigned n = 0; n < 6; n++) bits |= asciiMap[text[n]] << (20 - n * 4); + code = { substr(code, 0, 3), substr(code, 4, 3) }; + for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4); - addr = (bits >> 0) & 0xffff; + addr = (bits >> 0) & 0xffff; data = (bits >> 16) & 0xff; comp = ~0; @@ -34,12 +63,13 @@ bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned } if(code.wildcard("???" "-" "???" "-" "???")) { - string text = string(code).replace("-", ""); - for(unsigned n = 0; n < 8; n++) bits |= asciiMap[text[n == 7 ? 8 : n]] << (28 - n * 4); + code = { substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) }; + for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4); - addr = (bits >> 8) & 0xffff; + addr = (bits >> 8) & 0xffff; data = (bits >> 24) & 0xff; - comp = (bits >> 0) & 0xff; + comp = (bits >> 0) & 0xff; addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000; comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba; diff --git a/bsnes/gameboy/memory/memory.cpp b/bsnes/gameboy/memory/memory.cpp index 5eab4749..ad281844 100755 --- a/bsnes/gameboy/memory/memory.cpp +++ b/bsnes/gameboy/memory/memory.cpp @@ -48,7 +48,8 @@ uint8 Bus::read(uint16 addr) { for(unsigned n = 0; n < cheat.size(); n++) { if(cheat[n].addr == addr) { if(cheat[n].comp > 255 || cheat[n].comp == data) { - return cheat[n].data; + data = cheat[n].data; + break; } } } diff --git a/bsnes/nall/platform.hpp b/bsnes/nall/platform.hpp index 955ae811..539b2345 100755 --- a/bsnes/nall/platform.hpp +++ b/bsnes/nall/platform.hpp @@ -121,6 +121,8 @@ *path = 0; struct passwd *userinfo = getpwuid(getuid()); if(userinfo) strcpy(path, userinfo->pw_dir); + unsigned length = strlen(path); + if(path[length] != '/') strcpy(path + length, "/"); return path; } diff --git a/bsnes/nes/cheat/cheat.cpp b/bsnes/nes/cheat/cheat.cpp index 0c76e5ea..3d950111 100755 --- a/bsnes/nes/cheat/cheat.cpp +++ b/bsnes/nes/cheat/cheat.cpp @@ -4,24 +4,53 @@ namespace NES { Cheat cheat; -bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp) { +bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) { static bool initialize = false; - static uint8 asciiMap[256]; + static uint8 mapProActionReplay[256], mapGameGenie[256]; if(initialize == false) { initialize = true; - foreach(n, asciiMap) n = ~0; - asciiMap['A'] = 0; asciiMap['P'] = 1; asciiMap['Z'] = 2; asciiMap['L'] = 3; - asciiMap['G'] = 4; asciiMap['I'] = 5; asciiMap['T'] = 6; asciiMap['Y'] = 7; - asciiMap['E'] = 8; asciiMap['O'] = 9; asciiMap['X'] = 10; asciiMap['U'] = 11; - asciiMap['K'] = 12; asciiMap['S'] = 13; asciiMap['V'] = 14; asciiMap['N'] = 15; + + foreach(n, mapProActionReplay) n = ~0; + mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3; + mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7; + mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11; + mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15; + + foreach(n, mapGameGenie) n = ~0; + mapGameGenie['A'] = 0; mapGameGenie['P'] = 1; mapGameGenie['Z'] = 2; mapGameGenie['L'] = 3; + mapGameGenie['G'] = 4; mapGameGenie['I'] = 5; mapGameGenie['T'] = 6; mapGameGenie['Y'] = 7; + mapGameGenie['E'] = 8; mapGameGenie['O'] = 9; mapGameGenie['X'] = 10; mapGameGenie['U'] = 11; + mapGameGenie['K'] = 12; mapGameGenie['S'] = 13; mapGameGenie['V'] = 14; mapGameGenie['N'] = 15; } + string code = code_; + code.upper(); unsigned length = code.length(), bits = 0; - for(unsigned n = 0; n < length; n++) if(asciiMap[code[n]] > 15) return false; - if(code.length() == 6) { - for(unsigned n = 0; n < 6; n++) bits |= asciiMap[code[n]] << (20 - n * 4); + if(code.wildcard("????:??")) { + code = { substr(code, 0, 4), substr(code, 5, 2) }; + for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 8) & 0xffff; + data = (bits >> 0) & 0xff; + comp = ~0; + return true; + } + + if(code.wildcard("????:??:??")) { + code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) }; + for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 16) & 0xffff; + data = (bits >> 8) & 0xff; + comp = (bits >> 0) & 0xff; + return true; + } + + if(length == 6) { + for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4); unsigned addrTable[] = { 10, 9, 8, 7, 2, 1, 0, 19, 14, 13, 12, 11, 6, 5, 4 }; unsigned dataTable[] = { 23, 18, 17, 16, 3, 22, 21, 20 }; @@ -31,8 +60,9 @@ bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned return true; } - if(code.length() == 8) { - for(unsigned n = 0; n < 8; n++) bits |= asciiMap[code[n]] << (28 - n * 4); + if(length == 8) { + for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4); unsigned addrTable[] = { 18, 17, 16, 15, 10, 9, 8, 27, 22, 21, 20, 19, 14, 13, 12 }; unsigned dataTable[] = { 31, 26, 25, 24, 3, 30, 29, 28 }; unsigned compTable[] = { 7, 2, 1, 0, 11, 6, 5,4 }; diff --git a/bsnes/nes/memory/memory.cpp b/bsnes/nes/memory/memory.cpp index 239ed8c1..c158a232 100755 --- a/bsnes/nes/memory/memory.cpp +++ b/bsnes/nes/memory/memory.cpp @@ -13,15 +13,16 @@ Bus bus; uint8 Bus::read(uint16 addr) { uint8 data = cartridge.prg_read(addr); - if(addr <= 0x1fff) return cpu.ram_read(addr); - if(addr <= 0x3fff) return ppu.read(addr); - if(addr <= 0x4017) return cpu.read(addr); + if(addr <= 0x1fff) data = cpu.ram_read(addr); + else if(addr <= 0x3fff) data = ppu.read(addr); + else if(addr <= 0x4017) data = cpu.read(addr); if(cheat.override[addr]) { for(unsigned n = 0; n < cheat.size(); n++) { if(cheat[n].addr == addr) { if(cheat[n].comp > 255 || cheat[n].comp == data) { - return cheat[n].data; + data = cheat[n].data; + break; } } } diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index c8e81349..488f1282 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -4,7 +4,7 @@ namespace NES { namespace Info { static const char Name[] = "bnes"; - static const char Version[] = "000.10"; + static const char Version[] = "000.11"; } } diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index 4330c714..2c0004da 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -144,7 +144,7 @@ void Window::append(Layout &layout) { ((Sizable&)layout).state.window = this; ((Sizable&)layout).state.layout = 0; p.append(layout); - layout.synchronize(); + layout.synchronizeLayout(); } } @@ -159,7 +159,7 @@ void Window::append(Widget &widget) { if(state.widget.append(widget)) { ((Sizable&)widget).state.window = this; p.append(widget); - synchronize(); + synchronizeLayout(); } } @@ -286,7 +286,7 @@ void Window::setWidgetFont(const string &font) { return p.setWidgetFont(font); } -void Window::synchronize() { +void Window::synchronizeLayout() { setGeometry(geometry()); } @@ -509,7 +509,7 @@ void Layout::append(Sizable &sizable) { if(dynamic_cast(&sizable)) { Layout &layout = (Layout&)sizable; - layout.synchronize(); + layout.synchronizeLayout(); } if(dynamic_cast(&sizable)) { @@ -517,7 +517,7 @@ void Layout::append(Sizable &sizable) { if(sizable.window()) sizable.window()->append(widget); } - if(window()) window()->synchronize(); + if(window()) window()->synchronizeLayout(); } void Layout::remove(Sizable &sizable) { @@ -529,7 +529,7 @@ void Layout::remove(Sizable &sizable) { sizable.state.layout = 0; sizable.state.window = 0; - if(window()) window()->synchronize(); + if(window()) window()->synchronizeLayout(); } Layout::Layout(): diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index e1db8cd8..92ff1bf4 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -152,7 +152,7 @@ struct Window : private nall::base_from_member, Object { void setTitle(const nall::string &text); void setVisible(bool visible = true); void setWidgetFont(const nall::string &font); - void synchronize(); + void synchronizeLayout(); Window(); ~Window(); @@ -256,7 +256,7 @@ struct Layout : private nall::base_from_member, Sizable { virtual void append(Sizable &sizable); virtual void remove(Sizable &sizable); virtual void reset() {} - virtual void synchronize() = 0; + virtual void synchronizeLayout() = 0; Layout(); Layout(pLayout &p); diff --git a/bsnes/phoenix/core/layout/fixed-layout.cpp b/bsnes/phoenix/core/layout/fixed-layout.cpp index ca3131dd..10d90c1b 100755 --- a/bsnes/phoenix/core/layout/fixed-layout.cpp +++ b/bsnes/phoenix/core/layout/fixed-layout.cpp @@ -1,6 +1,6 @@ void FixedLayout::append(Sizable &sizable, const Geometry &geometry) { children.append({ &sizable, geometry }); - synchronize(); + synchronizeLayout(); } void FixedLayout::append(Sizable &sizable) { @@ -55,7 +55,7 @@ void FixedLayout::setVisible(bool visible) { } } -void FixedLayout::synchronize() { +void FixedLayout::synchronizeLayout() { foreach(child, children) { Layout::append(*child.sizable); child.sizable->setGeometry(child.geometry); diff --git a/bsnes/phoenix/core/layout/fixed-layout.hpp b/bsnes/phoenix/core/layout/fixed-layout.hpp index 840e3a2e..53a18c75 100755 --- a/bsnes/phoenix/core/layout/fixed-layout.hpp +++ b/bsnes/phoenix/core/layout/fixed-layout.hpp @@ -8,7 +8,7 @@ struct FixedLayout : Layout { void setEnabled(bool enabled = true); void setGeometry(const Geometry &geometry); void setVisible(bool visible = true); - void synchronize(); + void synchronizeLayout(); bool visible(); FixedLayout(); ~FixedLayout(); diff --git a/bsnes/phoenix/core/layout/horizontal-layout.cpp b/bsnes/phoenix/core/layout/horizontal-layout.cpp index 2ed53fe9..2d0c008e 100755 --- a/bsnes/phoenix/core/layout/horizontal-layout.cpp +++ b/bsnes/phoenix/core/layout/horizontal-layout.cpp @@ -1,13 +1,13 @@ void HorizontalLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) { foreach(child, children) if(child.sizable == &sizable) return; children.append({ &sizable, width, height, spacing }); - synchronize(); + synchronizeLayout(); } void HorizontalLayout::append(Sizable &sizable) { foreach(child, children) if(child.sizable == &sizable) return; Layout::append(sizable); - if(window()) window()->synchronize(); + if(window()) window()->synchronizeLayout(); } bool HorizontalLayout::enabled() { @@ -114,7 +114,7 @@ void HorizontalLayout::setVisible(bool visible) { } } -void HorizontalLayout::synchronize() { +void HorizontalLayout::synchronizeLayout() { foreach(child, children) Layout::append(*child.sizable); } diff --git a/bsnes/phoenix/core/layout/horizontal-layout.hpp b/bsnes/phoenix/core/layout/horizontal-layout.hpp index b3f08626..9730fc3b 100755 --- a/bsnes/phoenix/core/layout/horizontal-layout.hpp +++ b/bsnes/phoenix/core/layout/horizontal-layout.hpp @@ -10,7 +10,7 @@ struct HorizontalLayout : public Layout { void setGeometry(const Geometry &geometry); void setMargin(unsigned margin); void setVisible(bool visible = true); - void synchronize(); + void synchronizeLayout(); bool visible(); HorizontalLayout(); ~HorizontalLayout(); diff --git a/bsnes/phoenix/core/layout/vertical-layout.cpp b/bsnes/phoenix/core/layout/vertical-layout.cpp index 1f238bec..20e884b3 100755 --- a/bsnes/phoenix/core/layout/vertical-layout.cpp +++ b/bsnes/phoenix/core/layout/vertical-layout.cpp @@ -1,13 +1,13 @@ void VerticalLayout::append(Sizable &sizable, unsigned width, unsigned height, unsigned spacing) { foreach(child, children) if(child.sizable == &sizable) return; children.append({ &sizable, width, height, spacing }); - synchronize(); + synchronizeLayout(); } void VerticalLayout::append(Sizable &sizable) { foreach(child, children) if(child.sizable == &sizable) return; Layout::append(sizable); - if(window()) window()->synchronize(); + if(window()) window()->synchronizeLayout(); } bool VerticalLayout::enabled() { @@ -118,7 +118,7 @@ void VerticalLayout::setVisible(bool visible) { } } -void VerticalLayout::synchronize() { +void VerticalLayout::synchronizeLayout() { foreach(child, children) Layout::append(*child.sizable); } diff --git a/bsnes/phoenix/core/layout/vertical-layout.hpp b/bsnes/phoenix/core/layout/vertical-layout.hpp index 6be1fbe4..34f0a698 100755 --- a/bsnes/phoenix/core/layout/vertical-layout.hpp +++ b/bsnes/phoenix/core/layout/vertical-layout.hpp @@ -10,7 +10,7 @@ struct VerticalLayout : public Layout { void setGeometry(const Geometry &geometry); void setMargin(unsigned margin); void setVisible(bool visible = true); - void synchronize(); + void synchronizeLayout(); bool visible(); VerticalLayout(); ~VerticalLayout(); diff --git a/bsnes/phoenix/qt/platform.moc b/bsnes/phoenix/qt/platform.moc index 0ecd3114..dd9ed3e2 100755 --- a/bsnes/phoenix/qt/platform.moc +++ b/bsnes/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Thu Sep 8 17:34:23 2011 +** Created: Fri Sep 16 21:12:56 2011 ** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/widget/combo-box.cpp b/bsnes/phoenix/qt/widget/combo-box.cpp index a26033f0..b5e57a93 100755 --- a/bsnes/phoenix/qt/widget/combo-box.cpp +++ b/bsnes/phoenix/qt/widget/combo-box.cpp @@ -1,5 +1,7 @@ void pComboBox::append(const string &text) { + locked = true; qtComboBox->addItem(QString::fromUtf8(text)); + locked = false; } Geometry pComboBox::minimumGeometry() { @@ -10,7 +12,9 @@ Geometry pComboBox::minimumGeometry() { } void pComboBox::reset() { + locked = true; while(qtComboBox->count()) qtComboBox->removeItem(0); + locked = false; } unsigned pComboBox::selection() { @@ -45,7 +49,6 @@ void pComboBox::orphan() { } void pComboBox::onChange() { - if(locked == true) return; comboBox.state.selection = selection(); - if(comboBox.onChange) comboBox.onChange(); + if(locked == false && comboBox.onChange) comboBox.onChange(); } diff --git a/bsnes/snes/cheat/cheat.cpp b/bsnes/snes/cheat/cheat.cpp index 70942017..6cf3ebba 100755 --- a/bsnes/snes/cheat/cheat.cpp +++ b/bsnes/snes/cheat/cheat.cpp @@ -71,19 +71,21 @@ bool Cheat::decode(const string &code, unsigned &addr, unsigned &data) { #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) - if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) { + if(t.wildcard("??????:??")) { //Pro Action Replay - if(strlen(t) == 9 && t[6] == ':') t = { substr(t, 0, 6), substr(t, 7) }; //strip ':' - for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; //validate input + t = { substr(t, 0, 6), substr(t, 7, 2) }; + for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input unsigned r = hex(t); addr = r >> 8; data = r & 0xff; return true; - } else if(strlen(t) == 9 && t[4] == '-') { + } + + if(t.wildcard("????" "-" "????")) { //Game Genie - t = { substr(t, 0, 4), substr(t, 5) }; //strip '-' - for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; //validate input + t = { substr(t, 0, 4), substr(t, 5, 4) }; + for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input t.transform("df4709156bc8a23e", "0123456789abcdef"); unsigned r = hex(t); static unsigned bits[] = { 13, 12, 11, 10, 5, 4, 3, 2, 23, 22, 21, 20, 1, 0, 15, 14, 19, 18, 17, 16, 9, 8, 7, 6 }; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 7e87c37a..f129875b 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.14"; + static const char Version[] = "082.15"; static const unsigned SerializerVersion = 22; } } diff --git a/bsnes/ui/Makefile b/bsnes/ui/Makefile index f9354534..a566f2af 100755 --- a/bsnes/ui/Makefile +++ b/bsnes/ui/Makefile @@ -3,7 +3,7 @@ include $(snes)/Makefile include $(gameboy)/Makefile name := batch -ui_objects := ui-main ui-config ui-interface ui-utility +ui_objects := ui-main ui-config ui-interface ui-input ui-utility ui_objects += ui-general ui-settings ui-tools ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -70,6 +70,7 @@ objects := $(patsubst %,obj/%.o,$(objects)) obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/) obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/) obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/) +obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/) obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/) obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/) obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/) diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index 64dca409..2cc67954 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -21,6 +21,7 @@ using namespace ruby; #include "config/config.hpp" #include "interface/interface.hpp" +#include "input/input.hpp" #include "utility/utility.hpp" #include "general/general.hpp" #include "settings/settings.hpp" @@ -35,6 +36,7 @@ struct Application { string title; string normalFont; string boldFont; + string titleFont; void run(); Application(int argc, char **argv); diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index 853ceb99..3f93c729 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -19,6 +19,8 @@ MainWindow::MainWindow() { snesPower.setText("Power Cycle"); snesReset.setText("Reset"); snesCartridgeUnload.setText("Unload Cartridge"); + snesPort1.setText("Controller Port 1"); + snesPort2.setText("Controller Port 2"); gameBoyMenu.setText("Game Boy"); gameBoyPower.setText("Power Cycle"); @@ -26,7 +28,7 @@ MainWindow::MainWindow() { settingsMenu.setText("Settings"); settingsSynchronizeVideo.setText("Synchronize Video"); - settingsSynchronizeVideo.setChecked(false); + settingsSynchronizeVideo.setChecked(); settingsSynchronizeAudio.setText("Synchronize Audio"); settingsSynchronizeAudio.setChecked(); settingsMuteAudio.setText("Mute Audio"); @@ -67,8 +69,11 @@ MainWindow::MainWindow() { append(snesMenu); snesMenu.append(snesPower); snesMenu.append(snesReset); - snesMenu.append(snesSeparator); + snesMenu.append(snesSeparator1); snesMenu.append(snesCartridgeUnload); + snesMenu.append(snesSeparator2); + snesMenu.append(snesPort1); + snesMenu.append(snesPort2); append(gameBoyMenu); gameBoyMenu.append(gameBoyPower); diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index aae23f66..b47870a7 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -16,8 +16,11 @@ struct MainWindow : Window { Menu snesMenu; Item snesPower; Item snesReset; - Separator snesSeparator; + Separator snesSeparator1; Item snesCartridgeUnload; + Separator snesSeparator2; + Menu snesPort1; + Menu snesPort2; Menu gameBoyMenu; Item gameBoyPower; diff --git a/bsnes/ui/input/gameboy.cpp b/bsnes/ui/input/gameboy.cpp new file mode 100755 index 00000000..24644418 --- /dev/null +++ b/bsnes/ui/input/gameboy.cpp @@ -0,0 +1,52 @@ +int16_t GameBoyController::poll(unsigned n) { + switch(n) { + case 0: return up.poll() & !down.poll(); + case 1: return down.poll() & !up.poll(); + case 2: return left.poll() & !right.poll(); + case 3: return right.poll() & !left.poll(); + case 4: return b.poll(); + case 5: return a.poll(); + case 6: return select.poll(); + case 7: return start.poll(); + } + return 0; +} + +GameBoyController::GameBoyController() { + name = "Controller"; + + up.name = "Up"; + down.name = "Down"; + left.name = "Left"; + right.name = "Right"; + b.name = "B"; + a.name = "A"; + select.name = "Select"; + start.name = "Start"; + + up.mapping = "KB0::Up"; + down.mapping = "KB0::Down"; + left.mapping = "KB0::Left"; + right.mapping = "KB0::Right"; + b.mapping = "KB0::Z"; + a.mapping = "KB0::X"; + select.mapping = "KB0::Apostrophe"; + start.mapping = "KB0::Return"; + + append(up); append(down); append(left); append(right); + append(b); append(a); append(select); append(start); +} + +// + +GameBoyDevice::GameBoyDevice() { + name = "Device"; + append(controller); +} + +// + +GameBoyInput::GameBoyInput() { + name = "Game Boy"; + append(device); +} diff --git a/bsnes/ui/input/gameboy.hpp b/bsnes/ui/input/gameboy.hpp new file mode 100755 index 00000000..ae3a4048 --- /dev/null +++ b/bsnes/ui/input/gameboy.hpp @@ -0,0 +1,19 @@ +struct GameBoyController : TertiaryInput { + DigitalInput up, down, left, right; + DigitalInput b, a, select, start; + + int16_t poll(unsigned n); + GameBoyController(); +}; + +struct GameBoyDevice : SecondaryInput { + GameBoyController controller; + + GameBoyDevice(); +}; + +struct GameBoyInput : PrimaryInput { + GameBoyDevice device; + + GameBoyInput(); +}; diff --git a/bsnes/ui/input/input.cpp b/bsnes/ui/input/input.cpp new file mode 100755 index 00000000..64fa85bc --- /dev/null +++ b/bsnes/ui/input/input.cpp @@ -0,0 +1,184 @@ +#include "../base.hpp" +#include "nes.cpp" +#include "snes.cpp" +#include "gameboy.cpp" +#include "user-interface.cpp" +InputManager *inputManager = 0; + +void AbstractInput::attach(const string &primaryName, const string &secondaryName, const string &tertiaryName) { + string name = { primaryName, "::", secondaryName, "::", tertiaryName, "::", this->name }; + name.replace(" ", ""); + inputManager->config.attach(mapping, name); +} + +void AbstractInput::bind() { + if(mapping.endswith(".Up")) type = Type::HatUp; + else if(mapping.endswith(".Down")) type = Type::HatDown; + else if(mapping.endswith(".Left")) type = Type::HatLeft; + else if(mapping.endswith(".Right")) type = Type::HatRight; + else if(mapping.endswith(".Lo")) type = Type::AxisLo; + else if(mapping.endswith(".Hi")) type = Type::AxisHi; + else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Type::Axis; + else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::Axis; + else type = Type::Button; + + string decode = mapping; + if(auto position = decode.position(".")) decode[position()] = 0; + scancode = Scancode::decode(decode); +} + +int16_t AbstractInput::poll() { + return inputManager->scancode[inputManager->activeScancode][scancode]; +} + +// + +bool AnalogInput::bind(int16_t scancode, int16_t value) { + string encode = Scancode::encode(scancode); + + if(Mouse::isAnyAxis(scancode)) { + for(unsigned n = 0; n < Mouse::Count; n++) { + if(scancode == mouse(n)[Mouse::Xaxis]) { encode.append(".Xaxis"); goto bind; } + if(scancode == mouse(n)[Mouse::Yaxis]) { encode.append(".Yaxis"); goto bind; } + if(scancode == mouse(n)[Mouse::Zaxis]) { encode.append(".Zaxis"); goto bind; } + } + } + + if(Joypad::isAnyAxis(scancode)) { + goto bind; + } + + return false; + +bind: + mapping = encode; + this->scancode = scancode; + this->type = Type::Axis; +} + +// + +bool DigitalInput::bind(int16_t scancode, int16_t value) { + string encode = Scancode::encode(scancode); + Type type; + + if(Keyboard::isAnyKey(scancode) || Keyboard::isAnyModifier(scancode) || Joypad::isAnyButton(scancode)) { + if(value == 0) return false; + type = Type::Button; + goto bind; + } + + if(Joypad::isAnyHat(scancode)) { + if(value & Joypad::HatUp ) { type = Type::HatUp; encode.append(".Up" ); goto bind; } + if(value & Joypad::HatDown ) { type = Type::HatDown; encode.append(".Down" ); goto bind; } + if(value & Joypad::HatLeft ) { type = Type::HatLeft; encode.append(".Left" ); goto bind; } + if(value & Joypad::HatRight) { type = Type::HatRight; encode.append(".Right"); goto bind; } + } + + if(Joypad::isAnyAxis(scancode)) { + if(value < -12288) { type = Type::AxisLo; encode.append(".Lo"); goto bind; } + if(value > +24576) { type = Type::AxisHi; encode.append(".Hi"); goto bind; } + } + + return false; + +bind: + mapping = encode; + this->scancode = scancode; + this->type = type; + return true; +} + +int16_t DigitalInput::poll() { + int16_t value = AbstractInput::poll(); + switch(type) { + case Type::Button: return (bool)(value); + case Type::HatUp: return (bool)(value & Joypad::HatUp); + case Type::HatDown: return (bool)(value & Joypad::HatDown); + case Type::HatLeft: return (bool)(value & Joypad::HatLeft); + case Type::HatRight: return (bool)(value & Joypad::HatRight); + case Type::AxisLo: return (bool)(value < -16384); + case Type::AxisHi: return (bool)(value > +16384); + } + return 0; +} + +// + +void TertiaryInput::attach(const string &primaryName, const string &secondaryName) { + for(unsigned n = 0; n < size(); n++) { + operator[](n).attach(primaryName, secondaryName, name); + } +} + +void TertiaryInput::bind() { + for(unsigned n = 0; n < size(); n++) { + operator[](n).bind(); + } +} + +int16_t TertiaryInput::poll(unsigned n) { + return operator[](n).poll(); +} + +// + +void SecondaryInput::attach(const string &primaryName) { + for(unsigned n = 0; n < size(); n++) { + operator[](n).attach(primaryName, name); + } +} + +void SecondaryInput::bind() { + for(unsigned n = 0; n < size(); n++) { + operator[](n).bind(); + } +} + +// + +void PrimaryInput::attach() { + for(unsigned n = 0; n < size(); n++) { + operator[](n).attach(name); + } +} + +void PrimaryInput::bind() { + for(unsigned n = 0; n < size(); n++) { + operator[](n).bind(); + } +} + +// + +void InputManager::scan() { + activeScancode = !activeScancode; + input.poll(scancode[activeScancode]); + + for(unsigned n = 0; n < Scancode::Limit; n++) { + if(scancode[!activeScancode][n] != scancode[activeScancode][n]) { + inputSettings->inputEvent(n, scancode[activeScancode][n]); + userInterface.inputEvent(n, scancode[activeScancode][n]); + } + } +} + +InputManager::InputManager() { + inputManager = this; + + inputList.append(nes); + inputList.append(snes); + inputList.append(gameBoy); + inputList.append(userInterface); + + for(unsigned n = 0; n < inputList.size(); n++) inputList[n].attach(); + config.load(string{ application->userpath, "input.cfg" }); + config.save(string{ application->userpath, "input.cfg" }); + for(unsigned n = 0; n < inputList.size(); n++) inputList[n].bind(); + + activeScancode = 0; +} + +InputManager::~InputManager() { + config.save(string{ application->userpath, "input.cfg" }); +} diff --git a/bsnes/ui/input/input.hpp b/bsnes/ui/input/input.hpp new file mode 100755 index 00000000..4f383e90 --- /dev/null +++ b/bsnes/ui/input/input.hpp @@ -0,0 +1,65 @@ +struct AbstractInput { + enum class Type : unsigned { Button, Axis, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type; + string name; + string mapping; + unsigned scancode; + + virtual void attach(const string &primaryName, const string &secondaryName, const string &tertiaryName); + virtual void bind(); + virtual bool bind(int16_t scancode, int16_t value) = 0; + virtual int16_t poll(); +}; + +struct AnalogInput : AbstractInput { + bool bind(int16_t scancode, int16_t value); +}; + +struct DigitalInput : AbstractInput { + bool bind(int16_t scancode, int16_t value); + int16_t poll(); +}; + +struct TertiaryInput : reference_array { + string name; + + virtual void attach(const string &primaryName, const string &secondaryName); + virtual void bind(); + virtual int16_t poll(unsigned n); +}; + +struct SecondaryInput : reference_array { + string name; + + virtual void attach(const string &primaryName); + virtual void bind(); +}; + +struct PrimaryInput : reference_array { + string name; + + virtual void attach(); + virtual void bind(); +}; + +#include "nes.hpp" +#include "snes.hpp" +#include "gameboy.hpp" +#include "user-interface.hpp" + +struct InputManager { + configuration config; + int16_t scancode[2][Scancode::Limit]; + bool activeScancode; + + reference_array inputList; + NesInput nes; + SnesInput snes; + GameBoyInput gameBoy; + UserInterfaceInput userInterface; + + void scan(); + InputManager(); + ~InputManager(); +}; + +extern InputManager *inputManager; diff --git a/bsnes/ui/input/nes.cpp b/bsnes/ui/input/nes.cpp new file mode 100755 index 00000000..3460d1b3 --- /dev/null +++ b/bsnes/ui/input/nes.cpp @@ -0,0 +1,60 @@ +int16_t NesGamepad::poll(unsigned n) { + switch(n) { + case 0: return b.poll(); + case 1: return a.poll(); + case 2: return select.poll(); + case 3: return start.poll(); + case 4: return up.poll() & !down.poll(); + case 5: return down.poll() & !up.poll(); + case 6: return left.poll() & !right.poll(); + case 7: return right.poll() & !left.poll(); + } + return 0; +} + +NesGamepad::NesGamepad() { + name = "Gamepad"; + + up.name = "Up"; + down.name = "Down"; + left.name = "Left"; + right.name = "Right"; + b.name = "B"; + a.name = "A"; + select.name = "Select"; + start.name = "Start"; + + up.mapping = "KB0::Up"; + down.mapping = "KB0::Down"; + left.mapping = "KB0::Left"; + right.mapping = "KB0::Right"; + b.mapping = "KB0::Z"; + a.mapping = "KB0::X"; + select.mapping = "KB0::Apostrophe"; + start.mapping = "KB0::Return"; + + append(up); append(down); append(left); append(right); + append(b); append(a); append(select); append(start); +} + +// + +NesPort1Input::NesPort1Input() { + name = "Controller Port 1"; + append(gamepad); +} + +// + +NesPort2Input::NesPort2Input() { + name = "Controller Port 2"; + append(gamepad); +} + +// + +NesInput::NesInput() { + name = "NES"; + append(port1); + append(port2); +} diff --git a/bsnes/ui/input/nes.hpp b/bsnes/ui/input/nes.hpp new file mode 100755 index 00000000..06beed0e --- /dev/null +++ b/bsnes/ui/input/nes.hpp @@ -0,0 +1,26 @@ +struct NesGamepad : TertiaryInput { + DigitalInput up, down, left, right; + DigitalInput b, a, select, start; + + int16_t poll(unsigned n); + NesGamepad(); +}; + +struct NesPort1Input : SecondaryInput { + NesGamepad gamepad; + + NesPort1Input(); +}; + +struct NesPort2Input : SecondaryInput { + NesGamepad gamepad; + + NesPort2Input(); +}; + +struct NesInput : PrimaryInput { + NesPort1Input port1; + NesPort2Input port2; + + NesInput(); +}; diff --git a/bsnes/ui/input/snes.cpp b/bsnes/ui/input/snes.cpp new file mode 100755 index 00000000..e4d4e44a --- /dev/null +++ b/bsnes/ui/input/snes.cpp @@ -0,0 +1,73 @@ +int16_t SnesGamepad::poll(unsigned n) { + switch((SNES::Input::JoypadID)n) { + case SNES::Input::JoypadID::Up: return up.poll() & !down.poll(); + case SNES::Input::JoypadID::Down: return down.poll() & !up.poll(); + case SNES::Input::JoypadID::Left: return left.poll() & !right.poll(); + case SNES::Input::JoypadID::Right: return right.poll() & !left.poll(); + case SNES::Input::JoypadID::B: return b.poll(); + case SNES::Input::JoypadID::A: return a.poll(); + case SNES::Input::JoypadID::Y: return y.poll(); + case SNES::Input::JoypadID::X: return x.poll(); + case SNES::Input::JoypadID::L: return l.poll(); + case SNES::Input::JoypadID::R: return r.poll(); + case SNES::Input::JoypadID::Select: return select.poll(); + case SNES::Input::JoypadID::Start: return start.poll(); + } + return 0; +} + +SnesGamepad::SnesGamepad() { + name = "Gamepad"; + + up.name = "Up"; + down.name = "Down"; + left.name = "Left"; + right.name = "Right"; + b.name = "B"; + a.name = "A"; + y.name = "Y"; + x.name = "X"; + l.name = "L"; + r.name = "R"; + select.name = "Select"; + start.name = "Start"; + + up.mapping = "KB0::Up"; + down.mapping = "KB0::Down"; + left.mapping = "KB0::Left"; + right.mapping = "KB0::Right"; + b.mapping = "KB0::Z"; + a.mapping = "KB0::X"; + y.mapping = "KB0::A"; + x.mapping = "KB0::S"; + l.mapping = "KB0::D"; + r.mapping = "KB0::C"; + select.mapping = "KB0::Apostrophe"; + start.mapping = "KB0::Return"; + + append(up); append(down); append(left); append(right); + append(b); append(a); append(y); append(x); + append(l); append(r); append(select); append(start); +} + +// + +SnesPort1Input::SnesPort1Input() { + name = "Controller Port 1"; + append(gamepad); +} + +// + +SnesPort2Input::SnesPort2Input() { + name = "Controller Port 2"; + append(gamepad); +} + +// + +SnesInput::SnesInput() { + name = "SNES"; + append(port1); + append(port2); +} diff --git a/bsnes/ui/input/snes.hpp b/bsnes/ui/input/snes.hpp new file mode 100755 index 00000000..84579370 --- /dev/null +++ b/bsnes/ui/input/snes.hpp @@ -0,0 +1,27 @@ +struct SnesGamepad : TertiaryInput { + DigitalInput up, down, left, right; + DigitalInput b, a, y, x; + DigitalInput l, r, select, start; + + int16_t poll(unsigned n); + SnesGamepad(); +}; + +struct SnesPort1Input : SecondaryInput { + SnesGamepad gamepad; + + SnesPort1Input(); +}; + +struct SnesPort2Input : SecondaryInput { + SnesGamepad gamepad; + + SnesPort2Input(); +}; + +struct SnesInput : PrimaryInput { + SnesPort1Input port1; + SnesPort2Input port2; + + SnesInput(); +}; diff --git a/bsnes/ui/input/user-interface.cpp b/bsnes/ui/input/user-interface.cpp new file mode 100755 index 00000000..bd865a05 --- /dev/null +++ b/bsnes/ui/input/user-interface.cpp @@ -0,0 +1,53 @@ +void HotkeyGeneral::inputEvent(int16_t scancode, int16_t value) { + if(scancode == toggleFullScreen.scancode && value) { + utility->toggleFullScreen(); + } + + if(scancode == turboMode.scancode) { + static bool Vsync, Async; + if(value) { + Vsync = any_cast(video.get(Video::Synchronize)); + Async = any_cast(audio.get(Audio::Synchronize)); + video.set(Video::Synchronize, false); + audio.set(Audio::Synchronize, false); + } else { + video.set(Video::Synchronize, Vsync); + audio.set(Audio::Synchronize, Async); + } + } +} + +HotkeyGeneral::HotkeyGeneral() { + name = "General"; + + toggleFullScreen.name = "Toggle Fullscreen"; + turboMode.name = "Turbo Mode"; + + toggleFullScreen.mapping = "KB0::F11"; + turboMode.mapping = "KB0::Tilde"; + + append(toggleFullScreen); + append(turboMode); +} + +// + +void HotkeyInput::inputEvent(int16_t scancode, int16_t value) { + general.inputEvent(scancode, value); +} + +HotkeyInput::HotkeyInput() { + name = "Hotkeys"; + append(general); +} + +// + +void UserInterfaceInput::inputEvent(int16_t scancode, int16_t value) { + hotkey.inputEvent(scancode, value); +} + +UserInterfaceInput::UserInterfaceInput() { + name = "User Interface"; + append(hotkey); +} diff --git a/bsnes/ui/input/user-interface.hpp b/bsnes/ui/input/user-interface.hpp new file mode 100755 index 00000000..24e60b3c --- /dev/null +++ b/bsnes/ui/input/user-interface.hpp @@ -0,0 +1,21 @@ +struct HotkeyGeneral : TertiaryInput { + DigitalInput toggleFullScreen; + DigitalInput turboMode; + + void inputEvent(int16_t scancode, int16_t value); + HotkeyGeneral(); +}; + +struct HotkeyInput : SecondaryInput { + HotkeyGeneral general; + + void inputEvent(int16_t scancode, int16_t value); + HotkeyInput(); +}; + +struct UserInterfaceInput : PrimaryInput { + HotkeyInput hotkey; + + void inputEvent(int16_t scancode, int16_t value); + UserInterfaceInput(); +}; diff --git a/bsnes/ui/interface/gameboy.cpp b/bsnes/ui/interface/gameboy.cpp index e51872fa..3ecbd019 100755 --- a/bsnes/ui/interface/gameboy.cpp +++ b/bsnes/ui/interface/gameboy.cpp @@ -62,16 +62,5 @@ void InterfaceGameBoy::audioSample(int16_t csample, int16_t lsample, int16_t rsa } bool InterfaceGameBoy::inputPoll(unsigned id) { - switch((GameBoy::Input)id) { - case GameBoy::Input::Up: return interface->inputState[keyboard(0)[Keyboard::Up]]; - case GameBoy::Input::Down: return interface->inputState[keyboard(0)[Keyboard::Down]]; - case GameBoy::Input::Left: return interface->inputState[keyboard(0)[Keyboard::Left]]; - case GameBoy::Input::Right: return interface->inputState[keyboard(0)[Keyboard::Right]]; - case GameBoy::Input::B: return interface->inputState[keyboard(0)[Keyboard::Z]]; - case GameBoy::Input::A: return interface->inputState[keyboard(0)[Keyboard::X]]; - case GameBoy::Input::Select: return interface->inputState[keyboard(0)[Keyboard::Apostrophe]]; - case GameBoy::Input::Start: return interface->inputState[keyboard(0)[Keyboard::Return]]; - } - - return false; + return inputManager->gameBoy.device.controller.poll(id); } diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index 0bc157ad..3f8d597c 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -149,13 +149,6 @@ Interface::Interface() { //internal void Interface::input_poll() { - bool fullScreen = inputState[keyboard(0)[Keyboard::F11]]; - - input.poll(inputState); - - if(!fullScreen && inputState[keyboard(0)[Keyboard::F11]]) { - utility->toggleFullScreen(); - } } void Interface::video_refresh() { diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index c30ee27e..ef3967d8 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -31,7 +31,6 @@ struct Interface : property { Interface(); - int16_t inputState[Scancode::Limit]; void input_poll(); void video_refresh(); diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index 27f7489e..42258e29 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -45,19 +45,7 @@ void InterfaceNES::audioSample(int16_t sample) { } int16_t InterfaceNES::inputPoll(bool port, unsigned device, unsigned id) { - if(port == 0 && device == 0) { - switch(id) { - case 0: return interface->inputState[keyboard(0)[Keyboard::X]]; - case 1: return interface->inputState[keyboard(0)[Keyboard::Z]]; - case 2: return interface->inputState[keyboard(0)[Keyboard::Apostrophe]]; - case 3: return interface->inputState[keyboard(0)[Keyboard::Return]]; - case 4: return interface->inputState[keyboard(0)[Keyboard::Up]]; - case 5: return interface->inputState[keyboard(0)[Keyboard::Down]]; - case 6: return interface->inputState[keyboard(0)[Keyboard::Left]]; - case 7: return interface->inputState[keyboard(0)[Keyboard::Right]]; - } - } - + if(port == 0 && device == 0) return inputManager->nes.port1.gamepad.poll(id); return 0; } diff --git a/bsnes/ui/interface/snes.cpp b/bsnes/ui/interface/snes.cpp index 16958553..07c1e029 100755 --- a/bsnes/ui/interface/snes.cpp +++ b/bsnes/ui/interface/snes.cpp @@ -78,23 +78,7 @@ void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) { } int16_t InterfaceSNES::inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) { - if(port == 0 && device == SNES::Input::Device::Joypad) { - switch((SNES::Input::JoypadID)id) { - case SNES::Input::JoypadID::Up: return interface->inputState[keyboard(0)[Keyboard::Up]]; - case SNES::Input::JoypadID::Down: return interface->inputState[keyboard(0)[Keyboard::Down]]; - case SNES::Input::JoypadID::Left: return interface->inputState[keyboard(0)[Keyboard::Left]]; - case SNES::Input::JoypadID::Right: return interface->inputState[keyboard(0)[Keyboard::Right]]; - case SNES::Input::JoypadID::B: return interface->inputState[keyboard(0)[Keyboard::Z]]; - case SNES::Input::JoypadID::A: return interface->inputState[keyboard(0)[Keyboard::X]]; - case SNES::Input::JoypadID::Y: return interface->inputState[keyboard(0)[Keyboard::A]]; - case SNES::Input::JoypadID::X: return interface->inputState[keyboard(0)[Keyboard::S]]; - case SNES::Input::JoypadID::L: return interface->inputState[keyboard(0)[Keyboard::D]]; - case SNES::Input::JoypadID::R: return interface->inputState[keyboard(0)[Keyboard::C]]; - case SNES::Input::JoypadID::Select: return interface->inputState[keyboard(0)[Keyboard::Apostrophe]]; - case SNES::Input::JoypadID::Start: return interface->inputState[keyboard(0)[Keyboard::Return]]; - } - } - + if(port == 0 && device == SNES::Input::Device::Joypad) return inputManager->snes.port1.gamepad.poll(id); return 0; } diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index a8dd6ebd..0dccbd9a 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -4,7 +4,7 @@ Application *application = 0; nall::DSP dspaudio; void Application::run() { - interface->input_poll(); + inputManager->scan(); if(interface->loaded() == false) { usleep(20 * 1000); @@ -22,9 +22,16 @@ Application::Application(int argc, char **argv) : quit(false) { realpath = path; unused = ::userpath(path); userpath = path; + #if defined(PLATFORM_WIN) + userpath.append("batch/"); + #else + userpath.append(".config/batch/"); + #endif + mkdir(userpath, 0755); } config = new Config; interface = new Interface; + inputManager = new InputManager; utility = new Utility; title = "batch"; @@ -33,10 +40,12 @@ Application::Application(int argc, char **argv) : quit(false) { string videoDriver = "Direct3D", audioDriver = "XAudio2", inputDriver = "RawInput"; normalFont = "Tahoma, 8"; boldFont = "Tahoma, 8, Bold"; + titleFont = "Tahoma, 16, Bold"; #else string videoDriver = "OpenGL", audioDriver = "PulseAudio", inputDriver = "SDL"; normalFont = "Sans, 8"; boldFont = "Sans, 8, Bold"; + titleFont = "Sans, 16, Bold"; #endif mainWindow = new MainWindow; @@ -49,7 +58,7 @@ Application::Application(int argc, char **argv) : quit(false) { video.driver(videoDriver); video.set(Video::Handle, mainWindow->viewport.handle()); - video.set(Video::Synchronize, false); + video.set(Video::Synchronize, true); video.set(Video::Filter, 0u); video.init(); @@ -87,6 +96,7 @@ Application::~Application() { delete fileBrowser; delete mainWindow; delete utility; + delete inputManager; delete interface; delete config; } diff --git a/bsnes/ui/settings/input.cpp b/bsnes/ui/settings/input.cpp new file mode 100755 index 00000000..632da326 --- /dev/null +++ b/bsnes/ui/settings/input.cpp @@ -0,0 +1,88 @@ +InputSettings *inputSettings = 0; + +InputSettings::InputSettings() : activeInput(0) { + title.setFont(application->titleFont); + title.setText("Input Settings"); + inputList.setHeaderText("Name", "Mapping"); + inputList.setHeaderVisible(); + clearButton.setText("Clear"); + + for(unsigned n = 0; n < inputManager->inputList.size(); n++) { + primary.append(inputManager->inputList[n].name); + } + primaryChange(); + + append(title, ~0, 0, 5); + append(selectionLayout, ~0, 0, 5); + selectionLayout.append(primary, ~0, 0, 5); + selectionLayout.append(secondary, ~0, 0, 5); + selectionLayout.append(tertiary, ~0, 0); + append(inputList, ~0, ~0, 5); + append(controlLayout, ~0, 0); + controlLayout.append(spacer, ~0, 0); + controlLayout.append(clearButton, 80, 0); + + primary.onChange = { &InputSettings::primaryChange, this }; + secondary.onChange = { &InputSettings::secondaryChange, this }; + tertiary.onChange = { &InputSettings::tertiaryChange, this }; + inputList.onChange = { &InputSettings::synchronize, this }; + inputList.onActivate = { &InputSettings::assignInputBegin, this }; + + synchronize(); +} + +void InputSettings::synchronize() { + clearButton.setEnabled(inputList.selected()); +} + +void InputSettings::primaryChange() { + secondary.reset(); + tertiary.reset(); + PrimaryInput &input = inputManager->inputList[primary.selection()]; + for(unsigned n = 0; n < input.size(); n++) { + secondary.append(input[n].name); + } + secondaryChange(); +} + +void InputSettings::secondaryChange() { + tertiary.reset(); + PrimaryInput &pinput = inputManager->inputList[primary.selection()]; + SecondaryInput &input = pinput[secondary.selection()]; + for(unsigned n = 0; n < input.size(); n++) { + tertiary.append(input[n].name); + } + tertiaryChange(); +} + +void InputSettings::tertiaryChange() { + inputList.reset(); + PrimaryInput &pinput = inputManager->inputList[primary.selection()]; + SecondaryInput &sinput = pinput[secondary.selection()]; + TertiaryInput &input = sinput[tertiary.selection()]; + for(unsigned n = 0; n < input.size(); n++) { + inputList.append(input[n].name, input[n].mapping); + } + synchronize(); + inputList.autoSizeColumns(); +} + +void InputSettings::assignInputBegin() { + PrimaryInput &pinput = inputManager->inputList[primary.selection()]; + SecondaryInput &sinput = pinput[secondary.selection()]; + TertiaryInput &tinput = sinput[tertiary.selection()]; + activeInput = &tinput[inputList.selection()]; + + settingsWindow->layout.setEnabled(false); + settingsWindow->setStatusText({ "Set asssignment for [", tinput.name, "::", activeInput->name, "] ..." }); +} + +void InputSettings::inputEvent(int16_t scancode, int16_t value) { + if(activeInput == 0) return; + if(activeInput->bind(scancode, value) == false) return; + + activeInput = 0; + tertiaryChange(); + settingsWindow->setStatusText(""); + settingsWindow->layout.setEnabled(true); +} diff --git a/bsnes/ui/settings/input.hpp b/bsnes/ui/settings/input.hpp new file mode 100755 index 00000000..6238ad7f --- /dev/null +++ b/bsnes/ui/settings/input.hpp @@ -0,0 +1,25 @@ +struct InputSettings : VerticalLayout { + Label title; + HorizontalLayout selectionLayout; + ComboBox primary; + ComboBox secondary; + ComboBox tertiary; + ListView inputList; + HorizontalLayout controlLayout; + Widget spacer; + Button clearButton; + + InputSettings(); + + void synchronize(); + void primaryChange(); + void secondaryChange(); + void tertiaryChange(); + void assignInputBegin(); + void inputEvent(int16_t scancode, int16_t value); + +private: + AbstractInput *activeInput; +}; + +extern InputSettings *inputSettings; diff --git a/bsnes/ui/settings/settings.cpp b/bsnes/ui/settings/settings.cpp index ffd8b535..59b68d08 100755 --- a/bsnes/ui/settings/settings.cpp +++ b/bsnes/ui/settings/settings.cpp @@ -1,7 +1,24 @@ #include "../base.hpp" +#include "input.cpp" SettingsWindow *settingsWindow = 0; SettingsWindow::SettingsWindow() { setTitle("Configuration Settings"); setGeometry({ 128, 128, 640, 360 }); + setStatusFont(application->boldFont); + setStatusVisible(); + + panelList.append("Input"); + + inputSettings = new InputSettings; + + append(layout); + layout.setMargin(5); + layout.append(panelList, 120, ~0, 5); + + layout.append(*inputSettings, ~0, ~0); +} + +SettingsWindow::~SettingsWindow() { + delete inputSettings; } diff --git a/bsnes/ui/settings/settings.hpp b/bsnes/ui/settings/settings.hpp index 0ed081f8..3df61289 100755 --- a/bsnes/ui/settings/settings.hpp +++ b/bsnes/ui/settings/settings.hpp @@ -1,5 +1,11 @@ +#include "input.hpp" + struct SettingsWindow : Window { + HorizontalLayout layout; + ListView panelList; + SettingsWindow(); + ~SettingsWindow(); }; extern SettingsWindow *settingsWindow; diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index e5733bd0..f14f8297 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -19,7 +19,7 @@ void Utility::setMode(Interface::Mode mode) { mainWindow->setTitle({ notdir(interface->baseName), " - ", NES::Info::Name, " v", NES::Info::Version }); mainWindow->nesMenu.setVisible(true); dspaudio.setChannels(1); - dspaudio.setFrequency(315.0 / 88.8 * 6000000.0 / 12.0); + dspaudio.setFrequency(315.0 / 88.0 * 6000000.0 / 12.0); } else if(mode == Interface::Mode::SNES) {