Update to v106r36 release.

byuu says:

Changelog:

  - nall: renamed array to adaptive_array; marked it as deprecated
  - nall: created new array class; which is properly static (ala
    std::array) with optional bounds-checking
  - sfc/ppu-fast: converted unmanaged arrays to use nall/array (no speed
    penalty)
  - bsnes: rewrote the cheat code editor to a new design
  - nall: string class can stringify pointer types directly now, so
    pointer() was removed
  - nall: added array_view and pointer types (still unsure if/how I'll
    use pointer)
This commit is contained in:
Tim Allen 2018-06-04 12:44:57 +10:00
parent 77ac5f9e88
commit ec9729a9e1
26 changed files with 573 additions and 286 deletions

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <nall/nall.hpp> #include <nall/nall.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/vfs.hpp> #include <nall/vfs.hpp>
using namespace nall; using namespace nall;
@ -12,7 +13,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.35"; static const string Version = "106.36";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";

View File

@ -140,8 +140,8 @@ struct VDP : Thread {
Pixel output; Pixel output;
array<Object, 80> oam; adaptive_array<Object, 80> oam;
array<Object, 20> objects; adaptive_array<Object, 20> objects;
}; };
Sprite sprite; Sprite sprite;

View File

@ -68,7 +68,7 @@ struct VDP : Thread {
uint4 color; uint4 color;
} output; } output;
array<Object, 8> objects; adaptive_array<Object, 8> objects;
} sprite; } sprite;
//serialization.cpp //serialization.cpp

View File

@ -170,7 +170,7 @@ private:
bool vflip = 0; bool vflip = 0;
bool first = 0; bool first = 0;
}; };
array<Object, 64> objects; adaptive_array<Object, 64> objects;
uint4 color; uint4 color;
uint4 palette; uint4 palette;

View File

@ -3,8 +3,8 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void
if(self.tileMode == TileMode::Mode7) return renderMode7(self, source); if(self.tileMode == TileMode::Mode7) return renderMode7(self, source);
if(self.tileMode == TileMode::Inactive) return; if(self.tileMode == TileMode::Inactive) return;
bool windowAbove[256]; array<bool[256]> windowAbove;
bool windowBelow[256]; array<bool[256]> windowBelow;
renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow); renderWindow(self.window, self.window.belowEnable, windowBelow);

View File

@ -589,11 +589,11 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::BPP2; io.bg2.tileMode = TileMode::BPP2;
io.bg3.tileMode = TileMode::BPP2; io.bg3.tileMode = TileMode::BPP2;
io.bg4.tileMode = TileMode::BPP2; io.bg4.tileMode = TileMode::BPP2;
memory::assign(io.bg1.priority, 8, 11); io.bg1.priority = { 8, 11};
memory::assign(io.bg2.priority, 7, 10); io.bg2.priority = { 7, 10};
memory::assign(io.bg3.priority, 2, 5); io.bg3.priority = { 2, 5};
memory::assign(io.bg4.priority, 1, 4); io.bg4.priority = { 1, 4};
memory::assign(io.obj.priority, 3, 6, 9, 12); io.obj.priority = { 3, 6, 9, 12};
break; break;
case 1: case 1:
@ -602,15 +602,15 @@ auto PPU::updateVideoMode() -> void {
io.bg3.tileMode = TileMode::BPP2; io.bg3.tileMode = TileMode::BPP2;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
if(io.bgPriority) { if(io.bgPriority) {
memory::assign(io.bg1.priority, 5, 8); io.bg1.priority = { 5, 8};
memory::assign(io.bg2.priority, 4, 7); io.bg2.priority = { 4, 7};
memory::assign(io.bg3.priority, 1, 10); io.bg3.priority = { 1, 10};
memory::assign(io.obj.priority, 2, 3, 6, 9); io.obj.priority = { 2, 3, 6, 9};
} else { } else {
memory::assign(io.bg1.priority, 6, 9); io.bg1.priority = { 6, 9};
memory::assign(io.bg2.priority, 5, 8); io.bg2.priority = { 5, 8};
memory::assign(io.bg3.priority, 1, 3); io.bg3.priority = { 1, 3};
memory::assign(io.obj.priority, 2, 4, 7, 10); io.obj.priority = { 2, 4, 7, 10};
} }
break; break;
@ -619,9 +619,9 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::BPP4; io.bg2.tileMode = TileMode::BPP4;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 3, 7); io.bg1.priority = { 3, 7};
memory::assign(io.bg2.priority, 1, 5); io.bg2.priority = { 1, 5};
memory::assign(io.obj.priority, 2, 4, 6, 8); io.obj.priority = { 2, 4, 6, 8};
break; break;
case 3: case 3:
@ -629,9 +629,9 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::BPP4; io.bg2.tileMode = TileMode::BPP4;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 3, 7); io.bg1.priority = { 3, 7};
memory::assign(io.bg2.priority, 1, 5); io.bg2.priority = { 1, 5};
memory::assign(io.obj.priority, 2, 4, 6, 8); io.obj.priority = { 2, 4, 6, 8};
break; break;
case 4: case 4:
@ -639,9 +639,9 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::BPP2; io.bg2.tileMode = TileMode::BPP2;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 3, 7); io.bg1.priority = { 3, 7};
memory::assign(io.bg2.priority, 1, 5); io.bg2.priority = { 1, 5};
memory::assign(io.obj.priority, 2, 4, 6, 8); io.obj.priority = { 2, 4, 6, 8};
break; break;
case 5: case 5:
@ -649,9 +649,9 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::BPP2; io.bg2.tileMode = TileMode::BPP2;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 3, 7); io.bg1.priority = { 3, 7};
memory::assign(io.bg2.priority, 1, 5); io.bg2.priority = { 1, 5};
memory::assign(io.obj.priority, 2, 4, 6, 8); io.obj.priority = { 2, 4, 6, 8};
break; break;
case 6: case 6:
@ -659,8 +659,8 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::Inactive; io.bg2.tileMode = TileMode::Inactive;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 2, 5); io.bg1.priority = { 2, 5};
memory::assign(io.obj.priority, 1, 3, 4, 6); io.obj.priority = { 1, 3, 4, 6};
break; break;
case 7: case 7:
@ -669,16 +669,16 @@ auto PPU::updateVideoMode() -> void {
io.bg2.tileMode = TileMode::Inactive; io.bg2.tileMode = TileMode::Inactive;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 2); io.bg1.priority = { 2};
memory::assign(io.obj.priority, 1, 3, 4, 5); io.obj.priority = { 1, 3, 4, 5};
} else { } else {
io.bg1.tileMode = TileMode::Mode7; io.bg1.tileMode = TileMode::Mode7;
io.bg2.tileMode = TileMode::Mode7; io.bg2.tileMode = TileMode::Mode7;
io.bg3.tileMode = TileMode::Inactive; io.bg3.tileMode = TileMode::Inactive;
io.bg4.tileMode = TileMode::Inactive; io.bg4.tileMode = TileMode::Inactive;
memory::assign(io.bg1.priority, 3); io.bg1.priority = { 3};
memory::assign(io.bg2.priority, 1, 5); io.bg2.priority = { 1, 5};
memory::assign(io.obj.priority, 2, 4, 6, 7); io.obj.priority = { 2, 4, 6, 7};
} }
break; break;
} }

View File

@ -20,8 +20,8 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
int originX = (a * clip(hoffset - hcenter) & ~63) + (b * clip(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8); int originX = (a * clip(hoffset - hcenter) & ~63) + (b * clip(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8);
int originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8); int originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8);
bool windowAbove[256]; array<bool[256]> windowAbove;
bool windowBelow[256]; array<bool[256]> windowBelow;
renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow); renderWindow(self.window, self.window.belowEnable, windowBelow);

View File

@ -1,8 +1,8 @@
auto PPU::Line::renderObject(PPU::IO::Object& self) -> void { auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
if(!self.aboveEnable && !self.belowEnable) return; if(!self.aboveEnable && !self.belowEnable) return;
bool windowAbove[256]; array<bool[256]> windowAbove;
bool windowBelow[256]; array<bool[256]> windowBelow;
renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow); renderWindow(self.window, self.window.belowEnable, windowBelow);

View File

@ -13,21 +13,21 @@ PPU ppu;
#include <sfc/ppu/counter/serialization.cpp> #include <sfc/ppu/counter/serialization.cpp>
PPU::PPU() { PPU::PPU() {
output = new uint32[512 * 512]; output = new uint32[512 * 512] + 16 * 512; //overscan offset
output += 16 * 512; //overscan offset
tilecache[TileMode::BPP2] = new uint8[4096 * 8 * 8]; tilecache[TileMode::BPP2] = new uint8[4096 * 8 * 8];
tilecache[TileMode::BPP4] = new uint8[2048 * 8 * 8]; tilecache[TileMode::BPP4] = new uint8[2048 * 8 * 8];
tilecache[TileMode::BPP8] = new uint8[1024 * 8 * 8]; tilecache[TileMode::BPP8] = new uint8[1024 * 8 * 8];
for(uint y : range(240)) { for(uint y : range(lines.size())) {
lines[y].y = y; lines[y].y = y;
} }
} }
PPU::~PPU() { PPU::~PPU() {
output -= 16 * 512; //overscan offset delete[] (output - 16 * 512); //overscan offset
delete[] output; delete[] tilecache[TileMode::BPP2];
delete[] tilecache[TileMode::BPP4];
delete[] tilecache[TileMode::BPP8];
} }
auto PPU::Enter() -> void { auto PPU::Enter() -> void {

View File

@ -35,9 +35,9 @@ public:
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint1 interlace; uint1 interlace;
uint1 overscan; uint1 overscan;
uint1 hires; uint1 hires;
uint16 vram; uint16 vram;
uint8 oam; uint8 oam;
@ -153,7 +153,7 @@ public:
uint16 hoffset; uint16 hoffset;
uint16 voffset; uint16 voffset;
uint3 tileMode; uint3 tileMode;
uint4 priority[2]; array<uint4[2]> priority;
} bg1, bg2, bg3, bg4; } bg1, bg2, bg3, bg4;
struct Object { struct Object {
@ -170,7 +170,7 @@ public:
uint7 first; uint7 first;
uint1 rangeOver; uint1 rangeOver;
uint1 timeOver; uint1 timeOver;
uint4 priority[4]; array<uint4[4]> priority;
} obj; } obj;
struct Color { struct Color {
@ -178,7 +178,7 @@ public:
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
WindowColor window; WindowColor window;
uint1 enable[7]; array<uint1[7]> enable;
uint1 directColor; uint1 directColor;
uint1 blendMode; //0 = fixed; 1 = pixel uint1 blendMode; //0 = fixed; 1 = pixel
uint1 halve; uint1 halve;
@ -249,13 +249,13 @@ public:
Latch latch; Latch latch;
IO io; IO io;
uint16 vram[32 * 1024]; array<uint16[32 * 1024]> vram;
uint15 cgram[256]; array<uint15[256]> cgram;
Object objects[128]; array<Object[128]> objects;
//[unserialized] //[unserialized]
uint32* output = nullptr; uint32* output;
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata array<uint8*[3]> tilecache; //bitplane -> bitmap tiledata
struct Line { struct Line {
//line.cpp //line.cpp
@ -278,28 +278,29 @@ public:
auto renderObject(PPU::IO::Object&) -> void; auto renderObject(PPU::IO::Object&) -> void;
//window.cpp //window.cpp
auto renderWindow(PPU::IO::WindowLayer&, bool, bool*) -> void; auto renderWindow(PPU::IO::WindowLayer&, bool, array<bool[256]>&) -> void;
auto renderWindow(PPU::IO::WindowColor&, uint, bool*) -> void; auto renderWindow(PPU::IO::WindowColor&, uint, array<bool[256]>&) -> void;
//[unserialized] //[unserialized]
uint9 y; //constant uint9 y; //constant
IO io; IO io;
uint15 cgram[256]; array<uint15[256]> cgram;
ObjectItem items[32]; array<ObjectItem[32]> items;
ObjectTile tiles[34]; array<ObjectTile[34]> tiles;
Pixel above[256]; array<Pixel[256]> above;
Pixel below[256]; array<Pixel[256]> below;
bool windowAbove[256]; array<bool[256]> windowAbove;
bool windowBelow[256]; array<bool[256]> windowBelow;
//flush() //flush()
static uint start; static uint start;
static uint count; static uint count;
} lines[240]; };
array<Line[240]> lines;
}; };
extern PPU ppu; extern PPU ppu;

View File

@ -1,6 +1,6 @@
auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool* output) -> void { auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, array<bool[256]>& output) -> void {
if(!enable || (!self.oneEnable && !self.twoEnable)) { if(!enable || (!self.oneEnable && !self.twoEnable)) {
memory::fill<bool>(output, 256, 0); output.fill(0);
return; return;
} }
@ -32,17 +32,17 @@ auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool* outp
} }
} }
auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, bool* output) -> void { auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, array<bool[256]>& output) -> void {
bool set, clear; bool set, clear;
switch(mask) { switch(mask) {
case 0: memory::fill<bool>(output, 256, 1); return; //always case 0: output.fill(1); return; //always
case 1: set = 1, clear = 0; break; //inside case 1: set = 1, clear = 0; break; //inside
case 2: set = 0, clear = 1; break; //outside case 2: set = 0, clear = 1; break; //outside
case 3: memory::fill<bool>(output, 256, 0); return; //never case 3: output.fill(0); return; //never
} }
if(!self.oneEnable && !self.twoEnable) { if(!self.oneEnable && !self.twoEnable) {
memory::fill<bool>(output, 256, clear); output.fill(clear);
return; return;
} }

View File

@ -57,6 +57,7 @@ Program::Program(string_vector arguments) {
new InputManager; new InputManager;
new SettingsWindow; new SettingsWindow;
new CheatDatabase; new CheatDatabase;
new CheatWindow;
new ToolsWindow; new ToolsWindow;
new AboutWindow; new AboutWindow;

View File

@ -1,57 +0,0 @@
CheatDatabase::CheatDatabase() {
cheatDatabase = this;
layout.setMargin(5);
selectAllButton.setText("Select All").onActivate([&] {
for(auto item : cheatList.items()) item.setChecked(true);
});
unselectAllButton.setText("Unselect All").onActivate([&] {
for(auto item : cheatList.items()) item.setChecked(false);
});
addCheatsButton.setText("Add Cheats").onActivate([&] {
addCheats();
});
setSize({800, 400});
setAlignment({0.5, 1.0});
setDismissable();
}
auto CheatDatabase::findCheats() -> void {
auto sha256 = emulator->sha256();
auto document = BML::unserialize(string::read(locate("cheats.bml")));
for(auto game : document.find("cartridge")) {
if(game["sha256"].text() != sha256) continue;
cheatList.reset();
for(auto cheat : game.find("cheat")) {
cheatList.append(ListViewItem()
.setCheckable()
.setText(cheat["description"].text())
.setProperty("code", cheat["code"].text())
);
}
setTitle(game["name"].text());
setVisible();
return;
}
MessageDialog().setParent(*toolsWindow).setText("Sorry, no cheats were found for this game.").information();
}
auto CheatDatabase::addCheats() -> void {
for(auto item : cheatList.items()) {
if(!item.checked()) continue;
string code = item.property("code").replace("/", "=", 1L).replace("/", "?", 1L);
string description = item.text();
if(!toolsWindow->cheatEditor.addCode(false, code, description)) {
MessageDialog().setParent(*this).setText("Free slots exhausted. Not all cheats could be added.").warning();
break;
}
}
setVisible(false);
toolsWindow->cheatEditor.doRefresh();
}

View File

@ -1,139 +1,217 @@
CheatDatabase::CheatDatabase() {
cheatDatabase = this;
layout.setMargin(5);
selectAllButton.setText("Select All").onActivate([&] {
for(auto item : cheatList.items()) item.setChecked(true);
});
unselectAllButton.setText("Unselect All").onActivate([&] {
for(auto item : cheatList.items()) item.setChecked(false);
});
addCheatsButton.setText("Add Cheats").onActivate([&] {
addCheats();
});
setSize({800, 400});
setAlignment({0.5, 1.0});
setDismissable();
}
auto CheatDatabase::findCheats() -> void {
auto sha256 = emulator->sha256();
auto document = BML::unserialize(string::read(locate("cheats.bml")));
for(auto game : document.find("cartridge")) {
if(game["sha256"].text() != sha256) continue;
cheatList.reset();
for(auto cheat : game.find("cheat")) {
cheatList.append(ListViewItem()
.setCheckable()
.setText(cheat["description"].text())
.setProperty("code", cheat["code"].text())
);
}
setTitle(game["name"].text());
setVisible();
return;
}
MessageDialog().setParent(*toolsWindow).setText("Sorry, no cheats were found for this game.").information();
}
auto CheatDatabase::addCheats() -> void {
for(auto item : cheatList.items()) {
if(item.checked()) {
toolsWindow->cheatEditor.addCheat(item.text(), item.property("code"));
}
}
setVisible(false);
toolsWindow->cheatEditor.synchronizeCodes();
}
//
CheatWindow::CheatWindow() {
cheatWindow = this;
layout.setMargin(5);
nameLabel.setText("Name:");
nameValue.onChange([&] { doChange(); });
codeLabel.setText("Code:");
codeValue.onChange([&] { doChange(); });
enabledOption.setText("Enabled");
acceptButton.onActivate([&] { doAccept(); });
cancelButton.setText("Cancel");
setSize({480, layout.minimumSize().height()});
setDismissable();
}
auto CheatWindow::show(TableViewItem item) -> void {
this->item = item;
nameValue.setText(item.cell(1).text());
codeValue.setText(item.property("code"));
enabledOption.setChecked(item.cell(0).checked());
doChange();
setTitle(!item ? "Add Cheat Code" : "Edit Cheat Code");
setCentered(*toolsWindow);
setVisible();
setFocused();
nameValue.setFocused();
acceptButton.setText(!item ? "Add" : "Edit");
}
auto CheatWindow::doChange() -> void {
bool valid = true;
nameValue.setBackgroundColor(nameValue.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
codeValue.setBackgroundColor(codeValue.text().strip() ? Color{} : (valid = false, Color{255, 224, 224}));
acceptButton.setEnabled(valid);
}
auto CheatWindow::doAccept() -> void {
if(item) {
item.cell(0).setChecked(enabledOption.checked());
item.cell(1).setText(nameValue.text());
item.setProperty("code", codeValue.text());
} else {
toolsWindow->cheatEditor.addCheat(nameValue.text(), codeValue.text(), enabledOption.checked());
}
setVisible(false);
}
//
CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) { CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) {
setIcon(Icon::Edit::Replace); setIcon(Icon::Edit::Replace);
setText("Cheat Editor"); setText("Cheat Editor");
layout.setMargin(5); layout.setMargin(5);
cheatList.append(TableViewHeader().setVisible() cheatList.onActivate([&] { modifyButton.doActivate(); });
.append(TableViewColumn()) cheatList.onChange([&] {
.append(TableViewColumn().setText("Slot").setForegroundColor({0, 128, 0}).setAlignment(1.0)) auto selected = cheatList.selected();
.append(TableViewColumn().setText("Code(s)")) upButton.setEnabled((bool)selected && selected.offset() != 0);
.append(TableViewColumn().setText("Description").setExpandable()) downButton.setEnabled((bool)selected && selected.offset() != cheatList.itemCount() - 1);
); modifyButton.setEnabled((bool)selected);
for(uint slot : range(Slots)) { removeButton.setEnabled((bool)selected);
cheatList.append(TableViewItem()
.append(TableViewCell().setCheckable())
.append(TableViewCell().setText(1 + slot))
.append(TableViewCell())
.append(TableViewCell())
);
}
cheatList.onChange([&] { doChangeSelected(); });
cheatList.onToggle([&](auto cell) {
cheats[cell.parent().offset()].enabled = cell.checked();
this->synchronizeCodes();
}); });
codeLabel.setText("Code(s):"); cheatList.onToggle([&](TableViewCell) { synchronizeCodes(); });
codeValue.setEnabled(false).onChange([&] { doModify(); }); upButton.setIcon(Icon::Go::Up).onActivate([&] {
descriptionLabel.setText("Description:"); if(auto item = cheatList.selected()) {
descriptionValue.setEnabled(false).onChange([&] { doModify(); }); swap(item.offset(), item.offset() - 1);
findCodesButton.setText("Find Codes ...").onActivate([&] { cheatDatabase->findCheats(); });
resetButton.setText("Reset").onActivate([&] { doReset(); });
eraseButton.setText("Erase").onActivate([&] { doErase(); });
//do not display "Find Codes" button if there is no cheat database to look up codes in
if(!file::exists(locate("cheats.bml"))) findCodesButton.setVisible(false);
}
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(uint 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(2).setText(codes[0]);
cheatList.item(slot).cell(3).setText(cheat.description).setForegroundColor({0, 0, 0});
} else {
cheatList.item(slot).cell(0).setChecked(false);
cheatList.item(slot).cell(2).setText("");
cheatList.item(slot).cell(3).setText("<empty>").setForegroundColor({128, 128, 128});
} }
} });
cheatList.resizeColumns(); downButton.setIcon(Icon::Go::Down).onActivate([&] {
if(auto item = cheatList.selected()) {
swap(item.offset(), item.offset() + 1);
}
});
findCheatsButton.setText("Find Cheats ...").onActivate([&] {
cheatDatabase->findCheats();
});
resetButton.setText("Reset").onActivate([&] {
if(MessageDialog("Are you sure you want to permanently erase all cheat codes?").setParent(*toolsWindow).question() == "Yes") {
cheatList.reset().append(TableViewHeader()
.append(TableViewColumn())
.append(TableViewColumn().setExpandable())
.setVisible(false)
);
synchronizeCodes();
}
});
appendButton.setText("Add").onActivate([&] {
cheatWindow->show();
});
modifyButton.setText("Edit").onActivate([&] {
if(auto item = cheatList.selected()) {
cheatWindow->show(item);
}
});
removeButton.setText("Remove").onActivate([&] {
if(auto item = cheatList.selected()) {
cheatList.remove(item).doChange();
}
});
//do not display "Find Cheats" button if there is no cheat database available
if(!file::exists(locate("cheats.bml"))) findCheatsButton.setVisible(false);
} }
auto CheatEditor::doReset(bool force) -> void { auto CheatEditor::swap(uint x, uint y) -> void {
if(force || MessageDialog().setParent(*toolsWindow).setText("Permamently erase all cheats?").question() == "Yes") { auto itemX = cheatList.item(x);
for(auto& cheat : cheats) cheat = {}; auto itemY = cheatList.item(y);
for(auto& item : cheatList.items()) item.cell(0).setChecked(false); auto enabled = itemX.cell(0).checked();
doChangeSelected(); auto name = itemX.cell(1).text();
doRefresh(); auto code = itemX.property("code");
synchronizeCodes(); itemX.cell(0).setChecked(itemY.cell(0).checked());
} itemX.cell(1).setText(itemY.cell(1).text());
} itemX.setProperty("code", itemY.property("code"));
itemY.cell(0).setChecked(enabled);
auto CheatEditor::doErase() -> void { itemY.cell(1).setText(name);
if(auto item = cheatList.selected()) { itemY.setProperty("code", code);
cheats[item.offset()] = {}; itemY.setSelected();
codeValue.setText(""); cheatList.doChange();
descriptionValue.setText("");
doRefresh();
synchronizeCodes();
}
} }
auto CheatEditor::synchronizeCodes() -> void { auto CheatEditor::synchronizeCodes() -> void {
string_vector codes; string_vector codes;
for(auto& cheat : cheats) { for(auto item : cheatList.items()) {
if(!cheat.enabled || !cheat.code) continue; if(item.cell(0).checked()) codes.append(item.property("code"));
codes.append(cheat.code);
} }
emulator->cheatSet(codes); emulator->cheatSet(codes);
} }
auto CheatEditor::addCode(bool enabled, string code, string description) -> bool { auto CheatEditor::addCheat(string name, string code, bool enabled) -> void {
for(auto& cheat : cheats) { cheatList.append(TableViewItem()
if(cheat.code || cheat.description) continue; .append(TableViewCell().setChecked(enabled))
cheat.enabled = enabled; .append(TableViewCell().setText(name))
cheat.code = code; .setProperty("code", code)
cheat.description = description; ).resizeColumns();
return true;
}
return false;
} }
auto CheatEditor::loadCheats() -> void { auto CheatEditor::loadCheats() -> void {
doReset(true); cheatList.reset().append(TableViewHeader()
.append(TableViewColumn())
.append(TableViewColumn().setExpandable())
.setVisible(false)
);
auto location = program->path("Cheats", program->superNintendo.location, ".cht"); auto location = program->path("Cheats", program->superNintendo.location, ".cht");
auto document = BML::unserialize(string::read(location)); auto document = BML::unserialize(string::read(location));
for(auto cheat : document.find("cheat")) { for(auto cheat : document.find("cheat")) {
if(!addCode((bool)cheat["enabled"], cheat["code"].text(), cheat["description"].text())) break; addCheat(cheat["name"].text(), cheat["code"].text(), (bool)cheat["enabled"]);
} }
doRefresh(); cheatList.doChange();
synchronizeCodes(); synchronizeCodes();
} }
auto CheatEditor::saveCheats() -> void { auto CheatEditor::saveCheats() -> void {
string document; string document;
for(auto& cheat : cheats) { for(auto item : cheatList.items()) {
if(!cheat.code && !cheat.description) continue;
document.append("cheat\n"); document.append("cheat\n");
document.append(" description: ", cheat.description, "\n"); document.append(" name: ", item.cell(1).text(), "\n");
document.append(" code: ", cheat.code, "\n"); document.append(" code: ", item.property("code"), "\n");
if(cheat.enabled) if(item.cell(0).checked())
document.append(" enabled\n"); document.append(" enabled\n");
document.append("\n"); document.append("\n");
} }

View File

@ -1,7 +1,7 @@
#include "../bsnes.hpp" #include "../bsnes.hpp"
#include "cheat-database.cpp"
#include "cheat-editor.cpp" #include "cheat-editor.cpp"
unique_pointer<CheatDatabase> cheatDatabase; unique_pointer<CheatDatabase> cheatDatabase;
unique_pointer<CheatWindow> cheatWindow;
unique_pointer<ToolsWindow> toolsWindow; unique_pointer<ToolsWindow> toolsWindow;
ToolsWindow::ToolsWindow() { ToolsWindow::ToolsWindow() {
@ -17,10 +17,19 @@ ToolsWindow::ToolsWindow() {
onSize([&] { onSize([&] {
cheatEditor.cheatList.resizeColumns(); cheatEditor.cheatList.resizeColumns();
}); });
onClose([&] {
setVisible(false);
});
} }
auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& { auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& {
return Window::setVisible(visible), *this; Window::setVisible(visible);
if(!visible) {
cheatDatabase->setVisible(false);
cheatWindow->setVisible(false);
}
return *this;
} }
auto ToolsWindow::show(uint index) -> void { auto ToolsWindow::show(uint index) -> void {

View File

@ -13,6 +13,52 @@ public:
Button addCheatsButton{&controlLayout, Size{100, 0}}; Button addCheatsButton{&controlLayout, Size{100, 0}};
}; };
struct CheatWindow : Window {
CheatWindow();
auto show(TableViewItem item = {}) -> void;
auto doChange() -> void;
auto doAccept() -> void;
public:
TableViewItem item;
VerticalLayout layout{this};
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
Label nameLabel{&nameLayout, Size{40, 0}};
LineEdit nameValue{&nameLayout, Size{~0, 0}};
HorizontalLayout codeLayout{&layout, Size{~0, 0}};
Label codeLabel{&codeLayout, Size{40, 0}};
LineEdit codeValue{&codeLayout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget spacer{&controlLayout, Size{40, 0}};
CheckLabel enabledOption{&controlLayout, Size{~0, 0}};
Button acceptButton{&controlLayout, Size{80, 0}};
Button cancelButton{&controlLayout, Size{80, 0}};
};
struct CheatEditor : TabFrameItem {
CheatEditor(TabFrame*);
auto swap(uint x, uint y) -> void;
auto synchronizeCodes() -> void;
auto addCheat(string name, string code, bool enabled = false) -> void;
auto loadCheats() -> void;
auto saveCheats() -> void;
public:
VerticalLayout layout{this};
TableView cheatList{&layout, Size{~0, ~0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Button upButton{&controlLayout, Size{0, 0}};
Button downButton{&controlLayout, Size{0, 0}};
Button findCheatsButton{&controlLayout, Size{120, 0}};
Widget spacer{&controlLayout, Size{~0, 0}};
Button resetButton{&controlLayout, Size{80, 0}};
Button appendButton{&controlLayout, Size{80, 0}};
Button modifyButton{&controlLayout, Size{80, 0}};
Button removeButton{&controlLayout, Size{80, 0}};
};
/*
struct CheatEditor : TabFrameItem { struct CheatEditor : TabFrameItem {
enum : uint { Slots = 128 }; enum : uint { Slots = 128 };
@ -49,6 +95,7 @@ public:
Button resetButton{&controlLayout, Size{80, 0}}; Button resetButton{&controlLayout, Size{80, 0}};
Button eraseButton{&controlLayout, Size{80, 0}}; Button eraseButton{&controlLayout, Size{80, 0}};
}; };
*/
struct ToolsWindow : Window { struct ToolsWindow : Window {
ToolsWindow(); ToolsWindow();
@ -62,4 +109,5 @@ public:
}; };
extern unique_pointer<CheatDatabase> cheatDatabase; extern unique_pointer<CheatDatabase> cheatDatabase;
extern unique_pointer<CheatWindow> cheatWindow;
extern unique_pointer<ToolsWindow> toolsWindow; extern unique_pointer<ToolsWindow> toolsWindow;

View File

@ -1,6 +1,12 @@
#if defined(Hiro_ListView) #if defined(Hiro_ListView)
mListView::mListView() { mListView::mListView() {
mTableView::onActivate([&] {
doActivate();
});
mTableView::onChange([&] {
doChange();
});
mTableView::onToggle([&](TableViewCell cell) { mTableView::onToggle([&](TableViewCell cell) {
if(auto item = cell->parentTableViewItem()) { if(auto item = cell->parentTableViewItem()) {
if(auto shared = item->instance.acquire()) { if(auto shared = item->instance.acquire()) {
@ -18,6 +24,14 @@ auto mListView::batched() const -> vector<ListViewItem> {
return result; return result;
} }
auto mListView::doActivate() const -> void {
if(state.onActivate) state.onActivate();
}
auto mListView::doChange() const -> void {
if(state.onChange) state.onChange();
}
auto mListView::doToggle(ListViewItem item) const -> void { auto mListView::doToggle(ListViewItem item) const -> void {
if(state.onToggle) state.onToggle(item); if(state.onToggle) state.onToggle(item);
} }
@ -33,6 +47,16 @@ auto mListView::items() const -> vector<ListViewItem> {
return result; return result;
} }
auto mListView::onActivate(const function<void ()>& callback) -> type& {
state.onActivate = callback;
return *this;
}
auto mListView::onChange(const function<void ()>& callback) -> type& {
state.onChange = callback;
return *this;
}
auto mListView::onToggle(const function<void (ListViewItem)>& callback) -> type& { auto mListView::onToggle(const function<void (ListViewItem)>& callback) -> type& {
state.onToggle = callback; state.onToggle = callback;
return *this; return *this;

View File

@ -11,15 +11,21 @@ struct mListView : mTableView {
mListView(); mListView();
auto batched() const -> vector<ListViewItem>; auto batched() const -> vector<ListViewItem>;
auto doActivate() const -> void;
auto doChange() const -> void;
auto doToggle(ListViewItem) const -> void; auto doToggle(ListViewItem) const -> void;
auto item(uint position) const -> ListViewItem; auto item(uint position) const -> ListViewItem;
auto items() const -> vector<ListViewItem>; auto items() const -> vector<ListViewItem>;
auto onActivate(const function<void ()>& callback) -> type&;
auto onChange(const function<void ()>& callback) -> type&;
auto onToggle(const function<void (ListViewItem)>& callback) -> type&; auto onToggle(const function<void (ListViewItem)>& callback) -> type&;
auto reset() -> type& override; auto reset() -> type& override;
auto selected() const -> ListViewItem; auto selected() const -> ListViewItem;
//private: //private:
struct State { struct State {
function<void ()> onActivate;
function<void ()> onChange;
function<void (ListViewItem)> onToggle; function<void (ListViewItem)> onToggle;
} state; } state;
}; };

63
nall/adaptive-array.hpp Normal file
View File

@ -0,0 +1,63 @@
//deprecated
#pragma once
#include <nall/range.hpp>
namespace nall {
template<typename T, uint Capacity>
struct adaptive_array {
auto capacity() const -> uint { return Capacity; }
auto size() const -> uint { return _size; }
auto reset() -> void {
for(uint n : range(_size)) _pool.t[n].~T();
_size = 0;
}
auto operator[](uint index) -> T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{};
#endif
return _pool.t[index];
}
auto operator[](uint index) const -> const T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{};
#endif
return _pool.t[index];
}
auto append() -> T& {
new(_pool.t + _size) T;
return _pool.t[_size++];
}
auto append(const T& value) -> void {
new(_pool.t + _size++) T(value);
}
auto append(T&& value) -> void {
new(_pool.t + _size++) T(move(value));
}
auto begin() { return &_pool.t[0]; }
auto end() { return &_pool.t[_size]; }
auto begin() const { return &_pool.t[0]; }
auto end() const { return &_pool.t[_size]; }
private:
union U {
U() {}
~U() {}
T t[Capacity];
} _pool;
uint _size = 0;
};
}

View File

@ -1,61 +1,130 @@
#pragma once #pragma once
#define DEBUG
#include <nall/range.hpp> #include <nall/range.hpp>
namespace nall { namespace nall {
template<typename T, uint Capacity> template<typename T> struct array;
struct array {
auto capacity() const -> uint { return Capacity; }
auto size() const -> uint { return _size; }
auto reset() -> void { //usage: int x[256] => array<int, 256>
for(uint n : range(_size)) _pool.t[n].~T(); template<typename T, uint Size> struct array<T[Size]> {
_size = 0; array() = default;
array(const initializer_list<T>& source) {
uint index = 0;
for(auto& value : source) {
operator[](index++) = value;
}
} }
auto operator[](uint index) -> T& { alwaysinline auto operator[](uint index) -> T& {
#ifdef DEBUG #ifdef DEBUG
struct out_of_bounds {}; struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{}; if(index >= Size) throw out_of_bounds{};
#endif #endif
return _pool.t[index]; return values[index];
} }
auto operator[](uint index) const -> const T& { alwaysinline auto operator[](uint index) const -> const T& {
#ifdef DEBUG #ifdef DEBUG
struct out_of_bounds {}; struct out_of_bounds {};
if(index >= Capacity) throw out_of_bounds{}; if(index >= Size) throw out_of_bounds{};
#endif #endif
return _pool.t[index]; return values[index];
} }
auto append() -> T& { alwaysinline auto operator()(uint index, const T& fallback = {}) const -> const T& {
new(_pool.t + _size) T; if(index >= Size) return fallback;
return _pool.t[_size++]; return values[index];
} }
auto append(const T& value) -> void { auto fill(const T& fill = {}) {
new(_pool.t + _size++) T(value); for(auto& value : values) value = fill;
} }
auto append(T&& value) -> void { auto data() -> T* { return values; }
new(_pool.t + _size++) T(move(value)); auto data() const -> const T* { return values; }
} auto size() const -> uint { return Size; }
auto begin() { return &_pool.t[0]; } auto begin() -> T* { return &values[0]; }
auto end() { return &_pool.t[_size]; } auto end() -> T* { return &values[Size]; }
auto begin() const { return &_pool.t[0]; } auto begin() const -> const T* { return &values[0]; }
auto end() const { return &_pool.t[_size]; } auto end() const -> const T* { return &values[Size]; }
private: private:
union U { T values[Size];
U() {} };
~U() {}
T t[Capacity]; template<typename T> struct array_view;
} _pool;
uint _size = 0; template<typename T, uint Size> struct array_view<T[Size]> {
array_view() = default;
template<uint Capacity>
array_view(array<T[Capacity]>& source, uint offset = 0) {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + Size >= Capacity) throw out_of_bounds{};
#endif
values = &source.data()[offset];
}
template<uint Capacity>
array_view(T (&source)[Capacity], uint offset = 0) {
#ifdef DEBUG
struct out_of_bounds {};
if(offset + Size >= Capacity) throw out_of_bounds{};
#endif
values = &source[offset];
}
array_view(T* source, uint offset = 0) {
values = &source[offset];
}
explicit operator bool() const {
return values;
}
alwaysinline auto operator[](uint index) -> T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Size) throw out_of_bounds{};
#endif
return values[index];
}
alwaysinline auto operator[](uint index) const -> const T& {
#ifdef DEBUG
struct out_of_bounds {};
if(index >= Size) throw out_of_bounds{};
#endif
return values[index];
}
alwaysinline auto operator()(uint index, const T& fallback = {}) const -> const T& {
if(index >= Size) return fallback;
return values[index];
}
auto fill(const T& fill = {}) -> void {
for(uint index : range(Size)) values[index] = fill;
}
auto data() -> T* { return values; }
auto data() const -> const T* { return values; }
auto size() const -> uint { return Size; }
auto begin() -> T* { return &values[0]; }
auto end() -> T* { return &values[Size]; }
auto begin() const -> const T* { return &values[0]; }
auto end() const -> const T* { return &values[Size]; }
private:
T* values = nullptr;
}; };
} }

View File

@ -40,6 +40,7 @@
#include <nall/maybe.hpp> #include <nall/maybe.hpp>
#include <nall/memory.hpp> #include <nall/memory.hpp>
#include <nall/path.hpp> #include <nall/path.hpp>
#include <nall/pointer.hpp>
#include <nall/primitives.hpp> #include <nall/primitives.hpp>
#include <nall/property.hpp> #include <nall/property.hpp>
#include <nall/queue.hpp> #include <nall/queue.hpp>

34
nall/pointer.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
namespace nall {
template<typename T>
struct pointer {
explicit operator bool() const { return value; }
pointer() = default;
pointer(T* source) { value = source; }
pointer(const pointer& source) { value = source.value; }
auto& operator=(T* source) { value = source; return *this; }
auto& operator=(const pointer& source) { value = source.value; return *this; }
auto operator()() -> T* { return value; }
auto operator()() const -> const T* { return value; }
auto operator->() -> T* { return value; }
auto operator->() const -> const T* { return value; }
auto operator*() -> T& { return *value; }
auto operator*() const -> const T& { return *value; }
auto reset() -> void { value = nullptr; }
auto data() -> T* { return value; }
auto data() const -> const T* { return value; }
private:
T* value = nullptr;
};
}

View File

@ -11,6 +11,7 @@
//- only plain-old-data can be stored. complex classes must provide serialize(serializer&); //- only plain-old-data can be stored. complex classes must provide serialize(serializer&);
//- floating-point usage is not portable across different implementations //- floating-point usage is not portable across different implementations
#include <nall/array.hpp>
#include <nall/range.hpp> #include <nall/range.hpp>
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
#include <nall/traits.hpp> #include <nall/traits.hpp>
@ -96,6 +97,10 @@ struct serializer {
return *this; return *this;
} }
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
for(auto& value : array) operator()(value);
}
template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; } template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); } template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); } template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); }

View File

@ -66,8 +66,6 @@ template<typename T> inline auto pad(const T& value, long precision = 0, char pa
inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto pointer(uintptr value, long precision = 0) -> string;
template<typename T> inline auto pointer(const T* value, long precision = 0) -> string;
//match.hpp //match.hpp
inline auto tokenize(const char* s, const char* p) -> bool; inline auto tokenize(const char* s, const char* p) -> bool;

View File

@ -234,6 +234,22 @@ template<> struct stringify<const string_view&> {
const string_view& _view; const string_view& _view;
}; };
//pointers
template<typename T> struct stringify<T*> {
stringify(const T* source) {
if(!source) {
memory::copy(_data, "(nullptr)", 10);
} else {
memory::copy(_data, "0x", 2);
fromNatural(_data + 2, (uintptr)source);
}
}
auto data() const -> const char* { return _data; }
auto size() const -> uint { return strlen(_data); }
char _data[256];
};
// //
template<typename T> auto make_string(T value) -> stringify<T> { template<typename T> auto make_string(T value) -> stringify<T> {

View File

@ -133,14 +133,4 @@ auto binary(uintmax value, long precision, char padchar) -> string {
return buffer; return buffer;
} }
auto pointer(uintptr value, long precision) -> string {
if(value == 0) return "(nullptr)";
return {"0x", hex(value, precision)};
}
template<typename T> auto pointer(const T* value, long precision) -> string {
if(value == nullptr) return "(nullptr)";
return {"0x", hex((uintptr)value, precision)};
}
} }