#include using namespace nall; #include using namespace hiro; #include "genius.hpp" unique_pointer listWindow; unique_pointer gameWindow; unique_pointer memoryWindow; // ListWindow::ListWindow() { listWindow = this; fileMenu.setText("File"); newAction.setText("New").onActivate([&] { newDatabase(); }); openAction.setText("Open ...").onActivate([&] { if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).openFile()) { loadDatabase(location); } }); saveAction.setText("Save").onActivate([&] { if(!location) return saveAsAction.doActivate(); saveDatabase(location); }); saveAsAction.setText("Save As ...").onActivate([&] { if(auto location = BrowserDialog().setParent(*this).setFilters({"*.bml"}).saveFile()) { saveDatabase(location); } }); quitAction.setText("Quit").onActivate([&] { quit(); }); helpMenu.setText("Help"); aboutAction.setText("About ...").onActivate([&] { MessageDialog().setParent(*this).setTitle("About").setText({ "genius v01\n", "Author: byuu\n", "Website: https://byuu.org/" }).information(); }); layout.setMargin(5); gameList.onActivate([&] { modifyButton.doActivate(); }); gameList.onChange([&] { updateWindow(); }); appendButton.setText("Append").onActivate([&] { setEnabled(false); gameWindow->show(); }); modifyButton.setText("Modify").onActivate([&] { if(auto item = gameList.selected()) { setEnabled(false); gameWindow->show(games[item.offset()]); } }); removeButton.setText("Remove").onActivate([&] { removeGame(); }); onClose([&] { quit(); }); setSize({960, 600}); reloadList(); updateWindow(); setCentered(); setVisible(); } auto ListWindow::quit() -> void { if(!modified || MessageDialog().setParent(*this).setText({ "Are you sure you want to quit without saving your changes?" }).question() == "Yes") { Application::quit(); } } auto ListWindow::reloadList() -> void { gameList.reset(); gameList.append(TableViewHeader() .append(TableViewColumn().setText("Name").setExpandable()) .append(TableViewColumn().setText("Region")) .append(TableViewColumn().setText("Revision")) .append(TableViewColumn().setText("Board")) ); for(auto& game : games) { gameList.append(TableViewItem() .append(TableViewCell().setText(game.name)) .append(TableViewCell().setText(game.region)) .append(TableViewCell().setText(game.revision)) .append(TableViewCell().setText(game.board)) ); } Application::processEvents(); gameList.resizeColumns(); } auto ListWindow::updateWindow() -> void { modifyButton.setEnabled((bool)gameList.selected()); removeButton.setEnabled((bool)gameList.selected()); string name = Location::base(location); if(!name) name = "(Untitled)"; setTitle({modified ? "*" : "", name, " [", games.size(), "] - genius"}); } auto ListWindow::newDatabase() -> void { games.reset(); modified = false; location = ""; reloadList(); updateWindow(); } auto ListWindow::loadDatabase(string location) -> void { auto document = BML::unserialize(string::read(location)); games.reset(); for(auto node : document.find("game")) { Game game; game.sha256 = node["sha256"].text(); game.region = node["region"].text(); game.revision = node["revision"].text(); game.board = node["board"].text(); game.name = node["name"].text(); game.label = node["label"].text(); game.note = node["note"].text(); for(auto leaf : node.find("memory")) { Memory memory; memory.type = leaf["type"].text(); memory.size = leaf["size"].text(); memory.name = leaf["name"].text(); game.memories.append(memory); } games.append(game); } modified = false; this->location = location; reloadList(); updateWindow(); } auto ListWindow::saveDatabase(string location) -> void { file fp{location, file::mode::write}; if(!fp) return MessageDialog().setParent(*this).setText({ "Error: failed to write file.\n\n", "Name: ", location }).error(), void(); auto copy = games; copy.sort([](auto x, auto y) { return string::icompare(x.name, y.name) < 0; }); fp.print("database\n"); fp.print(" revision: ", chrono::local::date(), "\n\n"); for(auto& game : copy) { fp.print("game\n"); fp.print(" sha256: ", game.sha256, "\n"); fp.print(" region: ", game.region, "\n"); fp.print(" revision: ", game.revision, "\n"); if(game.board) fp.print(" board: ", game.board, "\n"); fp.print(" name: ", game.name, "\n"); if(game.label) fp.print(" label: ", game.label, "\n"); if(game.note) fp.print(" note: ", game.note, "\n"); for(auto& memory : game.memories) { fp.print(" memory\n"); fp.print(" type: ", memory.type, "\n"); fp.print(" size: ", memory.size, "\n"); fp.print(" name: ", memory.name, "\n"); } fp.print("\n"); } modified = false; this->location = location; updateWindow(); } auto ListWindow::appendGame(Game game) -> void { modified = true; auto offset = games.size(); games.append(game); reloadList(); gameList.item(offset).setSelected().setFocused(); updateWindow(); } auto ListWindow::modifyGame(Game game) -> void { if(auto item = gameList.selected()) { modified = true; auto offset = item.offset(); games[offset] = game; reloadList(); gameList.item(offset).setSelected().setFocused(); updateWindow(); } } auto ListWindow::removeGame() -> void { if(auto item = gameList.selected()) { if(MessageDialog().setParent(*this).setText({ "Are you sure you want to permanently remove this game?\n\n", "Name: ", item.cell(0).text() }).question() == "Yes") { modified = true; games.remove(item.offset()); reloadList(); updateWindow(); } } } // GameWindow::GameWindow() { gameWindow = this; layout.setMargin(5); hashLabel.setText("SHA256:"); hashEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); }); regionLabel.setText("Region:"); regionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); }); revisionLabel.setText("Revision:"); revisionEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); }); boardLabel.setText("Board:"); boardEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); }); nameLabel.setText("Name:"); nameEdit.onChange([&] { modified = true, updateWindow(); }); labelLabel.setText("Label:"); labelEdit.onChange([&] { modified = true, updateWindow(); }); noteLabel.setText("Note:"); noteEdit.onChange([&] { modified = true, updateWindow(); }); memoryList.onActivate([&] { modifyButton.doActivate(); }); memoryList.onChange([&] { updateWindow(); }); appendButton.setText("Append").onActivate([&] { setEnabled(false); memoryWindow->show(); }); modifyButton.setText("Modify").onActivate([&] { if(auto item = memoryList.selected()) { setEnabled(false); memoryWindow->show(game.memories[item.offset()]); } }); removeButton.setText("Remove").onActivate([&] { removeMemory(); }); acceptButton.setText("Accept").onActivate([&] { accept(); }); cancelButton.setText("Cancel").onActivate([&] { cancel(); }); onClose([&] { cancel(); }); setSize({800, 480}); setDismissable(); } auto GameWindow::show(Game game) -> void { this->game = game; modified = false; create = !game.sha256; hashEdit.setText(game.sha256).setEditable(create); regionEdit.setText(game.region); revisionEdit.setText(game.revision); boardEdit.setText(game.board); nameEdit.setText(game.name); labelEdit.setText(game.label); noteEdit.setText(game.note); acceptButton.setText(create ? "Create" : "Modify"); reloadList(); updateWindow(); setCentered(*listWindow); setVisible(); if(create) { hashEdit.setFocused(); } else { cancelButton.setFocused(); } } auto GameWindow::accept() -> void { game.sha256 = hashEdit.text().strip(); game.region = regionEdit.text().strip(); game.revision = revisionEdit.text().strip(); game.board = boardEdit.text().strip(); game.name = nameEdit.text().strip(); game.label = labelEdit.text().strip(); game.note = noteEdit.text().strip(); if(create) { listWindow->appendGame(game); } else { listWindow->modifyGame(game); } memoryWindow->setVisible(false); setVisible(false); listWindow->setEnabled(); listWindow->setFocused(); } auto GameWindow::cancel() -> void { if(!modified || MessageDialog().setParent(*this).setText({ "Are you sure you want to discard your changes to this game?" }).question() == "Yes") { memoryWindow->setVisible(false); setVisible(false); listWindow->setEnabled(); listWindow->setFocused(); } } auto GameWindow::reloadList() -> void { memoryList.reset(); memoryList.append(TableViewHeader() .append(TableViewColumn().setText("Type")) .append(TableViewColumn().setText("Size")) .append(TableViewColumn().setText("Name").setExpandable()) ); for(auto& memory : game.memories) { memoryList.append(TableViewItem() .append(TableViewCell().setText(memory.type)) .append(TableViewCell().setText(memory.size)) .append(TableViewCell().setText(memory.name)) ); } Application::processEvents(); memoryList.resizeColumns(); } auto GameWindow::updateWindow() -> void { bool valid = true; hashEdit.setBackgroundColor( !create ? Color{192, 255, 192} : hashEdit.text().strip().size() == 64 ? Color{} : (valid = false, Color{255, 224, 224}) ); regionEdit.setBackgroundColor(regionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); revisionEdit.setBackgroundColor(revisionEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); boardEdit.setBackgroundColor(boardEdit.text().strip() ? Color{} : (Color{255, 255, 240})); nameEdit.setBackgroundColor(nameEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); labelEdit.setBackgroundColor(labelEdit.text().strip() ? Color{} : (Color{255, 255, 240})); noteEdit.setBackgroundColor(noteEdit.text().strip() ? Color{} : (Color{255, 255, 240})); modifyButton.setEnabled((bool)memoryList.selected()); removeButton.setEnabled((bool)memoryList.selected()); acceptButton.setEnabled(valid); setTitle({modified ? "*" : "", create ? "Add New Game" : "Modify Game Details"}); } auto GameWindow::appendMemory(Memory memory) -> void { modified = true; auto offset = game.memories.size(); game.memories.append(memory); reloadList(); memoryList.item(offset).setSelected().setFocused(); updateWindow(); } auto GameWindow::modifyMemory(Memory memory) -> void { if(auto item = memoryList.selected()) { modified = true; auto offset = item.offset(); game.memories[offset] = memory; reloadList(); memoryList.item(offset).setSelected().setFocused(); updateWindow(); } } auto GameWindow::removeMemory() -> void { if(auto item = memoryList.selected()) { if(MessageDialog().setParent(*this).setText({ "Are you sure you want to permanently remove this memory?\n\n", "Name: ", item.cell(2).text() }).question() == "Yes") { modified = true; game.memories.remove(item.offset()); reloadList(); updateWindow(); } } } // MemoryWindow::MemoryWindow() { memoryWindow = this; layout.setMargin(5); typeLabel.setText("Type:"); typeEdit.append(ComboEditItem().setText("ROM")); typeEdit.append(ComboEditItem().setText("EPROM")); typeEdit.append(ComboEditItem().setText("EEPROM")); typeEdit.append(ComboEditItem().setText("NOR")); typeEdit.append(ComboEditItem().setText("PSRAM")); typeEdit.append(ComboEditItem().setText("NVRAM")); typeEdit.append(ComboEditItem().setText("RAM")); typeEdit.onChange([&] { modified = true, updateWindow(); }); sizeLabel.setText("Size:"); sizeEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); }); nameLabel.setText("Name:"); nameEdit.onChange([&] { modified = true, updateWindow(); }); acceptButton.setText("Accept").onActivate([&] { accept(); }); cancelButton.setText("Cancel").onActivate([&] { cancel(); }); onClose([&] { cancel(); }); setSize({280, layout.minimumSize().height()}); setDismissable(); } auto MemoryWindow::show(Memory memory) -> void { this->memory = memory; modified = false; create = !memory.type; typeEdit.setText(memory.type); sizeEdit.setText(memory.size); nameEdit.setText(memory.name); updateWindow(); setCentered(*gameWindow); setVisible(); typeEdit.setFocused(); } auto MemoryWindow::accept() -> void { memory.type = typeEdit.text().strip(); memory.size = sizeEdit.text().strip(); memory.name = nameEdit.text().strip(); if(create) { gameWindow->appendMemory(memory); } else { gameWindow->modifyMemory(memory); } setVisible(false); gameWindow->setEnabled(); gameWindow->setFocused(); } auto MemoryWindow::cancel() -> void { if(!modified || MessageDialog().setParent(*this).setText({ "Are you sure you want to discard your changes to this memory?" }).question() == "Yes") { setVisible(false); gameWindow->setEnabled(); gameWindow->setFocused(); } } auto MemoryWindow::updateWindow() -> void { bool valid = true; typeEdit.setBackgroundColor(typeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); sizeEdit.setBackgroundColor(sizeEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); nameEdit.setBackgroundColor(nameEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224})); acceptButton.setEnabled(valid); setTitle({modified ? "*" : "", create ? "Add New Memory" : "Modify Memory Details"}); } // #include auto nall::main(string_vector) -> void { Application::setName("genius"); new ListWindow; new GameWindow; new MemoryWindow; Application::run(); }