bsnes/higan/target-tomoko/tools/cheat-editor.cpp

158 lines
4.9 KiB
C++
Raw Normal View History

CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Edit::Replace);
setText("Cheat Editor");
layout.setMargin(5);
cheatList.append(TableViewHeader().setVisible()
.append(TableViewColumn().setText("Slot").setForegroundColor({0, 128, 0}).setAlignment(1.0))
.append(TableViewColumn().setText("Code(s)"))
.append(TableViewColumn().setText("Description").setExpandable())
);
for(auto slot : range(Slots)) {
cheatList.append(TableViewItem()
.append(TableViewCell().setCheckable().setText(1 + slot))
.append(TableViewCell())
.append(TableViewCell())
);
}
cheatList.onChange([&] { doChangeSelected(); });
cheatList.onToggle([&](TableViewCell cell) {
cheats[cell.parent().offset()].enabled = cell.checked();
synchronizeCodes();
});
codeLabel.setText("Code(s):");
codeValue.onChange([&] { doModify(); });
descriptionLabel.setText("Description:");
descriptionValue.onChange([&] { doModify(); });
findCodesButton.setText("Find Codes ...").onActivate([&] { cheatDatabase->findCodes(); });
resetButton.setText("Reset").onActivate([&] { doReset(); });
eraseButton.setText("Erase").onActivate([&] { doErase(); });
}
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(auto 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(1).setText(codes[0]);
cheatList.item(slot).cell(2).setText(cheat.description).setForegroundColor({0, 0, 0});
} else {
cheatList.item(slot).cell(0).setChecked(false);
cheatList.item(slot).cell(1).setText("");
cheatList.item(slot).cell(2).setText("(empty)").setForegroundColor({128, 128, 128});
}
}
cheatList.resizeColumns();
}
auto CheatEditor::doReset(bool force) -> void {
Update to v094r23 release. byuu says: The library window is gone, and replaced with hiro::BrowserWindow::openFolder(). This gives navigation capabilities to game loading, and it also completes our slotted cart selection code. As an added bonus, it's less code this way, too. I also set the window size to consistent sizes between all emulated systems, so that switching between SFC and GB don't cause the window size to keep changing, and so that the scaling size is consistent (eg at normal scale, GB @ 3x is closer to SNES @ 2x.) This means black borders in GB/GBA mode, but it doesn't look that bad, and it's not like many people ever use these modes anyway. Finally, added the placeholder tabs for video, audio and timing. I don't intend to add the timing calculator code to v095 (it might be better as a separate tool), but I'll add the ability to set video/audio rates, at least. Glitch 1: despite selecting the first item in the BrowserDialog list, if you press enter when the window appears, it doesn't activate the item until you press an arrow key first. Glitch 2: in Game Boy mode, if you set the 4x window size, it's not honoring the full requested height because the viewport is smaller than the window. 8+ years of trying to get GTK+ and Qt to simply set the god damned window size I ask for, and I still can't get them to do it reliably. Remaining issues: - finish configuration panels (video, audio, timing) - fix ruby driver compilation on Windows - add DIP switch selection window (NSS) [I may end up punting this one to v096]
2015-05-30 11:39:09 +00:00
if(force || MessageDialog().setParent(*toolsManager).setText("Permanently erase all slots?").question() == "Yes") {
for(auto& cheat : cheats) {
cheat.enabled = false;
cheat.code = "";
cheat.description = "";
}
for(auto& item : cheatList.items()) {
item.cell(0).setChecked(false);
}
doChangeSelected();
doRefresh();
synchronizeCodes();
}
}
auto CheatEditor::doErase() -> void {
if(auto item = cheatList.selected()) {
auto& cheat = cheats[item.offset()];
cheat.enabled = false;
cheat.code = "";
cheat.description = "";
codeValue.setText("");
descriptionValue.setText("");
doRefresh();
synchronizeCodes();
}
}
auto CheatEditor::synchronizeCodes() -> void {
if(!emulator) return;
string_vector codes;
for(auto& cheat : cheats) {
if(!cheat.enabled || !cheat.code) continue;
codes.append(cheat.code);
}
emulator->cheatSet(codes);
}
//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 enabled) -> 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::loadCheats() -> void {
doReset(true);
Update to v099r08 release. byuu says: Changelog: - nall/vfs work 100% completed; even SGB games load now - emulation cores now call load() for the base cartridges as well - updated port/device handling; portmask is gone; device ID bug should be resolved now - SNES controller port 1 multitap option was removed - added support for 128KiB SNES PPU VRAM (for now, edit sfc/ppu/ppu.hpp VRAM::size=0x10000; to enable) Overall, nall/vfs was a huge success!! We've substantially reduced the amount of boilerplate code everywhere, while still allowing (even easier than before) support for RAM-based game loading/saving. All of nall/stream is dead and buried. I am considering removing Emulator::Interface::Medium::id and/or bootable flag. Or at least, doing something different with it. The values for the non-bootable GB/BS/ST entries duplicate the ID that is supposed to be unique. They are for GB/GBC and WS/WSC. Maybe I'll use this as the hardware revision selection ID, and then gut non-bootable options. There's really no reason for that to be there. I think at one point I was using it to generate library tabs for non-bootable systems, but we don't do that anymore anyway. Emulator::Interface::load() may not need the required flag anymore ... it doesn't really do anything right now anyway. I have a few reasons for having the cores load the base cartridge. Most importantly, it is going to enable a special mode for the WonderSwan / WonderSwan Color in the future. If we ever get the IPLROMs dumped ... it's possible to boot these systems with no games inserted to set user profile information and such. There are also other systems that may accept being booted without a cartridge. To reach this state, you would load a game and then cancel the load dialog. Right now, this results in games not loading. The second reason is this prevents nasty crashes when loading fails. So if you're missing a required manifest, the emulator won't die a violent death anymore. It's able to back out at any point. The third reason is consistency: loading the base cartridge works the same as the slot cartridges. The fourth reason is Emulator::Interface::open(uint pathID) values. Before, the GB, SB, GBC modes were IDs 1,2,3 respectively. This complicated things because you had to pass the correct ID. But now instead, Emulator::Interface::load() returns maybe<uint> that is nothing when no game is selected, and a pathID for a valid game. And now open() can take this ID to access this game's folder contents. The downside, which is temporary, is that command-line loading is currently broken. But I do intend on restoring it. In fact, I want to do better than before and allow multi-cart booting from the command-line by specifying the base cartridge and then slot cartridges. The idea should be pretty simple: keep a queue of pending filenames that we fill from the command-line and/or drag-and-drop operations on the main window, and then empty out the queue or prompt for load dialogs from the UI when booting a system. This also might be a bit more unorthodox compared to the traditional emulator design of "loadGame(filename)", but ... oh well. It's easy enough still. The port/device changes are fun. We simplified things quite a bit. The portmask stuff is gone entirely. While ports and devices keep IDs, this is really just sugar-coating so UIs can use for(auto& port : emulator->ports) and access port.id; rather than having to use for(auto n : range(emulator->ports)) { auto& port = emulator->ports[n]; ... }; but they should otherwise generally be identical to the order they appear in their respective ranges. Still, don't rely on that. Input::id is gone. There was no point since we also got rid of the nasty Input::order vector. Since I was in here, I went ahead and caved on the pedantics and renamed Input::guid to Input::userData. I removed the SNES controller port 1 multitap option. Basically, the only game that uses this is N-warp Daisakusen and, no offense to d4s, it's not really a good game anyway. It's just a quick demo to show 8-players on the SNES. But in the UI, all it does is confuse people into wasting time mapping a controller they're never going to use, and they're going to wonder which port to use. If more compelling use cases for 8-players comes about, we can reconsider this. I left all the code to support this in place, so all you have to do is uncomment one line to enable it again. We now have dsnes emulation! :D If you change PPU::VRAM::size to 0x10000 (words), then you should now have 128KiB of VRAM. Even better, it serializes the used-VRAM size, so your save states shouldn't crash on you if you swap between the two (though if you try this, you're nuts.) Note that this option does break commercial software. Yoshi's Island in particular. This game is setting A15 on some PPU register writes, but not on others. The end result of this is things break horribly in-game. Also, this option is causing a very tiny speed hit for obvious reasons with the variable masking value (I'm even using size-1 for now.) Given how niche this is, I may just leave it a compile-time constant to avoid the overhead cost. Otherwise, if we keep the option, then it'll go into Super Famicom.sys/manifest.bml ... I'll flesh that out in the near-future. ---- Finally, some fun for my OCD ... my monitor suddenly cut out on me in the middle of working on this WIP, about six hours in of non-stop work. Had to hit a bunch of ctrl+alt+fN commands (among other things) and trying to log in headless on another TTY to do issue commands, trying to recover the display. Finally power cycled the monitor and it came back up. So all my typing ended up going to who knows where. Usually this sort of thing terrifies me enough that I scrap a WIP and start over to ensure I didn't screw anything up during the crashed screen when hitting keys randomly. Obviously, everything compiles and appears to work fine. And I know it's extremely paranoid, but OCD isn't logical, so ... I'm going to go over every line of the 100KiB r07->r08 diff looking for any corruption/errors/whatever. ---- Review finished. r08 diff review notes: - fc/controller/gamepad/gamepad.cpp: use uint device = ID::Device::Gamepad; not id = ...; - gb/cartridge/cartridge.hpp: remove redundant uint _pathID; (in Information::pathID already) - gb/cartridge/cartridge.hpp: pull sha256 inside Information - sfc/cartridge/load/cpp: add " - Slot (A,B)" to interface->load("Sufami Turbo"); to be more descriptive - sfc/controller/gamepad/gamepad.cpp: use uint device = ID::Device::Gamepad; not id = ...; - sfc/interface/interface.cpp: remove n variable from the Multitap device input generation loop (now unused) - sfc/interface/interface.hpp: put struct Port above struct Device like the other classes - ui-tomoko: cheats.bml is reading from/writing to mediumPaths(0) [system folder instead of game folder] - ui-tomoko: instead of mediumPaths(1) - call emulator->metadataPathID() or something like that
2016-06-24 12:16:53 +00:00
auto contents = string::read({program->mediumPaths(1), "cheats.bml"});
auto document = BML::unserialize(contents);
for(auto cheat : document["cartridge"].find("cheat")) {
if(!addCode(cheat["code"].text(), cheat["description"].text(), (bool)cheat["enabled"])) break;
}
doRefresh();
synchronizeCodes();
}
auto CheatEditor::saveCheats() -> void {
if(!emulator) return;
string document = {"cartridge sha256:", emulator->sha256(), "\n"};
uint 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->mediumPaths(1), "cheats.bml"}, document);
} else {
file::remove({program->mediumPaths(1), "cheats.bml"});
}
doReset(true);
}