mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
77ac5f9e88
commit
ec9729a9e1
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/adaptive-array.hpp>
|
||||
#include <nall/vfs.hpp>
|
||||
using namespace nall;
|
||||
|
||||
|
@ -12,7 +13,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -140,8 +140,8 @@ struct VDP : Thread {
|
|||
|
||||
Pixel output;
|
||||
|
||||
array<Object, 80> oam;
|
||||
array<Object, 20> objects;
|
||||
adaptive_array<Object, 80> oam;
|
||||
adaptive_array<Object, 20> objects;
|
||||
};
|
||||
Sprite sprite;
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ struct VDP : Thread {
|
|||
uint4 color;
|
||||
} output;
|
||||
|
||||
array<Object, 8> objects;
|
||||
adaptive_array<Object, 8> objects;
|
||||
} sprite;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -170,7 +170,7 @@ private:
|
|||
bool vflip = 0;
|
||||
bool first = 0;
|
||||
};
|
||||
array<Object, 64> objects;
|
||||
adaptive_array<Object, 64> objects;
|
||||
|
||||
uint4 color;
|
||||
uint4 palette;
|
||||
|
|
|
@ -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::Inactive) return;
|
||||
|
||||
bool windowAbove[256];
|
||||
bool windowBelow[256];
|
||||
array<bool[256]> windowAbove;
|
||||
array<bool[256]> windowBelow;
|
||||
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
||||
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
||||
|
||||
|
|
|
@ -589,11 +589,11 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::BPP2;
|
||||
io.bg4.tileMode = TileMode::BPP2;
|
||||
memory::assign(io.bg1.priority, 8, 11);
|
||||
memory::assign(io.bg2.priority, 7, 10);
|
||||
memory::assign(io.bg3.priority, 2, 5);
|
||||
memory::assign(io.bg4.priority, 1, 4);
|
||||
memory::assign(io.obj.priority, 3, 6, 9, 12);
|
||||
io.bg1.priority = { 8, 11};
|
||||
io.bg2.priority = { 7, 10};
|
||||
io.bg3.priority = { 2, 5};
|
||||
io.bg4.priority = { 1, 4};
|
||||
io.obj.priority = { 3, 6, 9, 12};
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
@ -602,15 +602,15 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg3.tileMode = TileMode::BPP2;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
if(io.bgPriority) {
|
||||
memory::assign(io.bg1.priority, 5, 8);
|
||||
memory::assign(io.bg2.priority, 4, 7);
|
||||
memory::assign(io.bg3.priority, 1, 10);
|
||||
memory::assign(io.obj.priority, 2, 3, 6, 9);
|
||||
io.bg1.priority = { 5, 8};
|
||||
io.bg2.priority = { 4, 7};
|
||||
io.bg3.priority = { 1, 10};
|
||||
io.obj.priority = { 2, 3, 6, 9};
|
||||
} else {
|
||||
memory::assign(io.bg1.priority, 6, 9);
|
||||
memory::assign(io.bg2.priority, 5, 8);
|
||||
memory::assign(io.bg3.priority, 1, 3);
|
||||
memory::assign(io.obj.priority, 2, 4, 7, 10);
|
||||
io.bg1.priority = { 6, 9};
|
||||
io.bg2.priority = { 5, 8};
|
||||
io.bg3.priority = { 1, 3};
|
||||
io.obj.priority = { 2, 4, 7, 10};
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -619,9 +619,9 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::BPP4;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
io.bg1.priority = { 3, 7};
|
||||
io.bg2.priority = { 1, 5};
|
||||
io.obj.priority = { 2, 4, 6, 8};
|
||||
break;
|
||||
|
||||
case 3:
|
||||
|
@ -629,9 +629,9 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::BPP4;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
io.bg1.priority = { 3, 7};
|
||||
io.bg2.priority = { 1, 5};
|
||||
io.obj.priority = { 2, 4, 6, 8};
|
||||
break;
|
||||
|
||||
case 4:
|
||||
|
@ -639,9 +639,9 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
io.bg1.priority = { 3, 7};
|
||||
io.bg2.priority = { 1, 5};
|
||||
io.obj.priority = { 2, 4, 6, 8};
|
||||
break;
|
||||
|
||||
case 5:
|
||||
|
@ -649,9 +649,9 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
io.bg1.priority = { 3, 7};
|
||||
io.bg2.priority = { 1, 5};
|
||||
io.obj.priority = { 2, 4, 6, 8};
|
||||
break;
|
||||
|
||||
case 6:
|
||||
|
@ -659,8 +659,8 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::Inactive;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 2, 5);
|
||||
memory::assign(io.obj.priority, 1, 3, 4, 6);
|
||||
io.bg1.priority = { 2, 5};
|
||||
io.obj.priority = { 1, 3, 4, 6};
|
||||
break;
|
||||
|
||||
case 7:
|
||||
|
@ -669,16 +669,16 @@ auto PPU::updateVideoMode() -> void {
|
|||
io.bg2.tileMode = TileMode::Inactive;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 2);
|
||||
memory::assign(io.obj.priority, 1, 3, 4, 5);
|
||||
io.bg1.priority = { 2};
|
||||
io.obj.priority = { 1, 3, 4, 5};
|
||||
} else {
|
||||
io.bg1.tileMode = TileMode::Mode7;
|
||||
io.bg2.tileMode = TileMode::Mode7;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 7);
|
||||
io.bg1.priority = { 3};
|
||||
io.bg2.priority = { 1, 5};
|
||||
io.obj.priority = { 2, 4, 6, 7};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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 originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8);
|
||||
|
||||
bool windowAbove[256];
|
||||
bool windowBelow[256];
|
||||
array<bool[256]> windowAbove;
|
||||
array<bool[256]> windowBelow;
|
||||
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
||||
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
||||
if(!self.aboveEnable && !self.belowEnable) return;
|
||||
|
||||
bool windowAbove[256];
|
||||
bool windowBelow[256];
|
||||
array<bool[256]> windowAbove;
|
||||
array<bool[256]> windowBelow;
|
||||
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
||||
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
||||
|
||||
|
|
|
@ -13,21 +13,21 @@ PPU ppu;
|
|||
#include <sfc/ppu/counter/serialization.cpp>
|
||||
|
||||
PPU::PPU() {
|
||||
output = new uint32[512 * 512];
|
||||
output += 16 * 512; //overscan offset
|
||||
|
||||
output = new uint32[512 * 512] + 16 * 512; //overscan offset
|
||||
tilecache[TileMode::BPP2] = new uint8[4096 * 8 * 8];
|
||||
tilecache[TileMode::BPP4] = new uint8[2048 * 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;
|
||||
}
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
output -= 16 * 512; //overscan offset
|
||||
delete[] output;
|
||||
delete[] (output - 16 * 512); //overscan offset
|
||||
delete[] tilecache[TileMode::BPP2];
|
||||
delete[] tilecache[TileMode::BPP4];
|
||||
delete[] tilecache[TileMode::BPP8];
|
||||
}
|
||||
|
||||
auto PPU::Enter() -> void {
|
||||
|
|
|
@ -35,9 +35,9 @@ public:
|
|||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint1 interlace;
|
||||
uint1 overscan;
|
||||
uint1 hires;
|
||||
uint1 interlace;
|
||||
uint1 overscan;
|
||||
uint1 hires;
|
||||
|
||||
uint16 vram;
|
||||
uint8 oam;
|
||||
|
@ -153,7 +153,7 @@ public:
|
|||
uint16 hoffset;
|
||||
uint16 voffset;
|
||||
uint3 tileMode;
|
||||
uint4 priority[2];
|
||||
array<uint4[2]> priority;
|
||||
} bg1, bg2, bg3, bg4;
|
||||
|
||||
struct Object {
|
||||
|
@ -170,7 +170,7 @@ public:
|
|||
uint7 first;
|
||||
uint1 rangeOver;
|
||||
uint1 timeOver;
|
||||
uint4 priority[4];
|
||||
array<uint4[4]> priority;
|
||||
} obj;
|
||||
|
||||
struct Color {
|
||||
|
@ -178,7 +178,7 @@ public:
|
|||
auto serialize(serializer&) -> void;
|
||||
|
||||
WindowColor window;
|
||||
uint1 enable[7];
|
||||
array<uint1[7]> enable;
|
||||
uint1 directColor;
|
||||
uint1 blendMode; //0 = fixed; 1 = pixel
|
||||
uint1 halve;
|
||||
|
@ -249,13 +249,13 @@ public:
|
|||
Latch latch;
|
||||
IO io;
|
||||
|
||||
uint16 vram[32 * 1024];
|
||||
uint15 cgram[256];
|
||||
Object objects[128];
|
||||
array<uint16[32 * 1024]> vram;
|
||||
array<uint15[256]> cgram;
|
||||
array<Object[128]> objects;
|
||||
|
||||
//[unserialized]
|
||||
uint32* output = nullptr;
|
||||
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
|
||||
uint32* output;
|
||||
array<uint8*[3]> tilecache; //bitplane -> bitmap tiledata
|
||||
|
||||
struct Line {
|
||||
//line.cpp
|
||||
|
@ -278,28 +278,29 @@ public:
|
|||
auto renderObject(PPU::IO::Object&) -> void;
|
||||
|
||||
//window.cpp
|
||||
auto renderWindow(PPU::IO::WindowLayer&, bool, bool*) -> void;
|
||||
auto renderWindow(PPU::IO::WindowColor&, uint, bool*) -> void;
|
||||
auto renderWindow(PPU::IO::WindowLayer&, bool, array<bool[256]>&) -> void;
|
||||
auto renderWindow(PPU::IO::WindowColor&, uint, array<bool[256]>&) -> void;
|
||||
|
||||
//[unserialized]
|
||||
uint9 y; //constant
|
||||
|
||||
IO io;
|
||||
uint15 cgram[256];
|
||||
array<uint15[256]> cgram;
|
||||
|
||||
ObjectItem items[32];
|
||||
ObjectTile tiles[34];
|
||||
array<ObjectItem[32]> items;
|
||||
array<ObjectTile[34]> tiles;
|
||||
|
||||
Pixel above[256];
|
||||
Pixel below[256];
|
||||
array<Pixel[256]> above;
|
||||
array<Pixel[256]> below;
|
||||
|
||||
bool windowAbove[256];
|
||||
bool windowBelow[256];
|
||||
array<bool[256]> windowAbove;
|
||||
array<bool[256]> windowBelow;
|
||||
|
||||
//flush()
|
||||
static uint start;
|
||||
static uint count;
|
||||
} lines[240];
|
||||
};
|
||||
array<Line[240]> lines;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
||||
|
|
|
@ -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)) {
|
||||
memory::fill<bool>(output, 256, 0);
|
||||
output.fill(0);
|
||||
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;
|
||||
switch(mask) {
|
||||
case 0: memory::fill<bool>(output, 256, 1); return; //always
|
||||
case 1: set = 1, clear = 0; break; //inside
|
||||
case 2: set = 0, clear = 1; break; //outside
|
||||
case 3: memory::fill<bool>(output, 256, 0); return; //never
|
||||
case 0: output.fill(1); return; //always
|
||||
case 1: set = 1, clear = 0; break; //inside
|
||||
case 2: set = 0, clear = 1; break; //outside
|
||||
case 3: output.fill(0); return; //never
|
||||
}
|
||||
|
||||
if(!self.oneEnable && !self.twoEnable) {
|
||||
memory::fill<bool>(output, 256, clear);
|
||||
output.fill(clear);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ Program::Program(string_vector arguments) {
|
|||
new InputManager;
|
||||
new SettingsWindow;
|
||||
new CheatDatabase;
|
||||
new CheatWindow;
|
||||
new ToolsWindow;
|
||||
new AboutWindow;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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) {
|
||||
setIcon(Icon::Edit::Replace);
|
||||
setText("Cheat Editor");
|
||||
|
||||
layout.setMargin(5);
|
||||
cheatList.append(TableViewHeader().setVisible()
|
||||
.append(TableViewColumn())
|
||||
.append(TableViewColumn().setText("Slot").setForegroundColor({0, 128, 0}).setAlignment(1.0))
|
||||
.append(TableViewColumn().setText("Code(s)"))
|
||||
.append(TableViewColumn().setText("Description").setExpandable())
|
||||
);
|
||||
for(uint slot : range(Slots)) {
|
||||
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();
|
||||
cheatList.onActivate([&] { modifyButton.doActivate(); });
|
||||
cheatList.onChange([&] {
|
||||
auto selected = cheatList.selected();
|
||||
upButton.setEnabled((bool)selected && selected.offset() != 0);
|
||||
downButton.setEnabled((bool)selected && selected.offset() != cheatList.itemCount() - 1);
|
||||
modifyButton.setEnabled((bool)selected);
|
||||
removeButton.setEnabled((bool)selected);
|
||||
});
|
||||
codeLabel.setText("Code(s):");
|
||||
codeValue.setEnabled(false).onChange([&] { doModify(); });
|
||||
descriptionLabel.setText("Description:");
|
||||
descriptionValue.setEnabled(false).onChange([&] { doModify(); });
|
||||
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.onToggle([&](TableViewCell) { synchronizeCodes(); });
|
||||
upButton.setIcon(Icon::Go::Up).onActivate([&] {
|
||||
if(auto item = cheatList.selected()) {
|
||||
swap(item.offset(), item.offset() - 1);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if(force || MessageDialog().setParent(*toolsWindow).setText("Permamently erase all cheats?").question() == "Yes") {
|
||||
for(auto& cheat : cheats) cheat = {};
|
||||
for(auto& item : cheatList.items()) item.cell(0).setChecked(false);
|
||||
doChangeSelected();
|
||||
doRefresh();
|
||||
synchronizeCodes();
|
||||
}
|
||||
}
|
||||
|
||||
auto CheatEditor::doErase() -> void {
|
||||
if(auto item = cheatList.selected()) {
|
||||
cheats[item.offset()] = {};
|
||||
codeValue.setText("");
|
||||
descriptionValue.setText("");
|
||||
doRefresh();
|
||||
synchronizeCodes();
|
||||
}
|
||||
auto CheatEditor::swap(uint x, uint y) -> void {
|
||||
auto itemX = cheatList.item(x);
|
||||
auto itemY = cheatList.item(y);
|
||||
auto enabled = itemX.cell(0).checked();
|
||||
auto name = itemX.cell(1).text();
|
||||
auto code = itemX.property("code");
|
||||
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);
|
||||
itemY.cell(1).setText(name);
|
||||
itemY.setProperty("code", code);
|
||||
itemY.setSelected();
|
||||
cheatList.doChange();
|
||||
}
|
||||
|
||||
auto CheatEditor::synchronizeCodes() -> void {
|
||||
string_vector codes;
|
||||
for(auto& cheat : cheats) {
|
||||
if(!cheat.enabled || !cheat.code) continue;
|
||||
codes.append(cheat.code);
|
||||
for(auto item : cheatList.items()) {
|
||||
if(item.cell(0).checked()) codes.append(item.property("code"));
|
||||
}
|
||||
emulator->cheatSet(codes);
|
||||
}
|
||||
|
||||
auto CheatEditor::addCode(bool enabled, string code, string description) -> 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::addCheat(string name, string code, bool enabled) -> void {
|
||||
cheatList.append(TableViewItem()
|
||||
.append(TableViewCell().setChecked(enabled))
|
||||
.append(TableViewCell().setText(name))
|
||||
.setProperty("code", code)
|
||||
).resizeColumns();
|
||||
}
|
||||
|
||||
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 document = BML::unserialize(string::read(location));
|
||||
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();
|
||||
}
|
||||
|
||||
auto CheatEditor::saveCheats() -> void {
|
||||
string document;
|
||||
for(auto& cheat : cheats) {
|
||||
if(!cheat.code && !cheat.description) continue;
|
||||
for(auto item : cheatList.items()) {
|
||||
document.append("cheat\n");
|
||||
document.append(" description: ", cheat.description, "\n");
|
||||
document.append(" code: ", cheat.code, "\n");
|
||||
if(cheat.enabled)
|
||||
document.append(" name: ", item.cell(1).text(), "\n");
|
||||
document.append(" code: ", item.property("code"), "\n");
|
||||
if(item.cell(0).checked())
|
||||
document.append(" enabled\n");
|
||||
document.append("\n");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "../bsnes.hpp"
|
||||
#include "cheat-database.cpp"
|
||||
#include "cheat-editor.cpp"
|
||||
unique_pointer<CheatDatabase> cheatDatabase;
|
||||
unique_pointer<CheatWindow> cheatWindow;
|
||||
unique_pointer<ToolsWindow> toolsWindow;
|
||||
|
||||
ToolsWindow::ToolsWindow() {
|
||||
|
@ -17,10 +17,19 @@ ToolsWindow::ToolsWindow() {
|
|||
onSize([&] {
|
||||
cheatEditor.cheatList.resizeColumns();
|
||||
});
|
||||
|
||||
onClose([&] {
|
||||
setVisible(false);
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -13,6 +13,52 @@ public:
|
|||
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 {
|
||||
enum : uint { Slots = 128 };
|
||||
|
||||
|
@ -49,6 +95,7 @@ public:
|
|||
Button resetButton{&controlLayout, Size{80, 0}};
|
||||
Button eraseButton{&controlLayout, Size{80, 0}};
|
||||
};
|
||||
*/
|
||||
|
||||
struct ToolsWindow : Window {
|
||||
ToolsWindow();
|
||||
|
@ -62,4 +109,5 @@ public:
|
|||
};
|
||||
|
||||
extern unique_pointer<CheatDatabase> cheatDatabase;
|
||||
extern unique_pointer<CheatWindow> cheatWindow;
|
||||
extern unique_pointer<ToolsWindow> toolsWindow;
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#if defined(Hiro_ListView)
|
||||
|
||||
mListView::mListView() {
|
||||
mTableView::onActivate([&] {
|
||||
doActivate();
|
||||
});
|
||||
mTableView::onChange([&] {
|
||||
doChange();
|
||||
});
|
||||
mTableView::onToggle([&](TableViewCell cell) {
|
||||
if(auto item = cell->parentTableViewItem()) {
|
||||
if(auto shared = item->instance.acquire()) {
|
||||
|
@ -18,6 +24,14 @@ auto mListView::batched() const -> vector<ListViewItem> {
|
|||
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 {
|
||||
if(state.onToggle) state.onToggle(item);
|
||||
}
|
||||
|
@ -33,6 +47,16 @@ auto mListView::items() const -> vector<ListViewItem> {
|
|||
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& {
|
||||
state.onToggle = callback;
|
||||
return *this;
|
||||
|
|
|
@ -11,15 +11,21 @@ struct mListView : mTableView {
|
|||
|
||||
mListView();
|
||||
auto batched() const -> vector<ListViewItem>;
|
||||
auto doActivate() const -> void;
|
||||
auto doChange() const -> void;
|
||||
auto doToggle(ListViewItem) const -> void;
|
||||
auto item(uint position) const -> 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 reset() -> type& override;
|
||||
auto selected() const -> ListViewItem;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
function<void ()> onActivate;
|
||||
function<void ()> onChange;
|
||||
function<void (ListViewItem)> onToggle;
|
||||
} state;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
131
nall/array.hpp
131
nall/array.hpp
|
@ -1,61 +1,130 @@
|
|||
#pragma once
|
||||
#define DEBUG
|
||||
|
||||
#include <nall/range.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
template<typename T, uint Capacity>
|
||||
struct array {
|
||||
auto capacity() const -> uint { return Capacity; }
|
||||
auto size() const -> uint { return _size; }
|
||||
template<typename T> struct array;
|
||||
|
||||
auto reset() -> void {
|
||||
for(uint n : range(_size)) _pool.t[n].~T();
|
||||
_size = 0;
|
||||
//usage: int x[256] => array<int, 256>
|
||||
template<typename T, uint Size> struct array<T[Size]> {
|
||||
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
|
||||
struct out_of_bounds {};
|
||||
if(index >= Capacity) throw out_of_bounds{};
|
||||
if(index >= Size) throw out_of_bounds{};
|
||||
#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
|
||||
struct out_of_bounds {};
|
||||
if(index >= Capacity) throw out_of_bounds{};
|
||||
if(index >= Size) throw out_of_bounds{};
|
||||
#endif
|
||||
return _pool.t[index];
|
||||
return values[index];
|
||||
}
|
||||
|
||||
auto append() -> T& {
|
||||
new(_pool.t + _size) T;
|
||||
return _pool.t[_size++];
|
||||
alwaysinline auto operator()(uint index, const T& fallback = {}) const -> const T& {
|
||||
if(index >= Size) return fallback;
|
||||
return values[index];
|
||||
}
|
||||
|
||||
auto append(const T& value) -> void {
|
||||
new(_pool.t + _size++) T(value);
|
||||
auto fill(const T& fill = {}) {
|
||||
for(auto& value : values) value = fill;
|
||||
}
|
||||
|
||||
auto append(T&& value) -> void {
|
||||
new(_pool.t + _size++) T(move(value));
|
||||
}
|
||||
auto data() -> T* { return values; }
|
||||
auto data() const -> const T* { return values; }
|
||||
auto size() const -> uint { return Size; }
|
||||
|
||||
auto begin() { return &_pool.t[0]; }
|
||||
auto end() { return &_pool.t[_size]; }
|
||||
auto begin() -> T* { return &values[0]; }
|
||||
auto end() -> T* { return &values[Size]; }
|
||||
|
||||
auto begin() const { return &_pool.t[0]; }
|
||||
auto end() const { return &_pool.t[_size]; }
|
||||
auto begin() const -> const T* { return &values[0]; }
|
||||
auto end() const -> const T* { return &values[Size]; }
|
||||
|
||||
private:
|
||||
union U {
|
||||
U() {}
|
||||
~U() {}
|
||||
T t[Capacity];
|
||||
} _pool;
|
||||
uint _size = 0;
|
||||
T values[Size];
|
||||
};
|
||||
|
||||
template<typename T> struct array_view;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <nall/maybe.hpp>
|
||||
#include <nall/memory.hpp>
|
||||
#include <nall/path.hpp>
|
||||
#include <nall/pointer.hpp>
|
||||
#include <nall/primitives.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/queue.hpp>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
//- only plain-old-data can be stored. complex classes must provide serialize(serializer&);
|
||||
//- floating-point usage is not portable across different implementations
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/range.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
|
@ -96,6 +97,10 @@ struct serializer {
|
|||
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<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); }
|
||||
|
|
|
@ -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 octal(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
|
||||
inline auto tokenize(const char* s, const char* p) -> bool;
|
||||
|
|
|
@ -234,6 +234,22 @@ template<> struct stringify<const string_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> {
|
||||
|
|
|
@ -133,14 +133,4 @@ auto binary(uintmax value, long precision, char padchar) -> string {
|
|||
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)};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue