From c335ee9d8060d7ae7cee59df3789636ea14aa3b5 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 21 Apr 2015 21:58:59 +1000 Subject: [PATCH] Update to v094r16 release. byuu says: Finished the cheat code system, it'll now load and save cheats.bml to disk. Also hooked up overscan masking. But for now you can only configure the amount it clips via the configuration file, since I don't have a video settings dialog anymore. And that's the last of the low-hanging fruit. The remaining items are all going to be a pain in the ass for one reason or another. Short-term: - add input port changing support - add other input types (mouse-based, etc) Long-term: - add slotted cart loader (SGB, BSX, ST) - add DIP switch selection window (NSS) - add timing configuration (video/audio sync) Not planned: - video color adjustments (will allow emulated color vs raw color; but no more sliders) - pixel shaders - ananke integration (will need to make a command-line version to get my games in) - fancy audio adjustment controls (resampler, latency, volume) - input focus settings - localization support (not enough users) - window geometry memory - anything else not in higan v094 --- emulator/emulator.hpp | 2 +- target-tomoko/configuration/configuration.cpp | 4 + target-tomoko/configuration/configuration.hpp | 6 ++ target-tomoko/presentation/presentation.cpp | 11 +-- target-tomoko/presentation/presentation.hpp | 2 +- target-tomoko/program/interface.cpp | 15 ++++ target-tomoko/program/media.cpp | 4 +- target-tomoko/tools/cheat-database.cpp | 1 + target-tomoko/tools/cheat-editor.cpp | 89 +++++++++++++------ target-tomoko/tools/tools.hpp | 9 +- 10 files changed, 105 insertions(+), 38 deletions(-) diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 7111bdfe..84f71df4 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "094.15"; + static const char Version[] = "094.16"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/target-tomoko/configuration/configuration.cpp b/target-tomoko/configuration/configuration.cpp index b5487c49..f2b7d868 100644 --- a/target-tomoko/configuration/configuration.cpp +++ b/target-tomoko/configuration/configuration.cpp @@ -17,6 +17,10 @@ ConfigurationManager::ConfigurationManager() { video.append(video.aspectCorrection, "AspectCorrection"); video.append(video.filter, "Filter"); video.append(video.colorEmulation, "ColorEmulation"); + video.overscan.append(video.overscan.mask, "Mask"); + video.overscan.append(video.overscan.horizontal, "Horizontal"); + video.overscan.append(video.overscan.vertical, "Vertical"); + video.append(video.overscan, "Overscan"); append(video, "Video"); audio.append(audio.driver, "Driver"); diff --git a/target-tomoko/configuration/configuration.hpp b/target-tomoko/configuration/configuration.hpp index 5971ef62..f02822d2 100644 --- a/target-tomoko/configuration/configuration.hpp +++ b/target-tomoko/configuration/configuration.hpp @@ -17,6 +17,12 @@ struct ConfigurationManager : Configuration::Document { bool aspectCorrection = true; string filter = "Blur"; bool colorEmulation = true; + + struct Overscan : Configuration::Node { + bool mask = false; + unsigned horizontal = 8; + unsigned vertical = 8; + } overscan; } video; struct Audio : Configuration::Node { diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index 063b4456..a004f3a5 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -39,6 +39,10 @@ Presentation::Presentation() { config().video.scale = "Large"; resizeViewport(); }); + aspectCorrection.setText("Aspect Correction").setChecked(config().video.aspectCorrection).onToggle([&] { + config().video.aspectCorrection = aspectCorrection.checked(); + resizeViewport(); + }); videoFilterMenu.setText("Video Filter"); MenuRadioItem::group({videoFilterNone, videoFilterBlur}); if(config().video.filter == "None") videoFilterNone.setChecked(); @@ -49,11 +53,8 @@ Presentation::Presentation() { config().video.colorEmulation = colorEmulation.checked(); program->updateVideoPalette(); }); - aspectCorrection.setText("Aspect Correction").setChecked(config().video.aspectCorrection).onToggle([&] { - config().video.aspectCorrection = aspectCorrection.checked(); - resizeViewport(); - }); - maskOverscan.setText("Mask Overscan").onToggle([&] { + maskOverscan.setText("Mask Overscan").setChecked(config().video.overscan.mask).onToggle([&] { + config().video.overscan.mask = maskOverscan.checked(); }); synchronizeVideo.setText("Synchronize Video").setChecked(config().video.synchronize).onToggle([&] { config().video.synchronize = synchronizeVideo.checked(); diff --git a/target-tomoko/presentation/presentation.hpp b/target-tomoko/presentation/presentation.hpp index 443437d8..2c94ef71 100644 --- a/target-tomoko/presentation/presentation.hpp +++ b/target-tomoko/presentation/presentation.hpp @@ -19,12 +19,12 @@ struct Presentation : Window { MenuRadioItem videoScaleLarge{&videoScaleMenu}; MenuSeparator videoScaleSeparator{&videoScaleMenu}; MenuCheckItem aspectCorrection{&videoScaleMenu}; - MenuCheckItem maskOverscan{&videoScaleMenu}; Menu videoFilterMenu{&settingsMenu}; MenuRadioItem videoFilterNone{&videoFilterMenu}; MenuRadioItem videoFilterBlur{&videoFilterMenu}; MenuSeparator videoFilterSeparator{&videoFilterMenu}; MenuCheckItem colorEmulation{&videoFilterMenu}; + MenuCheckItem maskOverscan{&videoFilterMenu}; MenuSeparator settingsMenuSeparator1{&settingsMenu}; MenuCheckItem synchronizeVideo{&settingsMenu}; MenuCheckItem synchronizeAudio{&settingsMenu}; diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp index 19e6da63..271e3e36 100644 --- a/target-tomoko/program/interface.cpp +++ b/target-tomoko/program/interface.cpp @@ -40,6 +40,21 @@ auto Program::videoRefresh(const uint32* palette, const uint32* data, unsigned p } } + if(emulator->information.overscan && config().video.overscan.mask) { + unsigned h = config().video.overscan.horizontal; + unsigned v = config().video.overscan.vertical; + + if(h) for(auto y : range(height)) { + memory::fill(output + y * length, 4 * h); + memory::fill(output + y * length + (width - h), 4 * h); + } + + if(v) for(auto y : range(v)) { + memory::fill(output + y * length, 4 * width); + memory::fill(output + (height - 1 - y) * length, 4 * width); + } + } + video.unlock(); video.refresh(); } diff --git a/target-tomoko/program/media.cpp b/target-tomoko/program/media.cpp index b5b48d84..2dd037c1 100644 --- a/target-tomoko/program/media.cpp +++ b/target-tomoko/program/media.cpp @@ -29,13 +29,14 @@ auto Program::loadMedia(Emulator::Interface& _emulator, Emulator::Interface::Med presentation->setTitle(emulator->title()); presentation->systemMenu.setVisible(true); presentation->toolsMenu.setVisible(true); - toolsManager->cheatEditor.doRefresh(); + toolsManager->cheatEditor.loadCheats(); toolsManager->stateManager.doRefresh(); } auto Program::unloadMedia() -> void { if(!emulator) return; + toolsManager->cheatEditor.saveCheats(); emulator->unload(); emulator = nullptr; @@ -45,4 +46,5 @@ auto Program::unloadMedia() -> void { presentation->setTitle({"tomoko v", Emulator::Version}); presentation->systemMenu.setVisible(false); presentation->toolsMenu.setVisible(false); + toolsManager->setVisible(false); } diff --git a/target-tomoko/tools/cheat-database.cpp b/target-tomoko/tools/cheat-database.cpp index 398cb29a..82d6cbac 100644 --- a/target-tomoko/tools/cheat-database.cpp +++ b/target-tomoko/tools/cheat-database.cpp @@ -49,4 +49,5 @@ auto CheatDatabase::addCodes() -> void { } } setVisible(false); + toolsManager->cheatEditor.doRefresh(); } diff --git a/target-tomoko/tools/cheat-editor.cpp b/target-tomoko/tools/cheat-editor.cpp index c82be8cc..299d6f57 100644 --- a/target-tomoko/tools/cheat-editor.cpp +++ b/target-tomoko/tools/cheat-editor.cpp @@ -4,13 +4,16 @@ CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) { layout.setMargin(5); cheatList.append(ListViewColumn().setText("Slot").setForegroundColor({0, 128, 0}).setHorizontalAlignment(1.0)); - cheatList.append(ListViewColumn().setText("Code(s)").setWidth(0)); + cheatList.append(ListViewColumn().setText("Code(s)")); cheatList.append(ListViewColumn().setText("Description").setWidth(~0)); for(auto slot : range(Slots)) cheatList.append(ListViewItem().setText(0, 1 + slot)); cheatList.setCheckable(); cheatList.setHeaderVisible(); - cheatList.onChange([&] { doChange(); }); - cheatList.onToggle([&](sListViewItem) { synchronizeCodes(); }); + cheatList.onChange([&] { doChangeSelected(); }); + cheatList.onToggle([&](sListViewItem item) { + cheats[item->offset()].enabled = item->checked(); + synchronizeCodes(); + }); codeLabel.setText("Code(s):"); codeValue.onChange([&] { doModify(); }); descriptionLabel.setText("Description:"); @@ -20,11 +23,11 @@ CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) { eraseButton.setText("Erase").onActivate([&] { doErase(); }); } -auto CheatEditor::doChange() -> void { +auto CheatEditor::doChangeSelected() -> void { if(auto item = cheatList.selected()) { - unsigned slot = item->offset(); - codeValue.setEnabled(true).setText(cheats[slot].code); - descriptionValue.setEnabled(true).setText(cheats[slot].description); + 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(""); @@ -35,9 +38,9 @@ auto CheatEditor::doChange() -> void { auto CheatEditor::doModify() -> void { if(auto item = cheatList.selected()) { - unsigned slot = item->offset(); - cheats[slot].code = codeValue.text(); - cheats[slot].description = descriptionValue.text(); + auto& cheat = cheats[item->offset()]; + cheat.code = codeValue.text(); + cheat.description = descriptionValue.text(); doRefresh(); synchronizeCodes(); } @@ -45,26 +48,28 @@ auto CheatEditor::doModify() -> void { auto CheatEditor::doRefresh() -> void { for(auto slot : range(Slots)) { - if(cheats[slot].code || cheats[slot].description) { - lstring codes = cheats[slot].code.split("+"); + auto& cheat = cheats[slot]; + if(cheat.code || cheat.description) { + lstring codes = cheat.code.split("+"); if(codes.size() > 1) codes[0].append("+..."); - cheatList.item(slot)->setText(1, codes[0]).setText(2, cheats[slot].description); + cheatList.item(slot)->setChecked(cheat.enabled).setText(1, codes[0]).setText(2, cheat.description); } else { - cheatList.item(slot)->setText(1, "").setText(2, "(empty)"); + cheatList.item(slot)->setChecked(false).setText(1, "").setText(2, "(empty)"); } } cheatList.resizeColumns(); } -auto CheatEditor::doReset() -> void { - if(MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == 0) { +auto CheatEditor::doReset(bool force) -> void { + if(force || MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == 0) { for(auto& cheat : cheats) { + cheat.enabled = false; cheat.code = ""; cheat.description = ""; } cheatList.setSelected(false); - doChange(); + doChangeSelected(); doRefresh(); synchronizeCodes(); } @@ -72,9 +77,10 @@ auto CheatEditor::doReset() -> void { auto CheatEditor::doErase() -> void { if(auto item = cheatList.selected()) { - unsigned slot = item->offset(); - cheats[slot].code = ""; - cheats[slot].description = ""; + auto& cheat = cheats[item->offset()]; + cheat.enabled = false; + cheat.code = ""; + cheat.description = ""; codeValue.setText(""); descriptionValue.setText(""); doRefresh(); @@ -86,10 +92,9 @@ auto CheatEditor::synchronizeCodes() -> void { if(!emulator) return; lstring codes; - for(auto slot : range(Slots)) { - if(!cheatList.item(slot)->checked()) continue; - if(!cheats[slot].code) continue; - codes.append(cheats[slot].code); + for(auto& cheat : cheats) { + if(!cheat.enabled || !cheat.code) continue; + codes.append(cheat.code); } emulator->cheatSet(codes); @@ -97,15 +102,45 @@ auto CheatEditor::synchronizeCodes() -> void { //returns true if code was added //returns false if there are no more free slots for additional codes -auto CheatEditor::addCode(const string& code, const string& description) -> bool { +auto CheatEditor::addCode(const string& code, const string& description, bool enabled) -> bool { for(auto& cheat : cheats) { if(cheat.code || cheat.description) continue; - + cheat.enabled = enabled; cheat.code = code; cheat.description = description; - doRefresh(); return true; } return false; } + +auto CheatEditor::loadCheats() -> void { + doReset(true); + auto contents = string::read({program->folderPaths[0], "cheats.bml"}); + auto document = Markup::Document(contents); + for(auto& cheat : document["cartridge"]) { + if(cheat.name != "cheat") continue; + if(!addCode(cheat["code"].text(), cheat["description"].text(), cheat["enabled"].exists())) break; + } + doRefresh(); + synchronizeCodes(); +} + +auto CheatEditor::saveCheats() -> void { + if(!emulator) return; + string document = {"cartridge sha256:", emulator->sha256(), "\n"}; + unsigned count = 0; + for(auto& cheat : cheats) { + if(!cheat.code && !cheat.description) continue; + document.append(" cheat", cheat.enabled ? " enabled" : "", "\n"); + document.append(" description:", cheat.description, "\n"); + document.append(" code:", cheat.code, "\n"); + count++; + } + if(count) { + file::write({program->folderPaths[0], "cheats.bml"}, document); + } else { + file::remove({program->folderPaths[0], "cheats.bml"}); + } + doReset(true); +} diff --git a/target-tomoko/tools/tools.hpp b/target-tomoko/tools/tools.hpp index d4a1e6b8..048e949c 100644 --- a/target-tomoko/tools/tools.hpp +++ b/target-tomoko/tools/tools.hpp @@ -18,15 +18,18 @@ struct CheatEditor : TabFrameItem { enum : unsigned { Slots = 128 }; CheatEditor(TabFrame*); - auto doChange() -> void; + auto doChangeSelected() -> void; auto doModify() -> void; auto doRefresh() -> void; - auto doReset() -> void; + auto doReset(bool force = false) -> void; auto doErase() -> void; auto synchronizeCodes() -> void; - auto addCode(const string& code, const string& description) -> bool; + auto addCode(const string& code, const string& description, bool enabled = false) -> bool; + auto loadCheats() -> void; + auto saveCheats() -> void; struct Cheat { + bool enabled = false; string code; string description; };