StateManager *stateManager = nullptr; StateManager::StateManager() { setTitle("State Manager"); setGeometry({ 128, 128, 600, 360 }); windowManager->append(this, "StateManager"); stateList.setHeaderText("Slot", "Description"); stateList.setHeaderVisible(); descLabel.setText("Description:"); loadButton.setText("Load"); saveButton.setText("Save"); eraseButton.setText("Erase"); append(layout); layout.setMargin(5); layout.append(stateList, { ~0, ~0 }, 5); layout.append(descLayout, { ~0, 0 }, 5); descLayout.append(descLabel, { 0, 0 }, 5); descLayout.append(descEdit, { ~0, 0 }, 0); layout.append(controlLayout, { ~0, 0 }, 0); controlLayout.append(spacer, { ~0, 0 }, 0); controlLayout.append(loadButton, { 80, 0 }, 5); controlLayout.append(saveButton, { 80, 0 }, 5); controlLayout.append(eraseButton, { 80, 0 }, 0); for(unsigned n = 0; n < 32; n++) stateList.append(decimal<2>(n + 1), "(empty)"); stateList.autoSizeColumns(); synchronize(); stateList.onActivate = { &StateManager::slotLoad, this }; stateList.onChange = { &StateManager::synchronize, this }; descEdit.onChange = { &StateManager::slotSaveDescription, this }; loadButton.onActivate = { &StateManager::slotLoad, this }; saveButton.onActivate = { &StateManager::slotSave, this }; eraseButton.onActivate = { &StateManager::slotErase, this }; } void StateManager::synchronize() { layout.setEnabled(interface->cartridgeLoaded()); descEdit.setText(""); descEdit.setEnabled(false); controlLayout.setEnabled(stateList.selected()); if(stateList.selected() == false) return; if(slot[stateList.selection()].capacity() > 0) { descEdit.setText(slotLoadDescription(stateList.selection())); descEdit.setEnabled(true); } } void StateManager::refresh() { for(unsigned n = 0; n < 32; n++) { stateList.modify(n, decimal<2>(n + 1), slotLoadDescription(n)); } stateList.autoSizeColumns(); } void StateManager::reset() { for(unsigned n = 0; n < 32; n++) slot[n] = serializer(); synchronize(); refresh(); } bool StateManager::load(const string &filename, unsigned revision) { for(unsigned n = 0; n < 32; n++) slot[n] = serializer(); synchronize(); file fp; if(fp.open(filename, file::mode::read) == false) return false; if(fp.readl(4) == 0x31415342) { if(fp.readl(4) == revision) { //'BSA1' for(unsigned n = 0; n < 32; n++) { if(fp.read() == false) continue; unsigned size = fp.readl(4); uint8_t *data = new uint8_t[size]; fp.read(data, size); slot[n] = serializer(data, size); delete[] data; } } } refresh(); return true; } bool StateManager::save(const string &filename, unsigned revision) { bool hasSave = false; for(unsigned n = 0; n < 32; n++) { if(slot[n].capacity() > 0) hasSave = true; } if(hasSave == false) { unlink(filename); return true; } file fp; if(fp.open(filename, file::mode::write) == false) return false; fp.writel(0x31415342, 4); //'BSA1' fp.writel(revision, 4); for(unsigned n = 0; n < 32; n++) { if(slot[n].capacity() == 0) { fp.write(false); } else { fp.write(true); fp.writel(slot[n].size(), 4); fp.write(slot[n].data(), slot[n].size()); } } } void StateManager::slotLoad() { if(stateList.selected() == false) return; serializer s(slot[stateList.selection()].data(), slot[stateList.selection()].capacity()); interface->unserialize(s); } void StateManager::slotSave() { if(stateList.selected()) { slot[stateList.selection()] = interface->serialize(); } refresh(); synchronize(); descEdit.setFocused(); } void StateManager::slotErase() { if(stateList.selected()) { slot[stateList.selection()] = serializer(); } refresh(); synchronize(); } string StateManager::slotLoadDescription(unsigned n) { if(slot[n].capacity() == 0) return "(empty)"; char text[DescriptionLength]; strlcpy(text, (const char*)slot[n].data() + HeaderLength, DescriptionLength); return text; } void StateManager::slotSaveDescription() { if(stateList.selected() == false) return; string text = descEdit.text(); if(slot[stateList.selection()].capacity() > 0) { strlcpy((char*)slot[stateList.selection()].data() + HeaderLength, (const char*)text, DescriptionLength); } refresh(); }