mirror of https://github.com/bsnes-emu/bsnes.git
v111.8
Serialize SDD1 decompressor Major speedup to nall/serializer [Alcaro] Removed fast PPU tile cache (major speedup for run-ahead mode)
This commit is contained in:
parent
53f8de6ac3
commit
6b7e6e01bb
|
@ -42,8 +42,6 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||||
flags += -fPIC
|
flags += -fPIC
|
||||||
options += -shared
|
options += -shared
|
||||||
endif
|
endif
|
||||||
else
|
|
||||||
$(error "unsupported platform")
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
objects := libco emulator filter lzma
|
objects := libco emulator filter lzma
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libco/libco.h>
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/adaptive-array.hpp>
|
#include <nall/adaptive-array.hpp>
|
||||||
#include <nall/any.hpp>
|
#include <nall/any.hpp>
|
||||||
|
@ -20,8 +22,6 @@
|
||||||
#include <nall/hash/sha256.hpp>
|
#include <nall/hash/sha256.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
#include <libco/libco.h>
|
|
||||||
|
|
||||||
#include <emulator/types.hpp>
|
#include <emulator/types.hpp>
|
||||||
#include <emulator/memory/readable.hpp>
|
#include <emulator/memory/readable.hpp>
|
||||||
#include <emulator/memory/writable.hpp>
|
#include <emulator/memory/writable.hpp>
|
||||||
|
@ -29,7 +29,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "bsnes";
|
static const string Name = "bsnes";
|
||||||
static const string Version = "111.8";
|
static const string Version = "111.9";
|
||||||
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";
|
||||||
|
|
|
@ -3,6 +3,7 @@ struct Decompressor {
|
||||||
IM(SDD1::Decompressor& self) : self(self) {}
|
IM(SDD1::Decompressor& self) : self(self) {}
|
||||||
auto init(uint offset) -> void;
|
auto init(uint offset) -> void;
|
||||||
auto getCodeWord(uint8 codeLength) -> uint8;
|
auto getCodeWord(uint8 codeLength) -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -13,6 +14,7 @@ struct Decompressor {
|
||||||
struct GCD { //golomb-code decoder
|
struct GCD { //golomb-code decoder
|
||||||
GCD(SDD1::Decompressor& self) : self(self) {}
|
GCD(SDD1::Decompressor& self) : self(self) {}
|
||||||
auto getRunCount(uint8 codeNumber, uint8& mpsCount, bool& lpsIndex) -> void;
|
auto getRunCount(uint8 codeNumber, uint8& mpsCount, bool& lpsIndex) -> void;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -23,6 +25,7 @@ struct Decompressor {
|
||||||
BG(SDD1::Decompressor& self, uint8 codeNumber) : self(self), codeNumber(codeNumber) {}
|
BG(SDD1::Decompressor& self, uint8 codeNumber) : self(self), codeNumber(codeNumber) {}
|
||||||
auto init() -> void;
|
auto init() -> void;
|
||||||
auto getBit(bool& endOfRun) -> uint8;
|
auto getBit(bool& endOfRun) -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -35,6 +38,7 @@ struct Decompressor {
|
||||||
PEM(SDD1::Decompressor& self) : self(self) {}
|
PEM(SDD1::Decompressor& self) : self(self) {}
|
||||||
auto init() -> void;
|
auto init() -> void;
|
||||||
auto getBit(uint8 context) -> uint8;
|
auto getBit(uint8 context) -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -54,6 +58,7 @@ struct Decompressor {
|
||||||
CM(SDD1::Decompressor& self) : self(self) {}
|
CM(SDD1::Decompressor& self) : self(self) {}
|
||||||
auto init(uint offset) -> void;
|
auto init(uint offset) -> void;
|
||||||
auto getBit() -> uint8;
|
auto getBit() -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -68,6 +73,7 @@ struct Decompressor {
|
||||||
OL(SDD1::Decompressor& self) : self(self) {}
|
OL(SDD1::Decompressor& self) : self(self) {}
|
||||||
auto init(uint offset) -> void;
|
auto init(uint offset) -> void;
|
||||||
auto decompress() -> uint8;
|
auto decompress() -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Decompressor& self;
|
Decompressor& self;
|
||||||
|
@ -78,6 +84,7 @@ struct Decompressor {
|
||||||
Decompressor();
|
Decompressor();
|
||||||
auto init(uint offset) -> void;
|
auto init(uint offset) -> void;
|
||||||
auto read() -> uint8;
|
auto read() -> uint8;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
IM im;
|
IM im;
|
||||||
GCD gcd;
|
GCD gcd;
|
||||||
|
|
|
@ -11,4 +11,56 @@ auto SDD1::serialize(serializer& s) -> void {
|
||||||
s.integer(dma[n].size);
|
s.integer(dma[n].size);
|
||||||
}
|
}
|
||||||
s.integer(dmaReady);
|
s.integer(dmaReady);
|
||||||
|
|
||||||
|
decompressor.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::serialize(serializer& s) -> void {
|
||||||
|
im.serialize(s);
|
||||||
|
gcd.serialize(s);
|
||||||
|
bg0.serialize(s);
|
||||||
|
bg1.serialize(s);
|
||||||
|
bg2.serialize(s);
|
||||||
|
bg3.serialize(s);
|
||||||
|
bg4.serialize(s);
|
||||||
|
bg5.serialize(s);
|
||||||
|
bg6.serialize(s);
|
||||||
|
bg7.serialize(s);
|
||||||
|
cm.serialize(s);
|
||||||
|
ol.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::IM::serialize(serializer& s) -> void {
|
||||||
|
s.integer(offset);
|
||||||
|
s.integer(bitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::GCD::serialize(serializer& s) -> void {
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::BG::serialize(serializer& s) -> void {
|
||||||
|
s.integer(mpsCount);
|
||||||
|
s.integer(lpsIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::PEM::serialize(serializer& s) -> void {
|
||||||
|
for(auto& info : contextInfo) {
|
||||||
|
s.integer(info.status);
|
||||||
|
s.integer(info.mps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::CM::serialize(serializer& s) -> void {
|
||||||
|
s.integer(bitplanesInfo);
|
||||||
|
s.integer(contextBitsInfo);
|
||||||
|
s.integer(bitNumber);
|
||||||
|
s.integer(currentBitplane);
|
||||||
|
s.array(previousBitplaneBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SDD1::Decompressor::OL::serialize(serializer& s) -> void {
|
||||||
|
s.integer(bitplanesInfo);
|
||||||
|
s.integer(r0);
|
||||||
|
s.integer(r1);
|
||||||
|
s.integer(r2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi
|
||||||
bool hires = io.bgMode == 5 || io.bgMode == 6;
|
bool hires = io.bgMode == 5 || io.bgMode == 6;
|
||||||
bool offsetPerTileMode = io.bgMode == 2 || io.bgMode == 4 || io.bgMode == 6;
|
bool offsetPerTileMode = io.bgMode == 2 || io.bgMode == 4 || io.bgMode == 6;
|
||||||
bool directColorMode = io.col.directColor && source == Source::BG1 && (io.bgMode == 3 || io.bgMode == 4);
|
bool directColorMode = io.col.directColor && source == Source::BG1 && (io.bgMode == 3 || io.bgMode == 4);
|
||||||
|
uint colorShift = 3 + self.tileMode;
|
||||||
int width = 256 << hires;
|
int width = 256 << hires;
|
||||||
|
|
||||||
uint tileHeight = 3 + self.tileSize;
|
uint tileHeight = 3 + self.tileSize;
|
||||||
|
@ -91,14 +92,36 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi
|
||||||
if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16;
|
if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16;
|
||||||
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
|
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
|
||||||
|
|
||||||
auto tiledata = ppu.tilecache[self.tileMode] + (tileNumber << 6);
|
uint16 address;
|
||||||
tiledata += (voffset & 7 ^ mirrorY) << 3;
|
address = (tileNumber << colorShift) + (voffset & 7 ^ mirrorY);
|
||||||
|
|
||||||
|
uint64 data;
|
||||||
|
data = (uint64)ppu.vram[address + 0] << 0;
|
||||||
|
data |= (uint64)ppu.vram[address + 8] << 16;
|
||||||
|
data |= (uint64)ppu.vram[address + 16] << 32;
|
||||||
|
data |= (uint64)ppu.vram[address + 24] << 48;
|
||||||
|
|
||||||
for(uint tileX = 0; tileX < 8; tileX++, x++) {
|
for(uint tileX = 0; tileX < 8; tileX++, x++) {
|
||||||
if(x & width) continue; //x < 0 || x >= width
|
if(x & width) continue; //x < 0 || x >= width
|
||||||
if(!self.mosaicEnable || --mosaicCounter == 0) {
|
if(!self.mosaicEnable || --mosaicCounter == 0) {
|
||||||
|
uint color, shift = mirrorX ? tileX : 7 - tileX;
|
||||||
|
/*if(self.tileMode >= TileMode::BPP2)*/ {
|
||||||
|
color = data >> shift + 0 & 1;
|
||||||
|
color += data >> shift + 7 & 2;
|
||||||
|
}
|
||||||
|
if(self.tileMode >= TileMode::BPP4) {
|
||||||
|
color += data >> shift + 14 & 4;
|
||||||
|
color += data >> shift + 21 & 8;
|
||||||
|
}
|
||||||
|
if(self.tileMode >= TileMode::BPP8) {
|
||||||
|
color += data >> shift + 28 & 16;
|
||||||
|
color += data >> shift + 35 & 32;
|
||||||
|
color += data >> shift + 42 & 64;
|
||||||
|
color += data >> shift + 49 & 128;
|
||||||
|
}
|
||||||
|
|
||||||
mosaicCounter = 1 + io.mosaicSize;
|
mosaicCounter = 1 + io.mosaicSize;
|
||||||
mosaicPalette = tiledata[tileX ^ mirrorX];
|
mosaicPalette = color;
|
||||||
mosaicPriority = tilePriority;
|
mosaicPriority = tilePriority;
|
||||||
if(directColorMode) {
|
if(directColorMode) {
|
||||||
mosaicColor = directColor(paletteNumber, mosaicPalette);
|
mosaicColor = directColor(paletteNumber, mosaicPalette);
|
||||||
|
|
|
@ -42,6 +42,7 @@ auto PPU::writeVRAM(uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::updateTiledata(uint address) -> void {
|
auto PPU::updateTiledata(uint address) -> void {
|
||||||
|
/*
|
||||||
auto word = vram[address & 0x7fff];
|
auto word = vram[address & 0x7fff];
|
||||||
auto line2bpp = tilecache[TileMode::BPP2] + (address << 3 & 0x3fff8);
|
auto line2bpp = tilecache[TileMode::BPP2] + (address << 3 & 0x3fff8);
|
||||||
auto line4bpp = tilecache[TileMode::BPP4] + (address << 2 & 0x1ffc0) + (address << 3 & 0x38);
|
auto line4bpp = tilecache[TileMode::BPP4] + (address << 2 & 0x1ffc0) + (address << 3 & 0x38);
|
||||||
|
@ -53,6 +54,7 @@ auto PPU::updateTiledata(uint address) -> void {
|
||||||
line4bpp[7 - x] = line4bpp[7 - x] & ~(3 << plane4bpp) | (word >> x & 1) << plane4bpp | (word >> x + 7 & 2) << plane4bpp;
|
line4bpp[7 - x] = line4bpp[7 - x] & ~(3 << plane4bpp) | (word >> x & 1) << plane4bpp | (word >> x + 7 & 2) << plane4bpp;
|
||||||
line8bpp[7 - x] = line8bpp[7 - x] & ~(3 << plane8bpp) | (word >> x & 1) << plane8bpp | (word >> x + 7 & 2) << plane8bpp;
|
line8bpp[7 - x] = line8bpp[7 - x] & ~(3 << plane8bpp) | (word >> x & 1) << plane8bpp | (word >> x + 7 & 2) << plane8bpp;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::readOAM(uint10 address) -> uint8 {
|
auto PPU::readOAM(uint10 address) -> uint8 {
|
||||||
|
|
|
@ -83,7 +83,9 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
||||||
|
|
||||||
uint mirrorX = !object.hflip ? tileX : tileWidth - 1 - tileX;
|
uint mirrorX = !object.hflip ? tileX : tileWidth - 1 - tileX;
|
||||||
uint address = tiledataAddress + ((characterY + (characterX + mirrorX & 15)) << 4);
|
uint address = tiledataAddress + ((characterY + (characterX + mirrorX & 15)) << 4);
|
||||||
tile.number = address >> 4 & 0x7ff;
|
address = (address & 0x7ff0) + (y & 7);
|
||||||
|
tile.data = ppu.vram[address + 0] << 0;
|
||||||
|
tile.data |= ppu.vram[address + 8] << 16;
|
||||||
|
|
||||||
if(tileCount++ >= ppu.TileLimit) break;
|
if(tileCount++ >= ppu.TileLimit) break;
|
||||||
tiles[tileCount - 1] = tile;
|
tiles[tileCount - 1] = tile;
|
||||||
|
@ -97,16 +99,19 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
||||||
uint8_t priority[256] = {};
|
uint8_t priority[256] = {};
|
||||||
|
|
||||||
for(uint n : range(ppu.TileLimit)) {
|
for(uint n : range(ppu.TileLimit)) {
|
||||||
const auto& tile = tiles[n];
|
auto& tile = tiles[n];
|
||||||
if(!tile.valid) continue;
|
if(!tile.valid) continue;
|
||||||
|
|
||||||
auto tiledata = ppu.tilecache[TileMode::BPP4] + (tile.number << 6) + ((tile.y & 7) << 3);
|
|
||||||
uint tileX = tile.x;
|
uint tileX = tile.x;
|
||||||
uint mirrorX = tile.hflip ? 7 : 0;
|
|
||||||
for(uint x : range(8)) {
|
for(uint x : range(8)) {
|
||||||
tileX &= 511;
|
tileX &= 511;
|
||||||
if(tileX < 256) {
|
if(tileX < 256) {
|
||||||
if(uint color = tiledata[x ^ mirrorX]) {
|
uint color, shift = tile.hflip ? x : 7 - x;
|
||||||
|
color = tile.data >> shift + 0 & 1;
|
||||||
|
color += tile.data >> shift + 7 & 2;
|
||||||
|
color += tile.data >> shift + 14 & 4;
|
||||||
|
color += tile.data >> shift + 21 & 8;
|
||||||
|
if(color) {
|
||||||
palette[tileX] = tile.palette + color;
|
palette[tileX] = tile.palette + color;
|
||||||
priority[tileX] = self.priority[tile.priority];
|
priority[tileX] = self.priority[tile.priority];
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,10 +51,6 @@ PPU::PPU() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tilecache[TileMode::BPP2] = new uint8_t[4096 * 8 * 8]();
|
|
||||||
tilecache[TileMode::BPP4] = new uint8_t[2048 * 8 * 8]();
|
|
||||||
tilecache[TileMode::BPP8] = new uint8_t[1024 * 8 * 8]();
|
|
||||||
|
|
||||||
for(uint y : range(240)) {
|
for(uint y : range(240)) {
|
||||||
lines[y].y = y;
|
lines[y].y = y;
|
||||||
}
|
}
|
||||||
|
@ -63,9 +59,6 @@ PPU::PPU() {
|
||||||
PPU::~PPU() {
|
PPU::~PPU() {
|
||||||
delete[] output;
|
delete[] output;
|
||||||
for(uint l : range(16)) delete[] lightTable[l];
|
for(uint l : range(16)) delete[] lightTable[l];
|
||||||
delete[] tilecache[TileMode::BPP2];
|
|
||||||
delete[] tilecache[TileMode::BPP4];
|
|
||||||
delete[] tilecache[TileMode::BPP8];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::synchronizeCPU() -> void {
|
auto PPU::synchronizeCPU() -> void {
|
||||||
|
|
|
@ -235,7 +235,7 @@ public:
|
||||||
uint8 priority = 0;
|
uint8 priority = 0;
|
||||||
uint8 palette = 0;
|
uint8 palette = 0;
|
||||||
bool hflip = 0;
|
bool hflip = 0;
|
||||||
uint16 number = 0;
|
uint32 data = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Pixel {
|
struct Pixel {
|
||||||
|
@ -276,7 +276,6 @@ public:
|
||||||
//[unserialized]
|
//[unserialized]
|
||||||
uint16* output = {};
|
uint16* output = {};
|
||||||
uint16* lightTable[16] = {};
|
uint16* lightTable[16] = {};
|
||||||
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
|
|
||||||
|
|
||||||
uint ItemLimit = 0;
|
uint ItemLimit = 0;
|
||||||
uint TileLimit = 0;
|
uint TileLimit = 0;
|
||||||
|
|
|
@ -8,8 +8,6 @@ auto PPU::serialize(serializer& s) -> void {
|
||||||
s.array(cgram);
|
s.array(cgram);
|
||||||
for(auto& object : objects) object.serialize(s);
|
for(auto& object : objects) object.serialize(s);
|
||||||
|
|
||||||
if (s.mode() == s.Load)
|
|
||||||
for(auto address : range(32768)) updateTiledata(address);
|
|
||||||
Line::start = 0;
|
Line::start = 0;
|
||||||
Line::count = 0;
|
Line::count = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
auto System::serialize(bool synchronize) -> serializer {
|
auto System::serialize(bool synchronize) -> serializer {
|
||||||
|
//deterministic serialization (synchronize=false) is only possible with select libco methods.
|
||||||
|
if(co_method() != "x86"
|
||||||
|
&& co_method() != "amd64"
|
||||||
|
&& co_method() != "arm"
|
||||||
|
&& co_method() != "aarch64"
|
||||||
|
) synchronize = true;
|
||||||
|
|
||||||
if(!information.serializeSize[synchronize]) return {}; //should never occur
|
if(!information.serializeSize[synchronize]) return {}; //should never occur
|
||||||
if(synchronize) runToSave();
|
if(synchronize) runToSave();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,14 @@ local := false
|
||||||
openmp := true
|
openmp := true
|
||||||
flags += -Wno-narrowing -Wno-multichar -g -fPIC
|
flags += -Wno-narrowing -Wno-multichar -g -fPIC
|
||||||
|
|
||||||
|
ifeq ($(platform), ios-arm64)
|
||||||
|
flags += -fPIC -miphoneos-version-min=11.0 -Wno-error=implicit-function-declaration -DHAVE_POSIX_MEMALIGN
|
||||||
|
options += -dynamiclib
|
||||||
|
else ifeq ($(platform), tvos-arm64)
|
||||||
|
flags += -fPIC -mtvos-version-min=11.0 -Wno-error=implicit-function-declaration -DHAVE_POSIX_MEMALIGN
|
||||||
|
options += -dynamiclib
|
||||||
|
endif
|
||||||
|
|
||||||
objects := libretro $(objects)
|
objects := libretro $(objects)
|
||||||
objects := $(patsubst %,obj/%.o,$(objects))
|
objects := $(patsubst %,obj/%.o,$(objects))
|
||||||
|
|
||||||
|
@ -10,9 +18,9 @@ obj/libretro.o: target-libretro/libretro.cpp
|
||||||
|
|
||||||
all: $(objects)
|
all: $(objects)
|
||||||
ifeq ($(platform),linux)
|
ifeq ($(platform),linux)
|
||||||
$(strip $(compiler) -o out/$(name).so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -Wl,-Bdynamic $(options))
|
$(strip $(compiler) -o out/$(name).so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
|
||||||
else ifeq ($(platform),windows)
|
else ifeq ($(platform),windows)
|
||||||
$(strip $(compiler) -o out/$(name).dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -Wl,-Bdynamic $(options))
|
$(strip $(compiler) -o out/$(name).dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
|
||||||
else ifeq ($(platform),macos)
|
else ifeq ($(platform),macos)
|
||||||
$(strip $(compiler) -o out/$(name).dylib -shared $(objects) $(options))
|
$(strip $(compiler) -o out/$(name).dylib -shared $(objects) $(options))
|
||||||
else ifeq ($(platform), ios-arm64)
|
else ifeq ($(platform), ios-arm64)
|
||||||
|
|
|
@ -490,38 +490,18 @@ RETRO_API void retro_run()
|
||||||
|
|
||||||
RETRO_API size_t retro_serialize_size()
|
RETRO_API size_t retro_serialize_size()
|
||||||
{
|
{
|
||||||
// To avoid having to serialize twice to query the size -> serialize.
|
return emulator->serialize().size();
|
||||||
if (program->has_cached_serialize)
|
|
||||||
{
|
|
||||||
return program->cached_serialize.size();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
program->cached_serialize = emulator->serialize();
|
|
||||||
program->has_cached_serialize = true;
|
|
||||||
return program->cached_serialize.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RETRO_API bool retro_serialize(void *data, size_t size)
|
RETRO_API bool retro_serialize(void *data, size_t size)
|
||||||
{
|
{
|
||||||
if (!program->has_cached_serialize)
|
memcpy(data, emulator->serialize().data(), size);
|
||||||
{
|
|
||||||
program->cached_serialize = emulator->serialize();
|
|
||||||
program->has_cached_serialize = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program->cached_serialize.size() != size)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memcpy(data, program->cached_serialize.data(), size);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RETRO_API bool retro_unserialize(const void *data, size_t size)
|
RETRO_API bool retro_unserialize(const void *data, size_t size)
|
||||||
{
|
{
|
||||||
serializer s(static_cast<const uint8_t *>(data), size);
|
serializer s(static_cast<const uint8_t *>(data), size);
|
||||||
program->has_cached_serialize = false;
|
|
||||||
return emulator->unserialize(s);
|
return emulator->unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,6 @@ struct Program : Emulator::Platform
|
||||||
|
|
||||||
auto hackPatchMemory(vector<uint8_t>& data) -> void;
|
auto hackPatchMemory(vector<uint8_t>& data) -> void;
|
||||||
|
|
||||||
serializer cached_serialize;
|
|
||||||
bool has_cached_serialize = false;
|
|
||||||
|
|
||||||
string base_name;
|
string base_name;
|
||||||
|
|
||||||
bool overscan = false;
|
bool overscan = false;
|
||||||
|
|
|
@ -59,6 +59,10 @@ static void co_init() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "aarch64";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
return co_active_handle;
|
return co_active_handle;
|
||||||
|
@ -73,7 +77,8 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
|
|
||||||
if(handle = (unsigned long*)memory) {
|
if(handle = (unsigned long*)memory) {
|
||||||
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
|
unsigned int offset = (size & ~15);
|
||||||
|
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
|
||||||
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
|
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
|
||||||
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
|
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
|
||||||
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
|
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
|
||||||
|
@ -83,23 +88,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||||
unsigned long* handle;
|
void* memory = malloc(size);
|
||||||
if(!co_swap) {
|
if(!memory) return (cothread_t)0;
|
||||||
co_init();
|
return co_derive(memory, size, entrypoint);
|
||||||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
|
||||||
}
|
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
|
||||||
size += 256;
|
|
||||||
size &= ~15;
|
|
||||||
|
|
||||||
if(handle = (unsigned long*)malloc(size)) {
|
|
||||||
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
|
|
||||||
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
|
|
||||||
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
|
|
||||||
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void co_delete(cothread_t handle) {
|
void co_delete(cothread_t handle) {
|
||||||
|
|
|
@ -115,6 +115,10 @@ static void crash() {
|
||||||
assert(0); /* called only if cothread_t entrypoint returns */
|
assert(0); /* called only if cothread_t entrypoint returns */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "amd64";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
return co_active_handle;
|
return co_active_handle;
|
||||||
|
@ -140,22 +144,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||||
cothread_t handle;
|
void* memory = malloc(size);
|
||||||
if(!co_swap) {
|
if(!memory) return (cothread_t)0;
|
||||||
co_init();
|
return co_derive(memory, size, entrypoint);
|
||||||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
|
||||||
}
|
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
|
||||||
|
|
||||||
if(handle = (cothread_t)malloc(size)) {
|
|
||||||
unsigned int offset = (size & ~15) - 32;
|
|
||||||
long long *p = (long long*)((char*)handle + offset); /* seek to top of stack */
|
|
||||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
|
||||||
*--p = (long long)entrypoint; /* start of function */
|
|
||||||
*(long long*)handle = (long long)p; /* stack pointer */
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void co_delete(cothread_t handle) {
|
void co_delete(cothread_t handle) {
|
||||||
|
|
26
libco/arm.c
26
libco/arm.c
|
@ -35,6 +35,10 @@ static void co_init() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "arm";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
return co_active_handle;
|
return co_active_handle;
|
||||||
|
@ -49,7 +53,8 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
|
|
||||||
if(handle = (unsigned long*)memory) {
|
if(handle = (unsigned long*)memory) {
|
||||||
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
|
unsigned int offset = (size & ~15);
|
||||||
|
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
|
||||||
handle[8] = (unsigned long)p;
|
handle[8] = (unsigned long)p;
|
||||||
handle[9] = (unsigned long)entrypoint;
|
handle[9] = (unsigned long)entrypoint;
|
||||||
}
|
}
|
||||||
|
@ -58,22 +63,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||||
unsigned long* handle;
|
void* memory = malloc(size);
|
||||||
if(!co_swap) {
|
if(!memory) return (cothread_t)0;
|
||||||
co_init();
|
return co_derive(memory, size, entrypoint);
|
||||||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
|
||||||
}
|
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
|
||||||
size += 256;
|
|
||||||
size &= ~15;
|
|
||||||
|
|
||||||
if(handle = (unsigned long*)malloc(size)) {
|
|
||||||
unsigned long* p = (unsigned long*)((unsigned char*)handle + size);
|
|
||||||
handle[8] = (unsigned long)p;
|
|
||||||
handle[9] = (unsigned long)entrypoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void co_delete(cothread_t handle) {
|
void co_delete(cothread_t handle) {
|
||||||
|
|
|
@ -16,6 +16,10 @@ static void __stdcall co_thunk(void* coentry) {
|
||||||
((void (*)(void))coentry)();
|
((void (*)(void))coentry)();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "fiber";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_) {
|
if(!co_active_) {
|
||||||
ConvertThreadToFiber(0);
|
ConvertThreadToFiber(0);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic ignored "-Wparentheses"
|
#pragma clang diagnostic ignored "-Wparentheses"
|
||||||
|
|
||||||
//placing code in section(text) does not mark it executable with Clang.
|
/* placing code in section(text) does not mark it executable with Clang. */
|
||||||
#undef LIBCO_MPROTECT
|
#undef LIBCO_MPROTECT
|
||||||
#define LIBCO_MPROTECT
|
#define LIBCO_MPROTECT
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
libco v20 (2019-10-14)
|
libco v20 (2019-10-16)
|
||||||
author: byuu
|
author: byuu
|
||||||
license: ISC
|
license: ISC
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +13,7 @@ extern "C" {
|
||||||
|
|
||||||
typedef void* cothread_t;
|
typedef void* cothread_t;
|
||||||
|
|
||||||
|
const char* co_method();
|
||||||
cothread_t co_active();
|
cothread_t co_active();
|
||||||
cothread_t co_derive(void*, unsigned int, void (*)(void));
|
cothread_t co_derive(void*, unsigned int, void (*)(void));
|
||||||
cothread_t co_create(unsigned int, void (*)(void));
|
cothread_t co_create(unsigned int, void (*)(void));
|
||||||
|
|
|
@ -413,6 +413,10 @@ static void co_init_(void) {
|
||||||
co_active_handle = co_create_(state_size, (uintptr_t)&co_switch);
|
co_active_handle = co_create_(state_size, (uintptr_t)&co_switch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "ppc";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) co_init_();
|
if(!co_active_handle) co_init_();
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,10 @@ __asm__(
|
||||||
".size swap_context, .-swap_context\n"
|
".size swap_context, .-swap_context\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "ppc64v2";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) {
|
if(!co_active_handle) {
|
||||||
co_active_handle = (struct ppc64_context*)malloc(MIN_STACK + sizeof(struct ppc64_context));
|
co_active_handle = (struct ppc64_context*)malloc(MIN_STACK + sizeof(struct ppc64_context));
|
||||||
|
@ -255,11 +259,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*coentry)(void)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
|
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
|
||||||
size_t total = MAX(size, MIN_STACK) + sizeof(struct ppc64_context);
|
void* memory = malloc(size);
|
||||||
void* memory = malloc(total);
|
|
||||||
|
|
||||||
if(!memory) return (cothread_t)0;
|
if(!memory) return (cothread_t)0;
|
||||||
return co_derive(memory, total, coentry);
|
return co_derive(memory, size, coentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void co_delete(cothread_t handle) {
|
void co_delete(cothread_t handle) {
|
||||||
|
|
|
@ -33,6 +33,10 @@ static void springboard(int ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "sjlj";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_running) co_running = &co_primary;
|
if(!co_running) co_running = &co_primary;
|
||||||
return (cothread_t)co_running;
|
return (cothread_t)co_running;
|
||||||
|
|
|
@ -26,6 +26,10 @@ extern "C" {
|
||||||
static thread_local ucontext_t co_primary;
|
static thread_local ucontext_t co_primary;
|
||||||
static thread_local ucontext_t* co_running = 0;
|
static thread_local ucontext_t* co_running = 0;
|
||||||
|
|
||||||
|
const char* co_module() {
|
||||||
|
return "ucontext";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_running) co_running = &co_primary;
|
if(!co_running) co_running = &co_primary;
|
||||||
return (cothread_t)co_running;
|
return (cothread_t)co_running;
|
||||||
|
|
23
libco/x86.c
23
libco/x86.c
|
@ -69,6 +69,10 @@ static void crash() {
|
||||||
assert(0); /* called only if cothread_t entrypoint returns */
|
assert(0); /* called only if cothread_t entrypoint returns */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* co_method() {
|
||||||
|
return "x86";
|
||||||
|
}
|
||||||
|
|
||||||
cothread_t co_active() {
|
cothread_t co_active() {
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||||
return co_active_handle;
|
return co_active_handle;
|
||||||
|
@ -94,22 +98,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||||
cothread_t handle;
|
void* memory = malloc(size);
|
||||||
if(!co_swap) {
|
if(!memory) return (cothread_t)0;
|
||||||
co_init();
|
return co_derive(memory, size, entrypoint);
|
||||||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
|
||||||
}
|
|
||||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
|
||||||
|
|
||||||
if(handle = (cothread_t)malloc(size)) {
|
|
||||||
unsigned int offset = (size & ~15) - 32;
|
|
||||||
long *p = (long*)((char*)handle + offset); /* seek to top of stack */
|
|
||||||
*--p = (long)crash; /* crash if entrypoint returns */
|
|
||||||
*--p = (long)entrypoint; /* start of function */
|
|
||||||
*(long*)handle = (long)p; /* stack pointer */
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void co_delete(cothread_t handle) {
|
void co_delete(cothread_t handle) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nall::DSP::Resampler {
|
||||||
struct Cubic {
|
struct Cubic {
|
||||||
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
|
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
|
||||||
inline auto setInputFrequency(double inputFrequency) -> void;
|
inline auto setInputFrequency(double inputFrequency) -> void;
|
||||||
inline auto pending() const -> uint;
|
inline auto pending() const -> bool;
|
||||||
inline auto read() -> double;
|
inline auto read() -> double;
|
||||||
inline auto write(double sample) -> void;
|
inline auto write(double sample) -> void;
|
||||||
inline auto serialize(serializer&) -> void;
|
inline auto serialize(serializer&) -> void;
|
||||||
|
@ -39,7 +39,7 @@ auto Cubic::setInputFrequency(double inputFrequency) -> void {
|
||||||
ratio = inputFrequency / outputFrequency;
|
ratio = inputFrequency / outputFrequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cubic::pending() const -> uint {
|
auto Cubic::pending() const -> bool {
|
||||||
return samples.pending();
|
return samples.pending();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -241,11 +241,26 @@ auto Response::setFile(const string& value) -> type& {
|
||||||
}
|
}
|
||||||
if(!valid) return *this;
|
if(!valid) return *this;
|
||||||
|
|
||||||
|
//cache images for seven days
|
||||||
|
auto suffix = Location::suffix(value);
|
||||||
|
uint maxAge = 0;
|
||||||
|
if(suffix == ".svg"
|
||||||
|
|| suffix == ".ico"
|
||||||
|
|| suffix == ".png"
|
||||||
|
|| suffix == ".gif"
|
||||||
|
|| suffix == ".jpg"
|
||||||
|
|| suffix == ".jpeg") {
|
||||||
|
maxAge = 7 * 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
_file = value;
|
_file = value;
|
||||||
string eTag = {"\"", chrono::utc::datetime(file::timestamp(value, file::time::modify)), "\""};
|
|
||||||
header.assign("Content-Length", file::size(value));
|
header.assign("Content-Length", file::size(value));
|
||||||
header.assign("Cache-Control", "public");
|
header.assign("ETag", {"\"", chrono::utc::datetime(file::timestamp(value, file::time::modify)), "\""});
|
||||||
header.assign("ETag", eTag);
|
if(maxAge == 0) {
|
||||||
|
header.assign("Cache-Control", {"public"});
|
||||||
|
} else {
|
||||||
|
header.assign("Cache-Control", {"public, max-age=", maxAge});
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ struct queue {
|
||||||
template<typename U = T> auto capacity() const -> uint { return _capacity * sizeof(T) / sizeof(U); }
|
template<typename U = T> auto capacity() const -> uint { return _capacity * sizeof(T) / sizeof(U); }
|
||||||
template<typename U = T> auto size() const -> uint { return _size * sizeof(T) / sizeof(U); }
|
template<typename U = T> auto size() const -> uint { return _size * sizeof(T) / sizeof(U); }
|
||||||
auto empty() const -> bool { return _size == 0; }
|
auto empty() const -> bool { return _size == 0; }
|
||||||
auto pending() const -> uint { return _size; }
|
auto pending() const -> bool { return _size > 0; }
|
||||||
auto full() const -> bool { return _size >= (int)_capacity; }
|
auto full() const -> bool { return _size >= (int)_capacity; }
|
||||||
auto underflow() const -> bool { return _size < 0; }
|
auto underflow() const -> bool { return _size < 0; }
|
||||||
auto overflow() const -> bool { return _size > (int)_capacity; }
|
auto overflow() const -> bool { return _size > (int)_capacity; }
|
||||||
|
|
|
@ -51,13 +51,9 @@ struct serializer {
|
||||||
return _capacity;
|
return _capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto setMode(Mode mode) -> bool {
|
auto setMode(Mode mode) -> void {
|
||||||
if(_mode == Mode::Save && mode == Mode::Load) {
|
|
||||||
_mode = mode;
|
_mode = mode;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> auto real(T& value) -> serializer& {
|
template<typename T> auto real(T& value) -> serializer& {
|
||||||
|
@ -110,35 +106,38 @@ struct serializer {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int N> auto array(uint8_t (&array_)[N]) -> serializer& {
|
|
||||||
array(array_, N);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
auto array(uint8_t* array, uint size) -> serializer& {
|
|
||||||
if(_mode == Save) {
|
|
||||||
memcpy(_data+_size, array, size);
|
|
||||||
} else if(_mode == Load) {
|
|
||||||
memcpy(array, _data+_size, size);
|
|
||||||
}
|
|
||||||
_size += size;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#ifdef ENDIAN_LSB
|
|
||||||
template<int N> auto array(uint16_t (&array_)[N]) -> serializer& {
|
|
||||||
array(array_, N);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
auto array(uint16_t* array_, uint size) -> serializer& {
|
|
||||||
array((uint8_t*)array_, size*2);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
|
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
|
||||||
for(auto& value : array) operator()(value);
|
for(auto& value : array) operator()(value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//optimized specializations
|
||||||
|
|
||||||
|
auto array(uint8_t* data, uint size) -> serializer& {
|
||||||
|
if(_mode == Save) {
|
||||||
|
memory::copy(_data + _size, data, size);
|
||||||
|
} else if(_mode == Load) {
|
||||||
|
memory::copy(data, _data + _size, size);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
_size += size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int N> auto array(uint8_t (&data)[N]) -> serializer& {
|
||||||
|
return array(data, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
//nall/serializer saves data in little-endian ordering
|
||||||
|
#if defined(ENDIAN_LSB)
|
||||||
|
auto array(uint16_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint16_t)); }
|
||||||
|
auto array(uint32_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint32_t)); }
|
||||||
|
auto array(uint64_t* data, uint size) -> serializer& { return array((uint8_t*)data, size * sizeof(uint64_t)); }
|
||||||
|
template<int N> auto array(uint16_t (&data)[N]) -> serializer& { return array(data, N); }
|
||||||
|
template<int N> auto array(uint32_t (&data)[N]) -> serializer& { return array(data, N); }
|
||||||
|
template<int N> auto array(uint64_t (&data)[N]) -> serializer& { return array(data, N); }
|
||||||
|
#endif
|
||||||
|
|
||||||
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 real(value); }
|
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return real(value); }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/* Document Markup Language (DML) v1.0 parser
|
/* Document Markup Language (DML) v1.0 parser
|
||||||
* revision 0.04
|
* revision 0.05
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <nall/location.hpp>
|
#include <nall/location.hpp>
|
||||||
|
@ -9,6 +9,11 @@
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
struct DML {
|
struct DML {
|
||||||
|
inline auto title() const -> string { return state.title; }
|
||||||
|
inline auto subtitle() const -> string { return state.subtitle; }
|
||||||
|
inline auto description() const -> string { return state.description; }
|
||||||
|
inline auto content() const -> string { return state.output; }
|
||||||
|
|
||||||
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
|
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
|
||||||
auto& setHost(const string& hostname) { settings.host = {hostname, "/"}; return *this; }
|
auto& setHost(const string& hostname) { settings.host = {hostname, "/"}; return *this; }
|
||||||
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
|
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
|
||||||
|
@ -28,6 +33,9 @@ private:
|
||||||
} settings;
|
} settings;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
string title;
|
||||||
|
string subtitle;
|
||||||
|
string description;
|
||||||
string output;
|
string output;
|
||||||
uint sections = 0;
|
uint sections = 0;
|
||||||
} state;
|
} state;
|
||||||
|
@ -41,12 +49,14 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
inline auto DML::parse(const string& filedata, const string& pathname) -> string {
|
inline auto DML::parse(const string& filedata, const string& pathname) -> string {
|
||||||
|
state = {};
|
||||||
settings.path = pathname;
|
settings.path = pathname;
|
||||||
parseDocument(filedata, settings.path, 0);
|
parseDocument(filedata, settings.path, 0);
|
||||||
return state.output;
|
return state.output;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto DML::parse(const string& filename) -> string {
|
inline auto DML::parse(const string& filename) -> string {
|
||||||
|
state = {};
|
||||||
if(!settings.path) settings.path = Location::path(filename);
|
if(!settings.path) settings.path = Location::path(filename);
|
||||||
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
string document = settings.reader ? settings.reader(filename) : string::read(filename);
|
||||||
parseDocument(document, settings.path, 0);
|
parseDocument(document, settings.path, 0);
|
||||||
|
@ -83,8 +93,8 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||||
|
|
||||||
//title
|
//title
|
||||||
else if(block.beginsWith("! ")) {
|
else if(block.beginsWith("! ")) {
|
||||||
auto name = lines.takeLeft().trimLeft("! ", 1L);
|
state.title = lines.takeLeft().trimLeft("! ", 1L);
|
||||||
state.output.append("<h1>", markup(name));
|
state.output.append("<h1>", markup(state.title));
|
||||||
for(auto& line : lines) {
|
for(auto& line : lines) {
|
||||||
if(!line.beginsWith("! ")) continue;
|
if(!line.beginsWith("! ")) continue;
|
||||||
state.output.append("<span>", markup(line.trimLeft("! ", 1L)), "</span>");
|
state.output.append("<span>", markup(line.trimLeft("! ", 1L)), "</span>");
|
||||||
|
@ -92,6 +102,14 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||||
state.output.append("</h1>\n");
|
state.output.append("</h1>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//description
|
||||||
|
else if(block.beginsWith("? ")) {
|
||||||
|
while(lines) {
|
||||||
|
state.description.append(lines.takeLeft().trimLeft("? ", 1L), " ");
|
||||||
|
}
|
||||||
|
state.description.strip();
|
||||||
|
}
|
||||||
|
|
||||||
//section
|
//section
|
||||||
else if(block.beginsWith("# ")) {
|
else if(block.beginsWith("# ")) {
|
||||||
if(settings.sectioned) {
|
if(settings.sectioned) {
|
||||||
|
@ -101,6 +119,7 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||||
auto content = lines.takeLeft().trimLeft("# ", 1L).split("::", 1L).strip();
|
auto content = lines.takeLeft().trimLeft("# ", 1L).split("::", 1L).strip();
|
||||||
auto data = markup(content[0]);
|
auto data = markup(content[0]);
|
||||||
auto name = escape(content(1, data.hash()));
|
auto name = escape(content(1, data.hash()));
|
||||||
|
state.subtitle = content[0];
|
||||||
state.output.append("<h2 id=\"", name, "\">", data);
|
state.output.append("<h2 id=\"", name, "\">", data);
|
||||||
for(auto& line : lines) {
|
for(auto& line : lines) {
|
||||||
if(!line.beginsWith("# ")) continue;
|
if(!line.beginsWith("# ")) continue;
|
||||||
|
@ -226,13 +245,14 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
|
|
||||||
natural link, linkBase;
|
natural link, linkBase;
|
||||||
natural embed, embedBase;
|
natural embed, embedBase;
|
||||||
|
natural photo, photoBase;
|
||||||
natural iframe, iframeBase;
|
natural iframe, iframeBase;
|
||||||
|
|
||||||
for(uint n = 0; n < s.size();) {
|
for(uint n = 0; n < s.size();) {
|
||||||
char a = s[n];
|
char a = s[n];
|
||||||
char b = s[n + 1];
|
char b = s[n + 1];
|
||||||
|
|
||||||
if(!link && !embed && !iframe) {
|
if(!link && !embed && !photo && !iframe) {
|
||||||
if(a == '*' && b == '*') { t.append(strong.flip() ? "<strong>" : "</strong>"); n += 2; continue; }
|
if(a == '*' && b == '*') { t.append(strong.flip() ? "<strong>" : "</strong>"); n += 2; continue; }
|
||||||
if(a == '/' && b == '/') { t.append(emphasis.flip() ? "<em>" : "</em>"); n += 2; continue; }
|
if(a == '/' && b == '/') { t.append(emphasis.flip() ? "<em>" : "</em>"); n += 2; continue; }
|
||||||
if(a == '_' && b == '_') { t.append(insertion.flip() ? "<ins>" : "</ins>"); n += 2; continue; }
|
if(a == '_' && b == '_') { t.append(insertion.flip() ? "<ins>" : "</ins>"); n += 2; continue; }
|
||||||
|
@ -244,7 +264,15 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
if(iframe == 0 && a == '<' && b == '<') { t.append("<iframe width='772' height='434' src=\""); iframe = 1; iframeBase = n += 2; continue; }
|
if(iframe == 0 && a == '<' && b == '<') { t.append("<iframe width='772' height='434' src=\""); iframe = 1; iframeBase = n += 2; continue; }
|
||||||
if(iframe != 0 && a == '>' && b == '>') { t.append("\" frameborder='0' allowfullscreen></iframe>"); iframe = 0; n += 2; continue; }
|
if(iframe != 0 && a == '>' && b == '>') { t.append("\" frameborder='0' allowfullscreen></iframe>"); iframe = 0; n += 2; continue; }
|
||||||
|
|
||||||
if(!embed) {
|
if(!embed && !link) {
|
||||||
|
if(photo == 0 && a == '[' && b == '{') { t.append("<a href=\""); photo = 1; photoBase = n += 2; continue; }
|
||||||
|
if(photo == 1 && a == '}' && b == ']') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"><img src=\"", slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\" alt=\"\"></a>"); n += 2; photo = 0; continue; }
|
||||||
|
if(photo == 1 && a == ':' && b == ':') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"><img src=\"", slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\" alt=\""); photo = 2; photoBase = n += 2; continue; }
|
||||||
|
if(photo == 2 && a == '}' && b == ']') { t.append(slice(s, photoBase, n - photoBase).replace("@/", settings.host), "\"></a>"); n += 2; photo = 0; continue; }
|
||||||
|
if(photo != 0) { n++; continue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!photo && !embed) {
|
||||||
if(link == 0 && a == '[' && b == '[') { t.append("<a href=\""); link = 1; linkBase = n += 2; continue; }
|
if(link == 0 && a == '[' && b == '[') { t.append("<a href=\""); link = 1; linkBase = n += 2; continue; }
|
||||||
if(link == 1 && a == ':' && b == ':') { t.append("\">"); link = 2; n += 2; continue; }
|
if(link == 1 && a == ':' && b == ':') { t.append("\">"); link = 2; n += 2; continue; }
|
||||||
if(link == 1 && a == ']' && b == ']') { t.append("\">", slice(s, linkBase, n - linkBase), "</a>"); n += 2; link = 0; continue; }
|
if(link == 1 && a == ']' && b == ']') { t.append("\">", slice(s, linkBase, n - linkBase), "</a>"); n += 2; link = 0; continue; }
|
||||||
|
@ -252,7 +280,7 @@ inline auto DML::markup(const string& s) -> string {
|
||||||
if(link == 1 && a == '@' && b == '/') { t.append(settings.host); n += 2; continue; }
|
if(link == 1 && a == '@' && b == '/') { t.append(settings.host); n += 2; continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!link) {
|
if(!photo && !link) {
|
||||||
if(embed == 0 && a == '{' && b == '{') { t.append("<img src=\""); embed = 1; embedBase = n += 2; continue; }
|
if(embed == 0 && a == '{' && b == '{') { t.append("<img src=\""); embed = 1; embedBase = n += 2; continue; }
|
||||||
if(embed == 1 && a == ':' && b == ':') { t.append("\" alt=\""); embed = 2; n += 2; continue; }
|
if(embed == 1 && a == ':' && b == ':') { t.append("\" alt=\""); embed = 2; n += 2; continue; }
|
||||||
if(embed != 0 && a == '}' && b == '}') { t.append("\">"); embed = 0; n += 2; continue; }
|
if(embed != 0 && a == '}' && b == '}') { t.append("\">"); embed = 0; n += 2; continue; }
|
||||||
|
|
Loading…
Reference in New Issue