mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-22 22:22:42 +01:00
byuu says: Changelog: - game/memory/type/battery → game/memory/volatile - (manufacturer.)content.type → (architecture.)content.type - nall: Markup::find() strips spaces from values in comparisons - higan: updated game manifest loading/saving code for all cores - GBA: flash memory ID is internally selected based on the manufacturer and memory size - SFC: ST018 (ARM6) frequency can be modified via game manifest now - WS: EEPROM::name removed (not useful) - icarus, genius: battery→volatile updates I did my best to look over the diff between r13 and r14, but it's 84KiB excluding the game database changes. It's just too much for me. I'd greatly appreciate if someone could look over it and check for any errors in this update. But more than likely, I suppose we'll iron out any issues by determining which games fail to load. Right now, I know the Super Game Boy support doesn't seem to work. But all non-SFC cores should work fully, and all normal + NEC DSP SFC games should work as well. Unsure about the rest. Also, I'm planning to change the Game Boy “MBC1M” mapper to “MBC1#A” to indicate it's an alternate wiring configuration of the stock MBC1, and not a new mapper type.
641 lines
21 KiB
C++
641 lines
21 KiB
C++
#include <nall/nall.hpp>
|
|
using namespace nall;
|
|
|
|
#include <hiro/hiro.hpp>
|
|
using namespace hiro;
|
|
|
|
#include "genius.hpp"
|
|
unique_pointer<ListWindow> listWindow;
|
|
unique_pointer<GameWindow> gameWindow;
|
|
unique_pointer<MemoryWindow> memoryWindow;
|
|
unique_pointer<OscillatorWindow> oscillatorWindow;
|
|
|
|
//
|
|
|
|
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\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({820, 600});
|
|
reloadList();
|
|
updateWindow();
|
|
setCentered();
|
|
}
|
|
|
|
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.label = node["label"].text();
|
|
game.name = node["name"].text();
|
|
game.region = node["region"].text();
|
|
game.revision = node["revision"].text();
|
|
game.board = node["board"].text();
|
|
for(auto object : node["board"]) {
|
|
Component component;
|
|
if(object.name() == "memory") {
|
|
component.type = Component::Type::Memory;
|
|
component.memory.type = object["type"].text();
|
|
component.memory.size = object["size"].text();
|
|
component.memory.content = object["content"].text();
|
|
component.memory.manufacturer = object["manufacturer"].text();
|
|
component.memory.architecture = object["architecture"].text();
|
|
component.memory.identifier = object["identifier"].text();
|
|
component.memory.Volatile = (bool)object["volatile"];
|
|
}
|
|
if(object.name() == "oscillator") {
|
|
component.type = Component::Type::Oscillator;
|
|
component.oscillator.frequency = object["frequency"].text();
|
|
}
|
|
game.components.append(component);
|
|
}
|
|
game.note = node["note"].text();
|
|
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, "\n", x.region, "\n", x.revision},
|
|
{y.name, "\n", y.region, "\n", y.revision}
|
|
) < 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");
|
|
if(game.label)
|
|
fp.print(" label: ", game.label, "\n");
|
|
fp.print(" name: ", game.name, "\n");
|
|
fp.print(" region: ", game.region, "\n");
|
|
fp.print(" revision: ", game.revision, "\n");
|
|
if(game.board)
|
|
fp.print(" board: ", game.board, "\n");
|
|
else if(game.components)
|
|
fp.print(" board\n");
|
|
for(auto& component : game.components) {
|
|
if(component.type == Component::Type::Memory) {
|
|
fp.print(" memory\n");
|
|
fp.print(" type: ", component.memory.type, "\n");
|
|
fp.print(" size: ", component.memory.size, "\n");
|
|
fp.print(" content: ", component.memory.content, "\n");
|
|
if(component.memory.manufacturer)
|
|
fp.print(" manufacturer: ", component.memory.manufacturer, "\n");
|
|
if(component.memory.architecture)
|
|
fp.print(" architecture: ", component.memory.architecture, "\n");
|
|
if(component.memory.identifier)
|
|
fp.print(" identifier: ", component.memory.identifier, "\n");
|
|
if(component.memory.Volatile)
|
|
fp.print(" volatile\n");
|
|
}
|
|
|
|
if(component.type == Component::Type::Oscillator) {
|
|
fp.print(" oscillator\n");
|
|
fp.print(" frequency: ", component.oscillator.frequency, "\n");
|
|
}
|
|
}
|
|
if(game.note)
|
|
fp.print(" note: ", game.note, "\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:").setAlignment(1.0);
|
|
hashEdit.setFont(Font().setFamily(Font::Mono)).onChange([&] { modified = true, updateWindow(); });
|
|
regionLabel.setText("Region:").setAlignment(1.0);
|
|
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:").setAlignment(1.0);
|
|
nameEdit.onChange([&] { modified = true, updateWindow(); });
|
|
labelLabel.setText("Label:").setAlignment(1.0);
|
|
labelEdit.onChange([&] { modified = true, updateWindow(); });
|
|
noteLabel.setText("Note:").setAlignment(1.0);
|
|
noteEdit.onChange([&] { modified = true, updateWindow(); });
|
|
componentLabel.setText("Tree:").setAlignment({1.0, 0.0});
|
|
componentTree.onActivate([&] { modifyComponentButton.doActivate(); });
|
|
componentTree.onChange([&] { updateWindow(); });
|
|
appendMemoryButton.setText("Memory").onActivate([&] {
|
|
setEnabled(false);
|
|
memoryWindow->show();
|
|
});
|
|
appendOscillatorButton.setText("Oscillator").onActivate([&] {
|
|
setEnabled(false);
|
|
oscillatorWindow->show();
|
|
});
|
|
modifyComponentButton.setText("Modify").onActivate([&] {
|
|
if(auto item = componentTree.selected()) {
|
|
setEnabled(false);
|
|
auto path = item.path().split("/");
|
|
auto offset = path(0).natural();
|
|
Component component = game.components[offset];
|
|
if(component.type == Component::Type::Memory) {
|
|
memoryWindow->show(component.memory);
|
|
}
|
|
if(component.type == Component::Type::Oscillator) {
|
|
oscillatorWindow->show(component.oscillator);
|
|
}
|
|
}
|
|
});
|
|
removeComponentButton.setText("Remove").onActivate([&] { removeComponent(); });
|
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
|
|
|
onClose([&] { cancel(); });
|
|
|
|
setSize({640, 480});
|
|
setDismissable();
|
|
}
|
|
|
|
auto GameWindow::show(Game game) -> void {
|
|
this->game = game;
|
|
modified = false;
|
|
create = !game.sha256;
|
|
|
|
hashEdit.setText(game.sha256);
|
|
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" : "Apply");
|
|
|
|
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 {
|
|
componentTree.reset();
|
|
uint counter = 1;
|
|
for(auto& component : game.components) {
|
|
TreeViewItem item;
|
|
|
|
string index = {"[", counter++, "] "};
|
|
if(component.type == Component::Type::Memory) {
|
|
item.setText({index, "Memory"});
|
|
item.append(TreeViewItem().setText({"Type: ", component.memory.type}));
|
|
item.append(TreeViewItem().setText({"Size: ", component.memory.size}));
|
|
item.append(TreeViewItem().setText({"Content: ", component.memory.content}));
|
|
if(component.memory.manufacturer)
|
|
item.append(TreeViewItem().setText({"Manufacturer: ", component.memory.manufacturer}));
|
|
if(component.memory.architecture)
|
|
item.append(TreeViewItem().setText({"Architecture: ", component.memory.architecture}));
|
|
if(component.memory.identifier)
|
|
item.append(TreeViewItem().setText({"Identifier: ", component.memory.identifier}));
|
|
if(component.memory.Volatile)
|
|
item.append(TreeViewItem().setText({"Volatile"}));
|
|
}
|
|
|
|
if(component.type == Component::Type::Oscillator) {
|
|
item.setText({index, "Oscillator"});
|
|
item.append(TreeViewItem().setText({"Frequency: ", component.oscillator.frequency}));
|
|
}
|
|
|
|
componentTree.append(item);
|
|
}
|
|
|
|
Application::processEvents();
|
|
for(auto& item : componentTree.items()) item.setExpanded();
|
|
}
|
|
|
|
auto GameWindow::updateWindow() -> void {
|
|
bool valid = true;
|
|
bool hashValid = hashEdit.text().strip().size() == 64;
|
|
hashEdit.setEditable(!hashValid).setBackgroundColor(
|
|
!create || hashValid ? Color{192, 255, 192}
|
|
: (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}));
|
|
modifyComponentButton.setEnabled((bool)componentTree.selected());
|
|
removeComponentButton.setEnabled((bool)componentTree.selected());
|
|
acceptButton.setEnabled(valid);
|
|
setTitle({modified ? "*" : "", create ? "Add New Game" : "Modify Game Details"});
|
|
if(create && hashValid && hashEdit.focused()) regionEdit.setFocused();
|
|
}
|
|
|
|
auto GameWindow::appendComponent(Component component) -> void {
|
|
modified = true;
|
|
auto offset = game.components.size();
|
|
game.components.append(component);
|
|
reloadList();
|
|
componentTree.item(offset).setSelected().setFocused();
|
|
updateWindow();
|
|
}
|
|
|
|
auto GameWindow::modifyComponent(Component component) -> void {
|
|
if(auto item = componentTree.selected()) {
|
|
modified = true;
|
|
auto path = item.path().split("/");
|
|
auto offset = path(0).natural();
|
|
game.components[offset] = component;
|
|
reloadList();
|
|
componentTree.item(offset).setSelected().setFocused();
|
|
updateWindow();
|
|
}
|
|
}
|
|
|
|
auto GameWindow::removeComponent() -> void {
|
|
if(auto item = componentTree.selected()) {
|
|
if(MessageDialog().setParent(*this).setText({
|
|
"Are you sure you want to permanently remove this component?"
|
|
}).question() == "Yes") {
|
|
modified = true;
|
|
auto path = item.path().split("/");
|
|
auto offset = path(0).natural();
|
|
game.components.remove(offset);
|
|
reloadList();
|
|
updateWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
|
|
MemoryWindow::MemoryWindow() {
|
|
memoryWindow = this;
|
|
|
|
layout.setMargin(5);
|
|
typeLabel.setText("Type:").setAlignment(1.0);
|
|
typeEdit.append(ComboEditItem().setText("ROM"));
|
|
typeEdit.append(ComboEditItem().setText("EEPROM"));
|
|
typeEdit.append(ComboEditItem().setText("Flash"));
|
|
typeEdit.append(ComboEditItem().setText("RAM"));
|
|
typeEdit.append(ComboEditItem().setText("RTC"));
|
|
typeEdit.onChange([&] { modified = true, updateWindow(); });
|
|
sizeLabel.setText("Size:").setAlignment(1.0);
|
|
sizeEdit.onChange([&] { modified = true, updateWindow(); });
|
|
contentLabel.setText("Content:").setAlignment(1.0);
|
|
contentEdit.append(ComboEditItem().setText("Program"));
|
|
contentEdit.append(ComboEditItem().setText("Data"));
|
|
contentEdit.append(ComboEditItem().setText("Character"));
|
|
contentEdit.append(ComboEditItem().setText("Save"));
|
|
contentEdit.append(ComboEditItem().setText("Time"));
|
|
contentEdit.onChange([&] { modified = true, updateWindow(); });
|
|
manufacturerLabel.setText("Manufacturer:").setAlignment(1.0);
|
|
manufacturerEdit.onChange([&] { modified = true, updateWindow(); });
|
|
architectureLabel.setText("Architecture:").setAlignment(1.0);
|
|
architectureEdit.onChange([&] { modified = true, updateWindow(); });
|
|
identifierLabel.setText("Identifier:").setAlignment(1.0);
|
|
identifierEdit.onChange([&] { modified = true, updateWindow(); });
|
|
volatileOption.setText("Volatile").onToggle([&] { modified = true, updateWindow(); });
|
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
|
|
|
onClose([&] { cancel(); });
|
|
|
|
setSize({320, 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);
|
|
contentEdit.setText(memory.content);
|
|
manufacturerEdit.setText(memory.manufacturer);
|
|
architectureEdit.setText(memory.architecture);
|
|
identifierEdit.setText(memory.identifier);
|
|
volatileOption.setChecked(memory.Volatile);
|
|
|
|
updateWindow();
|
|
setCentered(*gameWindow);
|
|
setVisible();
|
|
|
|
typeEdit.setFocused();
|
|
}
|
|
|
|
auto MemoryWindow::accept() -> void {
|
|
memory.type = typeEdit.text().strip();
|
|
memory.size = sizeEdit.text().strip();
|
|
memory.content = contentEdit.text().strip();
|
|
memory.manufacturer = manufacturerEdit.text().strip();
|
|
memory.architecture = architectureEdit.text().strip();
|
|
memory.identifier = identifierEdit.text().strip();
|
|
memory.Volatile = volatileOption.checked() && (memory.type == "RAM" || memory.type == "RTC");
|
|
|
|
Component component{Component::Type::Memory};
|
|
component.memory = memory;
|
|
if(create) {
|
|
gameWindow->appendComponent(component);
|
|
} else {
|
|
gameWindow->modifyComponent(component);
|
|
}
|
|
|
|
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}));
|
|
contentEdit.setBackgroundColor(contentEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
|
manufacturerEdit.setBackgroundColor(manufacturerEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
|
architectureEdit.setBackgroundColor(architectureEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
|
identifierEdit.setBackgroundColor(identifierEdit.text().strip() ? Color{} : (Color{255, 255, 240}));
|
|
volatileOption.setEnabled(typeEdit.text().strip() == "RAM" || typeEdit.text().strip() == "RTC");
|
|
acceptButton.setEnabled(valid);
|
|
setTitle({modified ? "*" : "", create ? "Add New Memory" : "Modify Memory Details"});
|
|
}
|
|
|
|
//
|
|
|
|
OscillatorWindow::OscillatorWindow() {
|
|
oscillatorWindow = this;
|
|
|
|
layout.setMargin(5);
|
|
frequencyLabel.setText("Frequency:").setAlignment(1.0);
|
|
frequencyEdit.onChange([&] { modified = true, updateWindow(); });
|
|
acceptButton.setText("Accept").onActivate([&] { accept(); });
|
|
cancelButton.setText("Cancel").onActivate([&] { cancel(); });
|
|
|
|
onClose([&] { cancel(); });
|
|
|
|
setSize({320, layout.minimumSize().height()});
|
|
setDismissable();
|
|
}
|
|
|
|
auto OscillatorWindow::show(Oscillator oscillator) -> void {
|
|
this->oscillator = oscillator;
|
|
modified = false;
|
|
create = !oscillator.frequency;
|
|
|
|
frequencyEdit.setText(oscillator.frequency);
|
|
|
|
updateWindow();
|
|
setCentered(*gameWindow);
|
|
setVisible();
|
|
|
|
frequencyEdit.setFocused();
|
|
}
|
|
|
|
auto OscillatorWindow::accept() -> void {
|
|
oscillator.frequency = frequencyEdit.text().strip();
|
|
|
|
Component component{Component::Type::Oscillator};
|
|
component.oscillator = oscillator;
|
|
if(create) {
|
|
gameWindow->appendComponent(component);
|
|
} else {
|
|
gameWindow->modifyComponent(component);
|
|
}
|
|
|
|
setVisible(false);
|
|
gameWindow->setEnabled();
|
|
gameWindow->setFocused();
|
|
}
|
|
|
|
auto OscillatorWindow::cancel() -> void {
|
|
if(!modified || MessageDialog().setParent(*this).setText({
|
|
"Are you sure you want to discard your changes to this property?"
|
|
}).question() == "Yes") {
|
|
setVisible(false);
|
|
gameWindow->setEnabled();
|
|
gameWindow->setFocused();
|
|
}
|
|
}
|
|
|
|
auto OscillatorWindow::updateWindow() -> void {
|
|
bool valid = true;
|
|
frequencyEdit.setBackgroundColor(frequencyEdit.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
|
|
acceptButton.setEnabled(valid);
|
|
setTitle({modified ? "*" : "", create ? "Add New Property" : "Modify Property Details"});
|
|
}
|
|
|
|
//
|
|
|
|
#include <nall/main.hpp>
|
|
auto nall::main(string_vector args) -> void {
|
|
Application::setName("genius");
|
|
new ListWindow;
|
|
new GameWindow;
|
|
new MemoryWindow;
|
|
new OscillatorWindow;
|
|
|
|
//internal command used to synchronize all genius databases from an old format to a new format
|
|
//if enabled, use with extreme caution and make backups first
|
|
/*if(args.size() == 3 && args[1] == "--sync") {
|
|
for(auto& filename : directory::contents(args[2], "*.bml")) {
|
|
if(filename.beginsWith("Boards")) continue;
|
|
print(filename, "\n");
|
|
listWindow->loadDatabase({args[2], filename});
|
|
listWindow->saveDatabase({args[2], filename});
|
|
}
|
|
return print("[Done]\n");
|
|
}*/
|
|
|
|
listWindow->setVisible();
|
|
Application::run();
|
|
}
|