diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 0dcec0a9..5dc526b7 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include using namespace nall; @@ -12,7 +13,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.35"; + static const string Version = "106.36"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index 923de5b6..3ddb6a9f 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -140,8 +140,8 @@ struct VDP : Thread { Pixel output; - array oam; - array objects; + adaptive_array oam; + adaptive_array objects; }; Sprite sprite; diff --git a/higan/ms/vdp/vdp.hpp b/higan/ms/vdp/vdp.hpp index c8388272..90928407 100644 --- a/higan/ms/vdp/vdp.hpp +++ b/higan/ms/vdp/vdp.hpp @@ -68,7 +68,7 @@ struct VDP : Thread { uint4 color; } output; - array objects; + adaptive_array objects; } sprite; //serialization.cpp diff --git a/higan/pce/vdc/vdc.hpp b/higan/pce/vdc/vdc.hpp index 1acc420e..27221752 100644 --- a/higan/pce/vdc/vdc.hpp +++ b/higan/pce/vdc/vdc.hpp @@ -170,7 +170,7 @@ private: bool vflip = 0; bool first = 0; }; - array objects; + adaptive_array objects; uint4 color; uint4 palette; diff --git a/higan/sfc/ppu-fast/background.cpp b/higan/sfc/ppu-fast/background.cpp index 74f4214d..14cfef17 100644 --- a/higan/sfc/ppu-fast/background.cpp +++ b/higan/sfc/ppu-fast/background.cpp @@ -3,8 +3,8 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void if(self.tileMode == TileMode::Mode7) return renderMode7(self, source); if(self.tileMode == TileMode::Inactive) return; - bool windowAbove[256]; - bool windowBelow[256]; + array windowAbove; + array windowBelow; renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.belowEnable, windowBelow); diff --git a/higan/sfc/ppu-fast/io.cpp b/higan/sfc/ppu-fast/io.cpp index d19ec204..8836cc49 100644 --- a/higan/sfc/ppu-fast/io.cpp +++ b/higan/sfc/ppu-fast/io.cpp @@ -589,11 +589,11 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::BPP2; io.bg3.tileMode = TileMode::BPP2; io.bg4.tileMode = TileMode::BPP2; - memory::assign(io.bg1.priority, 8, 11); - memory::assign(io.bg2.priority, 7, 10); - memory::assign(io.bg3.priority, 2, 5); - memory::assign(io.bg4.priority, 1, 4); - memory::assign(io.obj.priority, 3, 6, 9, 12); + io.bg1.priority = { 8, 11}; + io.bg2.priority = { 7, 10}; + io.bg3.priority = { 2, 5}; + io.bg4.priority = { 1, 4}; + io.obj.priority = { 3, 6, 9, 12}; break; case 1: @@ -602,15 +602,15 @@ auto PPU::updateVideoMode() -> void { io.bg3.tileMode = TileMode::BPP2; io.bg4.tileMode = TileMode::Inactive; if(io.bgPriority) { - memory::assign(io.bg1.priority, 5, 8); - memory::assign(io.bg2.priority, 4, 7); - memory::assign(io.bg3.priority, 1, 10); - memory::assign(io.obj.priority, 2, 3, 6, 9); + io.bg1.priority = { 5, 8}; + io.bg2.priority = { 4, 7}; + io.bg3.priority = { 1, 10}; + io.obj.priority = { 2, 3, 6, 9}; } else { - memory::assign(io.bg1.priority, 6, 9); - memory::assign(io.bg2.priority, 5, 8); - memory::assign(io.bg3.priority, 1, 3); - memory::assign(io.obj.priority, 2, 4, 7, 10); + io.bg1.priority = { 6, 9}; + io.bg2.priority = { 5, 8}; + io.bg3.priority = { 1, 3}; + io.obj.priority = { 2, 4, 7, 10}; } break; @@ -619,9 +619,9 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::BPP4; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 3, 7); - memory::assign(io.bg2.priority, 1, 5); - memory::assign(io.obj.priority, 2, 4, 6, 8); + io.bg1.priority = { 3, 7}; + io.bg2.priority = { 1, 5}; + io.obj.priority = { 2, 4, 6, 8}; break; case 3: @@ -629,9 +629,9 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::BPP4; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 3, 7); - memory::assign(io.bg2.priority, 1, 5); - memory::assign(io.obj.priority, 2, 4, 6, 8); + io.bg1.priority = { 3, 7}; + io.bg2.priority = { 1, 5}; + io.obj.priority = { 2, 4, 6, 8}; break; case 4: @@ -639,9 +639,9 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::BPP2; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 3, 7); - memory::assign(io.bg2.priority, 1, 5); - memory::assign(io.obj.priority, 2, 4, 6, 8); + io.bg1.priority = { 3, 7}; + io.bg2.priority = { 1, 5}; + io.obj.priority = { 2, 4, 6, 8}; break; case 5: @@ -649,9 +649,9 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::BPP2; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 3, 7); - memory::assign(io.bg2.priority, 1, 5); - memory::assign(io.obj.priority, 2, 4, 6, 8); + io.bg1.priority = { 3, 7}; + io.bg2.priority = { 1, 5}; + io.obj.priority = { 2, 4, 6, 8}; break; case 6: @@ -659,8 +659,8 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 2, 5); - memory::assign(io.obj.priority, 1, 3, 4, 6); + io.bg1.priority = { 2, 5}; + io.obj.priority = { 1, 3, 4, 6}; break; case 7: @@ -669,16 +669,16 @@ auto PPU::updateVideoMode() -> void { io.bg2.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 2); - memory::assign(io.obj.priority, 1, 3, 4, 5); + io.bg1.priority = { 2}; + io.obj.priority = { 1, 3, 4, 5}; } else { io.bg1.tileMode = TileMode::Mode7; io.bg2.tileMode = TileMode::Mode7; io.bg3.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive; - memory::assign(io.bg1.priority, 3); - memory::assign(io.bg2.priority, 1, 5); - memory::assign(io.obj.priority, 2, 4, 6, 7); + io.bg1.priority = { 3}; + io.bg2.priority = { 1, 5}; + io.obj.priority = { 2, 4, 6, 7}; } break; } diff --git a/higan/sfc/ppu-fast/mode7.cpp b/higan/sfc/ppu-fast/mode7.cpp index 3678e057..48749338 100644 --- a/higan/sfc/ppu-fast/mode7.cpp +++ b/higan/sfc/ppu-fast/mode7.cpp @@ -20,8 +20,8 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { int originX = (a * clip(hoffset - hcenter) & ~63) + (b * clip(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8); int originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8); - bool windowAbove[256]; - bool windowBelow[256]; + array windowAbove; + array windowBelow; renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.belowEnable, windowBelow); diff --git a/higan/sfc/ppu-fast/object.cpp b/higan/sfc/ppu-fast/object.cpp index dcd4f917..1e1bf5ab 100644 --- a/higan/sfc/ppu-fast/object.cpp +++ b/higan/sfc/ppu-fast/object.cpp @@ -1,8 +1,8 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void { if(!self.aboveEnable && !self.belowEnable) return; - bool windowAbove[256]; - bool windowBelow[256]; + array windowAbove; + array windowBelow; renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.belowEnable, windowBelow); diff --git a/higan/sfc/ppu-fast/ppu.cpp b/higan/sfc/ppu-fast/ppu.cpp index 07c93065..5b41920f 100644 --- a/higan/sfc/ppu-fast/ppu.cpp +++ b/higan/sfc/ppu-fast/ppu.cpp @@ -13,21 +13,21 @@ PPU ppu; #include PPU::PPU() { - output = new uint32[512 * 512]; - output += 16 * 512; //overscan offset - + output = new uint32[512 * 512] + 16 * 512; //overscan offset tilecache[TileMode::BPP2] = new uint8[4096 * 8 * 8]; tilecache[TileMode::BPP4] = new uint8[2048 * 8 * 8]; tilecache[TileMode::BPP8] = new uint8[1024 * 8 * 8]; - for(uint y : range(240)) { + for(uint y : range(lines.size())) { lines[y].y = y; } } PPU::~PPU() { - output -= 16 * 512; //overscan offset - delete[] output; + delete[] (output - 16 * 512); //overscan offset + delete[] tilecache[TileMode::BPP2]; + delete[] tilecache[TileMode::BPP4]; + delete[] tilecache[TileMode::BPP8]; } auto PPU::Enter() -> void { diff --git a/higan/sfc/ppu-fast/ppu.hpp b/higan/sfc/ppu-fast/ppu.hpp index 41b9e185..ca15c189 100644 --- a/higan/sfc/ppu-fast/ppu.hpp +++ b/higan/sfc/ppu-fast/ppu.hpp @@ -35,9 +35,9 @@ public: //serialization.cpp auto serialize(serializer&) -> void; - uint1 interlace; - uint1 overscan; - uint1 hires; + uint1 interlace; + uint1 overscan; + uint1 hires; uint16 vram; uint8 oam; @@ -153,7 +153,7 @@ public: uint16 hoffset; uint16 voffset; uint3 tileMode; - uint4 priority[2]; + array priority; } bg1, bg2, bg3, bg4; struct Object { @@ -170,7 +170,7 @@ public: uint7 first; uint1 rangeOver; uint1 timeOver; - uint4 priority[4]; + array priority; } obj; struct Color { @@ -178,7 +178,7 @@ public: auto serialize(serializer&) -> void; WindowColor window; - uint1 enable[7]; + array enable; uint1 directColor; uint1 blendMode; //0 = fixed; 1 = pixel uint1 halve; @@ -249,13 +249,13 @@ public: Latch latch; IO io; - uint16 vram[32 * 1024]; - uint15 cgram[256]; - Object objects[128]; + array vram; + array cgram; + array objects; //[unserialized] - uint32* output = nullptr; - uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata + uint32* output; + array tilecache; //bitplane -> bitmap tiledata struct Line { //line.cpp @@ -278,28 +278,29 @@ public: auto renderObject(PPU::IO::Object&) -> void; //window.cpp - auto renderWindow(PPU::IO::WindowLayer&, bool, bool*) -> void; - auto renderWindow(PPU::IO::WindowColor&, uint, bool*) -> void; + auto renderWindow(PPU::IO::WindowLayer&, bool, array&) -> void; + auto renderWindow(PPU::IO::WindowColor&, uint, array&) -> void; //[unserialized] uint9 y; //constant IO io; - uint15 cgram[256]; + array cgram; - ObjectItem items[32]; - ObjectTile tiles[34]; + array items; + array tiles; - Pixel above[256]; - Pixel below[256]; + array above; + array below; - bool windowAbove[256]; - bool windowBelow[256]; + array windowAbove; + array windowBelow; //flush() static uint start; static uint count; - } lines[240]; + }; + array lines; }; extern PPU ppu; diff --git a/higan/sfc/ppu-fast/window.cpp b/higan/sfc/ppu-fast/window.cpp index 8b0ad4c2..2d49dc40 100644 --- a/higan/sfc/ppu-fast/window.cpp +++ b/higan/sfc/ppu-fast/window.cpp @@ -1,6 +1,6 @@ -auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool* output) -> void { +auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, array& output) -> void { if(!enable || (!self.oneEnable && !self.twoEnable)) { - memory::fill(output, 256, 0); + output.fill(0); return; } @@ -32,17 +32,17 @@ auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool* outp } } -auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, bool* output) -> void { +auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, array& output) -> void { bool set, clear; switch(mask) { - case 0: memory::fill(output, 256, 1); return; //always - case 1: set = 1, clear = 0; break; //inside - case 2: set = 0, clear = 1; break; //outside - case 3: memory::fill(output, 256, 0); return; //never + case 0: output.fill(1); return; //always + case 1: set = 1, clear = 0; break; //inside + case 2: set = 0, clear = 1; break; //outside + case 3: output.fill(0); return; //never } if(!self.oneEnable && !self.twoEnable) { - memory::fill(output, 256, clear); + output.fill(clear); return; } diff --git a/higan/target-bsnes/program/program.cpp b/higan/target-bsnes/program/program.cpp index 51a83f3b..cc8a8de8 100644 --- a/higan/target-bsnes/program/program.cpp +++ b/higan/target-bsnes/program/program.cpp @@ -57,6 +57,7 @@ Program::Program(string_vector arguments) { new InputManager; new SettingsWindow; new CheatDatabase; + new CheatWindow; new ToolsWindow; new AboutWindow; diff --git a/higan/target-bsnes/tools/cheat-database.cpp b/higan/target-bsnes/tools/cheat-database.cpp deleted file mode 100644 index 7cd745fd..00000000 --- a/higan/target-bsnes/tools/cheat-database.cpp +++ /dev/null @@ -1,57 +0,0 @@ -CheatDatabase::CheatDatabase() { - cheatDatabase = this; - - layout.setMargin(5); - selectAllButton.setText("Select All").onActivate([&] { - for(auto item : cheatList.items()) item.setChecked(true); - }); - unselectAllButton.setText("Unselect All").onActivate([&] { - for(auto item : cheatList.items()) item.setChecked(false); - }); - addCheatsButton.setText("Add Cheats").onActivate([&] { - addCheats(); - }); - - setSize({800, 400}); - setAlignment({0.5, 1.0}); - setDismissable(); -} - -auto CheatDatabase::findCheats() -> void { - auto sha256 = emulator->sha256(); - - auto document = BML::unserialize(string::read(locate("cheats.bml"))); - for(auto game : document.find("cartridge")) { - if(game["sha256"].text() != sha256) continue; - - cheatList.reset(); - for(auto cheat : game.find("cheat")) { - cheatList.append(ListViewItem() - .setCheckable() - .setText(cheat["description"].text()) - .setProperty("code", cheat["code"].text()) - ); - } - - setTitle(game["name"].text()); - setVisible(); - return; - } - - MessageDialog().setParent(*toolsWindow).setText("Sorry, no cheats were found for this game.").information(); -} - -auto CheatDatabase::addCheats() -> void { - for(auto item : cheatList.items()) { - if(!item.checked()) continue; - - string code = item.property("code").replace("/", "=", 1L).replace("/", "?", 1L); - string description = item.text(); - if(!toolsWindow->cheatEditor.addCode(false, code, description)) { - MessageDialog().setParent(*this).setText("Free slots exhausted. Not all cheats could be added.").warning(); - break; - } - } - setVisible(false); - toolsWindow->cheatEditor.doRefresh(); -} diff --git a/higan/target-bsnes/tools/cheat-editor.cpp b/higan/target-bsnes/tools/cheat-editor.cpp index d35ca085..f02dc0d1 100644 --- a/higan/target-bsnes/tools/cheat-editor.cpp +++ b/higan/target-bsnes/tools/cheat-editor.cpp @@ -1,139 +1,217 @@ +CheatDatabase::CheatDatabase() { + cheatDatabase = this; + + layout.setMargin(5); + selectAllButton.setText("Select All").onActivate([&] { + for(auto item : cheatList.items()) item.setChecked(true); + }); + unselectAllButton.setText("Unselect All").onActivate([&] { + for(auto item : cheatList.items()) item.setChecked(false); + }); + addCheatsButton.setText("Add Cheats").onActivate([&] { + addCheats(); + }); + + setSize({800, 400}); + setAlignment({0.5, 1.0}); + setDismissable(); +} + +auto CheatDatabase::findCheats() -> void { + auto sha256 = emulator->sha256(); + + auto document = BML::unserialize(string::read(locate("cheats.bml"))); + for(auto game : document.find("cartridge")) { + if(game["sha256"].text() != sha256) continue; + + cheatList.reset(); + for(auto cheat : game.find("cheat")) { + cheatList.append(ListViewItem() + .setCheckable() + .setText(cheat["description"].text()) + .setProperty("code", cheat["code"].text()) + ); + } + + setTitle(game["name"].text()); + setVisible(); + return; + } + + MessageDialog().setParent(*toolsWindow).setText("Sorry, no cheats were found for this game.").information(); +} + +auto CheatDatabase::addCheats() -> void { + for(auto item : cheatList.items()) { + if(item.checked()) { + toolsWindow->cheatEditor.addCheat(item.text(), item.property("code")); + } + } + setVisible(false); + toolsWindow->cheatEditor.synchronizeCodes(); +} + +// + +CheatWindow::CheatWindow() { + cheatWindow = this; + + layout.setMargin(5); + nameLabel.setText("Name:"); + nameValue.onChange([&] { doChange(); }); + codeLabel.setText("Code:"); + codeValue.onChange([&] { doChange(); }); + enabledOption.setText("Enabled"); + acceptButton.onActivate([&] { doAccept(); }); + cancelButton.setText("Cancel"); + + setSize({480, layout.minimumSize().height()}); + setDismissable(); +} + +auto CheatWindow::show(TableViewItem item) -> void { + this->item = item; + nameValue.setText(item.cell(1).text()); + codeValue.setText(item.property("code")); + enabledOption.setChecked(item.cell(0).checked()); + doChange(); + setTitle(!item ? "Add Cheat Code" : "Edit Cheat Code"); + setCentered(*toolsWindow); + setVisible(); + setFocused(); + nameValue.setFocused(); + acceptButton.setText(!item ? "Add" : "Edit"); +} + +auto CheatWindow::doChange() -> void { + bool valid = true; + nameValue.setBackgroundColor(nameValue.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); + codeValue.setBackgroundColor(codeValue.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); + acceptButton.setEnabled(valid); +} + +auto CheatWindow::doAccept() -> void { + if(item) { + item.cell(0).setChecked(enabledOption.checked()); + item.cell(1).setText(nameValue.text()); + item.setProperty("code", codeValue.text()); + } else { + toolsWindow->cheatEditor.addCheat(nameValue.text(), codeValue.text(), enabledOption.checked()); + } + setVisible(false); +} + +// + CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) { setIcon(Icon::Edit::Replace); setText("Cheat Editor"); layout.setMargin(5); - cheatList.append(TableViewHeader().setVisible() - .append(TableViewColumn()) - .append(TableViewColumn().setText("Slot").setForegroundColor({0, 128, 0}).setAlignment(1.0)) - .append(TableViewColumn().setText("Code(s)")) - .append(TableViewColumn().setText("Description").setExpandable()) - ); - for(uint slot : range(Slots)) { - cheatList.append(TableViewItem() - .append(TableViewCell().setCheckable()) - .append(TableViewCell().setText(1 + slot)) - .append(TableViewCell()) - .append(TableViewCell()) - ); - } - cheatList.onChange([&] { doChangeSelected(); }); - cheatList.onToggle([&](auto cell) { - cheats[cell.parent().offset()].enabled = cell.checked(); - this->synchronizeCodes(); + cheatList.onActivate([&] { modifyButton.doActivate(); }); + cheatList.onChange([&] { + auto selected = cheatList.selected(); + upButton.setEnabled((bool)selected && selected.offset() != 0); + downButton.setEnabled((bool)selected && selected.offset() != cheatList.itemCount() - 1); + modifyButton.setEnabled((bool)selected); + removeButton.setEnabled((bool)selected); }); - codeLabel.setText("Code(s):"); - codeValue.setEnabled(false).onChange([&] { doModify(); }); - descriptionLabel.setText("Description:"); - descriptionValue.setEnabled(false).onChange([&] { doModify(); }); - findCodesButton.setText("Find Codes ...").onActivate([&] { cheatDatabase->findCheats(); }); - resetButton.setText("Reset").onActivate([&] { doReset(); }); - eraseButton.setText("Erase").onActivate([&] { doErase(); }); - - //do not display "Find Codes" button if there is no cheat database to look up codes in - if(!file::exists(locate("cheats.bml"))) findCodesButton.setVisible(false); -} - -auto CheatEditor::doChangeSelected() -> void { - if(auto item = cheatList.selected()) { - auto& cheat = cheats[item.offset()]; - codeValue.setEnabled(true).setText(cheat.code); - descriptionValue.setEnabled(true).setText(cheat.description); - eraseButton.setEnabled(true); - } else { - codeValue.setEnabled(false).setText(""); - descriptionValue.setEnabled(false).setText(""); - eraseButton.setEnabled(false); - } -} - -auto CheatEditor::doModify() -> void { - if(auto item = cheatList.selected()) { - auto& cheat = cheats[item.offset()]; - cheat.code = codeValue.text(); - cheat.description = descriptionValue.text(); - doRefresh(); - synchronizeCodes(); - } -} - -auto CheatEditor::doRefresh() -> void { - for(uint slot : range(Slots)) { - auto& cheat = cheats[slot]; - if(cheat.code || cheat.description) { - auto codes = cheat.code.split("+"); - if(codes.size() > 1) codes[0].append("+..."); - cheatList.item(slot).cell(0).setChecked(cheat.enabled); - cheatList.item(slot).cell(2).setText(codes[0]); - cheatList.item(slot).cell(3).setText(cheat.description).setForegroundColor({0, 0, 0}); - } else { - cheatList.item(slot).cell(0).setChecked(false); - cheatList.item(slot).cell(2).setText(""); - cheatList.item(slot).cell(3).setText("").setForegroundColor({128, 128, 128}); + cheatList.onToggle([&](TableViewCell) { synchronizeCodes(); }); + upButton.setIcon(Icon::Go::Up).onActivate([&] { + if(auto item = cheatList.selected()) { + swap(item.offset(), item.offset() - 1); } - } - cheatList.resizeColumns(); + }); + downButton.setIcon(Icon::Go::Down).onActivate([&] { + if(auto item = cheatList.selected()) { + swap(item.offset(), item.offset() + 1); + } + }); + findCheatsButton.setText("Find Cheats ...").onActivate([&] { + cheatDatabase->findCheats(); + }); + resetButton.setText("Reset").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(); + }); + modifyButton.setText("Edit").onActivate([&] { + if(auto item = cheatList.selected()) { + cheatWindow->show(item); + } + }); + removeButton.setText("Remove").onActivate([&] { + if(auto item = cheatList.selected()) { + cheatList.remove(item).doChange(); + } + }); + + //do not display "Find Cheats" button if there is no cheat database available + if(!file::exists(locate("cheats.bml"))) findCheatsButton.setVisible(false); } -auto CheatEditor::doReset(bool force) -> void { - if(force || MessageDialog().setParent(*toolsWindow).setText("Permamently erase all cheats?").question() == "Yes") { - for(auto& cheat : cheats) cheat = {}; - for(auto& item : cheatList.items()) item.cell(0).setChecked(false); - doChangeSelected(); - doRefresh(); - synchronizeCodes(); - } -} - -auto CheatEditor::doErase() -> void { - if(auto item = cheatList.selected()) { - cheats[item.offset()] = {}; - codeValue.setText(""); - descriptionValue.setText(""); - doRefresh(); - synchronizeCodes(); - } +auto CheatEditor::swap(uint x, uint y) -> void { + auto itemX = cheatList.item(x); + auto itemY = cheatList.item(y); + 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& cheat : cheats) { - if(!cheat.enabled || !cheat.code) continue; - codes.append(cheat.code); + for(auto item : cheatList.items()) { + if(item.cell(0).checked()) codes.append(item.property("code")); } emulator->cheatSet(codes); } -auto CheatEditor::addCode(bool enabled, string code, string description) -> bool { - for(auto& cheat : cheats) { - if(cheat.code || cheat.description) continue; - cheat.enabled = enabled; - cheat.code = code; - cheat.description = description; - return true; - } - return false; +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 { - doReset(true); + cheatList.reset().append(TableViewHeader() + .append(TableViewColumn()) + .append(TableViewColumn().setExpandable()) + .setVisible(false) + ); auto location = program->path("Cheats", program->superNintendo.location, ".cht"); auto document = BML::unserialize(string::read(location)); for(auto cheat : document.find("cheat")) { - if(!addCode((bool)cheat["enabled"], cheat["code"].text(), cheat["description"].text())) break; + addCheat(cheat["name"].text(), cheat["code"].text(), (bool)cheat["enabled"]); } - doRefresh(); + cheatList.doChange(); synchronizeCodes(); } auto CheatEditor::saveCheats() -> void { string document; - for(auto& cheat : cheats) { - if(!cheat.code && !cheat.description) continue; + for(auto item : cheatList.items()) { document.append("cheat\n"); - document.append(" description: ", cheat.description, "\n"); - document.append(" code: ", cheat.code, "\n"); - if(cheat.enabled) + document.append(" name: ", item.cell(1).text(), "\n"); + document.append(" code: ", item.property("code"), "\n"); + if(item.cell(0).checked()) document.append(" enabled\n"); document.append("\n"); } diff --git a/higan/target-bsnes/tools/tools.cpp b/higan/target-bsnes/tools/tools.cpp index 70fc7ab1..5f6c8051 100644 --- a/higan/target-bsnes/tools/tools.cpp +++ b/higan/target-bsnes/tools/tools.cpp @@ -1,7 +1,7 @@ #include "../bsnes.hpp" -#include "cheat-database.cpp" #include "cheat-editor.cpp" unique_pointer cheatDatabase; +unique_pointer cheatWindow; unique_pointer toolsWindow; ToolsWindow::ToolsWindow() { @@ -17,10 +17,19 @@ ToolsWindow::ToolsWindow() { onSize([&] { cheatEditor.cheatList.resizeColumns(); }); + + onClose([&] { + setVisible(false); + }); } auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& { - return Window::setVisible(visible), *this; + Window::setVisible(visible); + if(!visible) { + cheatDatabase->setVisible(false); + cheatWindow->setVisible(false); + } + return *this; } auto ToolsWindow::show(uint index) -> void { diff --git a/higan/target-bsnes/tools/tools.hpp b/higan/target-bsnes/tools/tools.hpp index f252b71d..3988be5a 100644 --- a/higan/target-bsnes/tools/tools.hpp +++ b/higan/target-bsnes/tools/tools.hpp @@ -13,6 +13,52 @@ public: Button addCheatsButton{&controlLayout, Size{100, 0}}; }; +struct CheatWindow : Window { + CheatWindow(); + auto show(TableViewItem item = {}) -> void; + auto doChange() -> void; + auto doAccept() -> void; + +public: + TableViewItem item; + + VerticalLayout layout{this}; + HorizontalLayout nameLayout{&layout, Size{~0, 0}}; + Label nameLabel{&nameLayout, Size{40, 0}}; + LineEdit nameValue{&nameLayout, Size{~0, 0}}; + HorizontalLayout codeLayout{&layout, Size{~0, 0}}; + Label codeLabel{&codeLayout, Size{40, 0}}; + LineEdit codeValue{&codeLayout, Size{~0, 0}}; + HorizontalLayout controlLayout{&layout, Size{~0, 0}}; + Widget spacer{&controlLayout, Size{40, 0}}; + CheckLabel enabledOption{&controlLayout, Size{~0, 0}}; + Button acceptButton{&controlLayout, Size{80, 0}}; + Button cancelButton{&controlLayout, Size{80, 0}}; +}; + +struct CheatEditor : TabFrameItem { + CheatEditor(TabFrame*); + auto swap(uint x, uint y) -> void; + auto synchronizeCodes() -> void; + auto addCheat(string name, string code, bool enabled = false) -> void; + auto loadCheats() -> void; + auto saveCheats() -> void; + +public: + VerticalLayout layout{this}; + TableView cheatList{&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}}; + Widget spacer{&controlLayout, Size{~0, 0}}; + Button resetButton{&controlLayout, Size{80, 0}}; + Button appendButton{&controlLayout, Size{80, 0}}; + Button modifyButton{&controlLayout, Size{80, 0}}; + Button removeButton{&controlLayout, Size{80, 0}}; +}; + +/* struct CheatEditor : TabFrameItem { enum : uint { Slots = 128 }; @@ -49,6 +95,7 @@ public: Button resetButton{&controlLayout, Size{80, 0}}; Button eraseButton{&controlLayout, Size{80, 0}}; }; +*/ struct ToolsWindow : Window { ToolsWindow(); @@ -62,4 +109,5 @@ public: }; extern unique_pointer cheatDatabase; +extern unique_pointer cheatWindow; extern unique_pointer toolsWindow; diff --git a/hiro/extension/list-view.cpp b/hiro/extension/list-view.cpp index 07d51152..3db2a86c 100644 --- a/hiro/extension/list-view.cpp +++ b/hiro/extension/list-view.cpp @@ -1,6 +1,12 @@ #if defined(Hiro_ListView) mListView::mListView() { + mTableView::onActivate([&] { + doActivate(); + }); + mTableView::onChange([&] { + doChange(); + }); mTableView::onToggle([&](TableViewCell cell) { if(auto item = cell->parentTableViewItem()) { if(auto shared = item->instance.acquire()) { @@ -18,6 +24,14 @@ auto mListView::batched() const -> vector { return result; } +auto mListView::doActivate() const -> void { + if(state.onActivate) state.onActivate(); +} + +auto mListView::doChange() const -> void { + if(state.onChange) state.onChange(); +} + auto mListView::doToggle(ListViewItem item) const -> void { if(state.onToggle) state.onToggle(item); } @@ -33,6 +47,16 @@ auto mListView::items() const -> vector { return result; } +auto mListView::onActivate(const function& callback) -> type& { + state.onActivate = callback; + return *this; +} + +auto mListView::onChange(const function& callback) -> type& { + state.onChange = callback; + return *this; +} + auto mListView::onToggle(const function& callback) -> type& { state.onToggle = callback; return *this; diff --git a/hiro/extension/list-view.hpp b/hiro/extension/list-view.hpp index 3650983d..0a18fa4d 100644 --- a/hiro/extension/list-view.hpp +++ b/hiro/extension/list-view.hpp @@ -11,15 +11,21 @@ struct mListView : mTableView { mListView(); auto batched() const -> vector; + auto doActivate() const -> void; + auto doChange() const -> void; auto doToggle(ListViewItem) const -> void; auto item(uint position) const -> ListViewItem; auto items() const -> vector; + auto onActivate(const function& callback) -> type&; + auto onChange(const function& callback) -> type&; auto onToggle(const function& callback) -> type&; auto reset() -> type& override; auto selected() const -> ListViewItem; //private: struct State { + function onActivate; + function onChange; function onToggle; } state; }; diff --git a/nall/adaptive-array.hpp b/nall/adaptive-array.hpp new file mode 100644 index 00000000..7f724c9e --- /dev/null +++ b/nall/adaptive-array.hpp @@ -0,0 +1,63 @@ +//deprecated + +#pragma once + +#include + +namespace nall { + +template +struct adaptive_array { + auto capacity() const -> uint { return Capacity; } + auto size() const -> uint { return _size; } + + auto reset() -> void { + for(uint n : range(_size)) _pool.t[n].~T(); + _size = 0; + } + + auto operator[](uint index) -> T& { + #ifdef DEBUG + struct out_of_bounds {}; + if(index >= Capacity) throw out_of_bounds{}; + #endif + return _pool.t[index]; + } + + auto operator[](uint index) const -> const T& { + #ifdef DEBUG + struct out_of_bounds {}; + if(index >= Capacity) throw out_of_bounds{}; + #endif + return _pool.t[index]; + } + + auto append() -> T& { + new(_pool.t + _size) T; + return _pool.t[_size++]; + } + + auto append(const T& value) -> void { + new(_pool.t + _size++) T(value); + } + + auto append(T&& value) -> void { + new(_pool.t + _size++) T(move(value)); + } + + auto begin() { return &_pool.t[0]; } + auto end() { return &_pool.t[_size]; } + + auto begin() const { return &_pool.t[0]; } + auto end() const { return &_pool.t[_size]; } + +private: + union U { + U() {} + ~U() {} + T t[Capacity]; + } _pool; + uint _size = 0; +}; + +} diff --git a/nall/array.hpp b/nall/array.hpp index baf7af39..504f63f3 100644 --- a/nall/array.hpp +++ b/nall/array.hpp @@ -1,61 +1,130 @@ #pragma once +#define DEBUG #include namespace nall { -template -struct array { - auto capacity() const -> uint { return Capacity; } - auto size() const -> uint { return _size; } +template struct array; - auto reset() -> void { - for(uint n : range(_size)) _pool.t[n].~T(); - _size = 0; +//usage: int x[256] => array +template struct array { + array() = default; + + array(const initializer_list& source) { + uint index = 0; + for(auto& value : source) { + operator[](index++) = value; + } } - auto operator[](uint index) -> T& { + alwaysinline auto operator[](uint index) -> T& { #ifdef DEBUG struct out_of_bounds {}; - if(index >= Capacity) throw out_of_bounds{}; + if(index >= Size) throw out_of_bounds{}; #endif - return _pool.t[index]; + return values[index]; } - auto operator[](uint index) const -> const T& { + alwaysinline auto operator[](uint index) const -> const T& { #ifdef DEBUG struct out_of_bounds {}; - if(index >= Capacity) throw out_of_bounds{}; + if(index >= Size) throw out_of_bounds{}; #endif - return _pool.t[index]; + return values[index]; } - auto append() -> T& { - new(_pool.t + _size) T; - return _pool.t[_size++]; + alwaysinline auto operator()(uint index, const T& fallback = {}) const -> const T& { + if(index >= Size) return fallback; + return values[index]; } - auto append(const T& value) -> void { - new(_pool.t + _size++) T(value); + auto fill(const T& fill = {}) { + for(auto& value : values) value = fill; } - auto append(T&& value) -> void { - new(_pool.t + _size++) T(move(value)); - } + auto data() -> T* { return values; } + auto data() const -> const T* { return values; } + auto size() const -> uint { return Size; } - auto begin() { return &_pool.t[0]; } - auto end() { return &_pool.t[_size]; } + auto begin() -> T* { return &values[0]; } + auto end() -> T* { return &values[Size]; } - auto begin() const { return &_pool.t[0]; } - auto end() const { return &_pool.t[_size]; } + auto begin() const -> const T* { return &values[0]; } + auto end() const -> const T* { return &values[Size]; } private: - union U { - U() {} - ~U() {} - T t[Capacity]; - } _pool; - uint _size = 0; + T values[Size]; +}; + +template struct array_view; + +template struct array_view { + array_view() = default; + + template + array_view(array& source, uint offset = 0) { + #ifdef DEBUG + struct out_of_bounds {}; + if(offset + Size >= Capacity) throw out_of_bounds{}; + #endif + values = &source.data()[offset]; + } + + template + array_view(T (&source)[Capacity], uint offset = 0) { + #ifdef DEBUG + struct out_of_bounds {}; + if(offset + Size >= Capacity) throw out_of_bounds{}; + #endif + values = &source[offset]; + } + + array_view(T* source, uint offset = 0) { + values = &source[offset]; + } + + explicit operator bool() const { + return values; + } + + alwaysinline auto operator[](uint index) -> T& { + #ifdef DEBUG + struct out_of_bounds {}; + if(index >= Size) throw out_of_bounds{}; + #endif + return values[index]; + } + + alwaysinline auto operator[](uint index) const -> const T& { + #ifdef DEBUG + struct out_of_bounds {}; + if(index >= Size) throw out_of_bounds{}; + #endif + return values[index]; + } + + alwaysinline auto operator()(uint index, const T& fallback = {}) const -> const T& { + if(index >= Size) return fallback; + return values[index]; + } + + auto fill(const T& fill = {}) -> void { + for(uint index : range(Size)) values[index] = fill; + } + + auto data() -> T* { return values; } + auto data() const -> const T* { return values; } + auto size() const -> uint { return Size; } + + auto begin() -> T* { return &values[0]; } + auto end() -> T* { return &values[Size]; } + + auto begin() const -> const T* { return &values[0]; } + auto end() const -> const T* { return &values[Size]; } + +private: + T* values = nullptr; }; } diff --git a/nall/nall.hpp b/nall/nall.hpp index 900863a8..b25c52f4 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/nall/pointer.hpp b/nall/pointer.hpp new file mode 100644 index 00000000..0e96ddb0 --- /dev/null +++ b/nall/pointer.hpp @@ -0,0 +1,34 @@ +#pragma once + +namespace nall { + +template +struct pointer { + explicit operator bool() const { return value; } + + pointer() = default; + pointer(T* source) { value = source; } + pointer(const pointer& source) { value = source.value; } + + auto& operator=(T* source) { value = source; return *this; } + auto& operator=(const pointer& source) { value = source.value; return *this; } + + auto operator()() -> T* { return value; } + auto operator()() const -> const T* { return value; } + + auto operator->() -> T* { return value; } + auto operator->() const -> const T* { return value; } + + auto operator*() -> T& { return *value; } + auto operator*() const -> const T& { return *value; } + + auto reset() -> void { value = nullptr; } + + auto data() -> T* { return value; } + auto data() const -> const T* { return value; } + +private: + T* value = nullptr; +}; + +} diff --git a/nall/serializer.hpp b/nall/serializer.hpp index 0f9c4547..7f8f2239 100644 --- a/nall/serializer.hpp +++ b/nall/serializer.hpp @@ -11,6 +11,7 @@ //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); //- floating-point usage is not portable across different implementations +#include #include #include #include @@ -96,6 +97,10 @@ struct serializer { return *this; } + template auto array(nall::array& array) -> serializer& { + for(auto& value : array) operator()(value); + } + template auto operator()(T& value, typename std::enable_if::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; } template auto operator()(T& value, typename std::enable_if::value>::type* = 0) -> serializer& { return integer(value); } template auto operator()(T& value, typename std::enable_if::value>::type* = 0) -> serializer& { return floatingpoint(value); } diff --git a/nall/string.hpp b/nall/string.hpp index 6c15e7c4..acb3b14d 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -66,8 +66,6 @@ template inline auto pad(const T& value, long precision = 0, char pa inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string; -inline auto pointer(uintptr value, long precision = 0) -> string; -template inline auto pointer(const T* value, long precision = 0) -> string; //match.hpp inline auto tokenize(const char* s, const char* p) -> bool; diff --git a/nall/string/cast.hpp b/nall/string/cast.hpp index 9a1a3926..dc3e0826 100644 --- a/nall/string/cast.hpp +++ b/nall/string/cast.hpp @@ -234,6 +234,22 @@ template<> struct stringify { const string_view& _view; }; +//pointers + +template struct stringify { + stringify(const T* source) { + if(!source) { + memory::copy(_data, "(nullptr)", 10); + } else { + memory::copy(_data, "0x", 2); + fromNatural(_data + 2, (uintptr)source); + } + } + auto data() const -> const char* { return _data; } + auto size() const -> uint { return strlen(_data); } + char _data[256]; +}; + // template auto make_string(T value) -> stringify { diff --git a/nall/string/format.hpp b/nall/string/format.hpp index 2fbff26b..c5e6fe1c 100644 --- a/nall/string/format.hpp +++ b/nall/string/format.hpp @@ -133,14 +133,4 @@ auto binary(uintmax value, long precision, char padchar) -> string { return buffer; } -auto pointer(uintptr value, long precision) -> string { - if(value == 0) return "(nullptr)"; - return {"0x", hex(value, precision)}; -} - -template auto pointer(const T* value, long precision) -> string { - if(value == nullptr) return "(nullptr)"; - return {"0x", hex((uintptr)value, precision)}; -} - }