mirror of https://github.com/bsnes-emu/bsnes.git
Update to v095r07 release.
byuu says: Changelog: - entire GBA core ported to auto function() -> return; syntax - fixed GBA BLDY bug that was causing flickering in a few games - replaced nall/config usage with nall/string/markup/node - this merges all configuration files to a unified settings.bml file - added "Ignore Manifests" option to the advanced setting tab - this lets you keep a manifest.bml for an older version of higan; if you want to do regression testing Be sure to remap your controller/hotkey inputs, and for SNES, choose "Gamepad" from "Controller Port 1" in the system menu. Otherwise you won't get any input. No need to blow away your old config files, unless you want to.
This commit is contained in:
parent
40f4b91000
commit
41c478ac4a
|
@ -7,7 +7,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "095.06";
|
||||
static const string Version = "095.07";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace GameBoyAdvance {
|
|||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
void APU::Enter() {
|
||||
auto APU::Enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
|
@ -24,23 +24,23 @@ void APU::Enter() {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::main() {
|
||||
for(unsigned n = 0; n < 64; n++) {
|
||||
auto APU::main() -> void {
|
||||
for(auto n : range(64)) {
|
||||
runsequencer();
|
||||
}
|
||||
|
||||
signed lsample = regs.bias.level - 0x0200;
|
||||
signed rsample = regs.bias.level - 0x0200;
|
||||
int lsample = regs.bias.level - 0x0200;
|
||||
int rsample = regs.bias.level - 0x0200;
|
||||
|
||||
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
||||
if(sequencer.masterenable) {
|
||||
signed lsequence = 0;
|
||||
int lsequence = 0;
|
||||
if(sequencer.lenable[0]) lsequence += square1.output;
|
||||
if(sequencer.lenable[1]) lsequence += square2.output;
|
||||
if(sequencer.lenable[2]) lsequence += wave.output;
|
||||
if(sequencer.lenable[3]) lsequence += noise.output;
|
||||
|
||||
signed rsequence = 0;
|
||||
int rsequence = 0;
|
||||
if(sequencer.renable[0]) rsequence += square1.output;
|
||||
if(sequencer.renable[1]) rsequence += square2.output;
|
||||
if(sequencer.renable[2]) rsequence += wave.output;
|
||||
|
@ -53,8 +53,8 @@ void APU::main() {
|
|||
}
|
||||
|
||||
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
||||
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||
int fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||
int fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||
|
||||
if(fifo[0].lenable) lsample += fifo0;
|
||||
if(fifo[1].lenable) lsample += fifo1;
|
||||
|
@ -74,12 +74,12 @@ void APU::main() {
|
|||
step(512);
|
||||
}
|
||||
|
||||
void APU::step(unsigned clocks) {
|
||||
auto APU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
auto APU::power() -> void {
|
||||
create(APU::Enter, 16777216);
|
||||
|
||||
square1.power();
|
||||
|
@ -92,7 +92,7 @@ void APU::power() {
|
|||
|
||||
regs.bias = 0x0200;
|
||||
|
||||
for(unsigned n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
|
||||
for(uint n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
struct APU : Thread, MMIO {
|
||||
#include "registers.hpp"
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
void step(unsigned clocks);
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
uint8 read(uint32 addr);
|
||||
void write(uint32 addr, uint8 byte);
|
||||
void power();
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
void runsequencer();
|
||||
auto runsequencer() -> void;
|
||||
|
||||
void serialize(serializer&);
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
void APU::FIFO::read() {
|
||||
auto APU::FIFO::read() -> void {
|
||||
if(size == 0) return;
|
||||
size--;
|
||||
output = sample[rdoffset++];
|
||||
}
|
||||
|
||||
void APU::FIFO::write(int8 byte) {
|
||||
auto APU::FIFO::write(int8 byte) -> void {
|
||||
if(size == 32) rdoffset++;
|
||||
else size++;
|
||||
sample[wroffset++] = byte;
|
||||
}
|
||||
|
||||
void APU::FIFO::reset() {
|
||||
auto APU::FIFO::reset() -> void {
|
||||
for(auto& byte : sample) byte = 0;
|
||||
output = 0;
|
||||
|
||||
|
@ -19,7 +19,7 @@ void APU::FIFO::reset() {
|
|||
size = 0;
|
||||
}
|
||||
|
||||
void APU::FIFO::power() {
|
||||
auto APU::FIFO::power() -> void {
|
||||
reset();
|
||||
|
||||
lenable = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
uint8 APU::read(uint32 addr) {
|
||||
auto APU::read(uint32 addr) -> uint8 {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
|
@ -97,7 +97,7 @@ uint8 APU::read(uint32 addr) {
|
|||
return 0u;
|
||||
}
|
||||
|
||||
void APU::write(uint32 addr, uint8 byte) {
|
||||
auto APU::write(uint32 addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
unsigned APU::Noise::divider() const {
|
||||
auto APU::Noise::divider() const -> uint {
|
||||
if(divisor == 0) return 4;
|
||||
return divisor * 8;
|
||||
}
|
||||
|
||||
void APU::Noise::run() {
|
||||
auto APU::Noise::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = divider() << frequency;
|
||||
if(frequency < 14) {
|
||||
|
@ -16,13 +16,13 @@ void APU::Noise::run() {
|
|||
if(enable == false || (lfsr & 1)) output = 0;
|
||||
}
|
||||
|
||||
void APU::Noise::clocklength() {
|
||||
auto APU::Noise::clocklength() -> void {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::clockenvelope() {
|
||||
auto APU::Noise::clockenvelope() -> void {
|
||||
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||
envelope.period = envelope.frequency;
|
||||
if(envelope.direction == 0 && volume > 0) volume--;
|
||||
|
@ -30,7 +30,7 @@ void APU::Noise::clockenvelope() {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 APU::Noise::read(unsigned addr) const {
|
||||
auto APU::Noise::read(uint addr) const -> uint8 {
|
||||
switch(addr) {
|
||||
case 1: return 0;
|
||||
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||
|
@ -39,7 +39,7 @@ uint8 APU::Noise::read(unsigned addr) const {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Noise::write(unsigned addr, uint8 byte) {
|
||||
auto APU::Noise::write(uint addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
case 1: //NR41
|
||||
length = byte >> 0;
|
||||
|
@ -49,7 +49,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
|
|||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
if(!envelope.dacEnable()) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR43
|
||||
|
@ -64,7 +64,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
|
|||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
enable = envelope.dacEnable();
|
||||
lfsr = ~0u;
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
|
@ -74,7 +74,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Noise::power() {
|
||||
auto APU::Noise::power() -> void {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.volume = 0;
|
||||
|
|
|
@ -5,7 +5,7 @@ APU::Registers::SoundBias::operator uint16() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint16 APU::Registers::SoundBias::operator=(uint16 source) {
|
||||
auto APU::Registers::SoundBias::operator=(uint16 source) -> uint16 {
|
||||
level = (source >> 0) & 1023;
|
||||
amplitude = (source >> 14) & 3;
|
||||
return operator uint16();
|
||||
|
|
|
@ -4,11 +4,11 @@ struct Registers {
|
|||
uint2 amplitude;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
SoundBias& operator=(const SoundBias&) = delete;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const SoundBias&) -> SoundBias& = delete;
|
||||
} bias;
|
||||
|
||||
unsigned clock;
|
||||
uint clock;
|
||||
} regs;
|
||||
|
||||
struct Sweep {
|
||||
|
@ -28,7 +28,7 @@ struct Envelope {
|
|||
|
||||
uint3 period;
|
||||
|
||||
inline bool dacenable() const { return volume || direction; }
|
||||
auto dacEnable() const -> bool { return volume || direction; }
|
||||
};
|
||||
|
||||
struct Square {
|
||||
|
@ -40,32 +40,32 @@ struct Square {
|
|||
uint1 counter;
|
||||
uint1 initialize;
|
||||
|
||||
signed shadowfrequency;
|
||||
int shadowfrequency;
|
||||
uint1 signal;
|
||||
uint4 output;
|
||||
unsigned period;
|
||||
uint period;
|
||||
uint3 phase;
|
||||
uint4 volume;
|
||||
|
||||
void run();
|
||||
void clocklength();
|
||||
void clockenvelope();
|
||||
auto run() -> void;
|
||||
auto clocklength() -> void;
|
||||
auto clockenvelope() -> void;
|
||||
};
|
||||
|
||||
struct Square1 : Square {
|
||||
Sweep sweep;
|
||||
|
||||
void runsweep(bool update);
|
||||
void clocksweep();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
auto runsweep(bool update) -> void;
|
||||
auto clocksweep() -> void;
|
||||
auto read(uint addr) const -> uint8;
|
||||
auto write(uint addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
} square1;
|
||||
|
||||
struct Square2 : Square {
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
auto read(uint addr) const -> uint8;
|
||||
auto write(uint addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
} square2;
|
||||
|
||||
struct Wave {
|
||||
|
@ -84,15 +84,15 @@ struct Wave {
|
|||
uint4 patternaddr;
|
||||
uint1 patternbank;
|
||||
uint4 patternsample;
|
||||
unsigned period;
|
||||
uint period;
|
||||
|
||||
void run();
|
||||
void clocklength();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
uint8 readram(unsigned addr) const;
|
||||
void writeram(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
auto run() -> void;
|
||||
auto clocklength() -> void;
|
||||
auto read(uint addr) const -> uint8;
|
||||
auto write(uint addr, uint8 byte) -> void;
|
||||
auto readram(uint addr) const -> uint8;
|
||||
auto writeram(uint addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
} wave;
|
||||
|
||||
struct Noise {
|
||||
|
@ -107,16 +107,16 @@ struct Noise {
|
|||
uint1 enable;
|
||||
uint15 lfsr;
|
||||
uint4 output;
|
||||
unsigned period;
|
||||
uint period;
|
||||
uint4 volume;
|
||||
|
||||
unsigned divider() const;
|
||||
void run();
|
||||
void clocklength();
|
||||
void clockenvelope();
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
auto divider() const -> uint;
|
||||
auto run() -> void;
|
||||
auto clocklength() -> void;
|
||||
auto clockenvelope() -> void;
|
||||
auto read(uint addr) const -> uint8;
|
||||
auto write(uint addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
} noise;
|
||||
|
||||
struct Sequencer {
|
||||
|
@ -133,9 +133,9 @@ struct Sequencer {
|
|||
int16 lsample;
|
||||
int16 rsample;
|
||||
|
||||
uint8 read(unsigned addr) const;
|
||||
void write(unsigned addr, uint8 byte);
|
||||
void power();
|
||||
auto read(uint addr) const -> uint8;
|
||||
auto write(uint addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
} sequencer;
|
||||
|
||||
struct FIFO {
|
||||
|
@ -151,8 +151,8 @@ struct FIFO {
|
|||
uint1 renable;
|
||||
uint1 timer;
|
||||
|
||||
void read();
|
||||
void write(int8 byte);
|
||||
void reset();
|
||||
void power();
|
||||
auto read() -> void;
|
||||
auto write(int8 byte) -> void;
|
||||
auto reset() -> void;
|
||||
auto power() -> void;
|
||||
} fifo[2];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void APU::runsequencer() {
|
||||
auto APU::runsequencer() -> void {
|
||||
auto& r = sequencer;
|
||||
|
||||
if(r.base == 0) { //512hz
|
||||
|
@ -26,7 +26,7 @@ void APU::runsequencer() {
|
|||
if(r.enable[3]) noise.run();
|
||||
}
|
||||
|
||||
uint8 APU::Sequencer::read(unsigned addr) const {
|
||||
auto APU::Sequencer::read(uint addr) const -> uint8 {
|
||||
switch(addr) {
|
||||
case 0: return (rvolume << 0) | (lvolume << 4);
|
||||
case 1: return (
|
||||
|
@ -43,7 +43,7 @@ uint8 APU::Sequencer::read(unsigned addr) const {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Sequencer::write(unsigned addr, uint8 byte) {
|
||||
auto APU::Sequencer::write(uint addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
case 0: //NR50
|
||||
rvolume = byte >> 0;
|
||||
|
@ -71,7 +71,7 @@ void APU::Sequencer::write(unsigned addr, uint8 byte) {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Sequencer::power() {
|
||||
auto APU::Sequencer::power() -> void {
|
||||
lvolume = 0;
|
||||
rvolume = 0;
|
||||
for(auto& n : lenable) n = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void APU::serialize(serializer& s) {
|
||||
auto APU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.integer(regs.bias.level);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void APU::Square::run() {
|
||||
auto APU::Square::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
phase++;
|
||||
|
@ -15,13 +15,13 @@ void APU::Square::run() {
|
|||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square::clocklength() {
|
||||
auto APU::Square::clocklength() -> void {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square::clockenvelope() {
|
||||
auto APU::Square::clockenvelope() -> void {
|
||||
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||
envelope.period = envelope.frequency;
|
||||
if(envelope.direction == 0 && volume > 0) volume--;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
void APU::Square1::runsweep(bool update) {
|
||||
if(sweep.enable == false) return;
|
||||
auto APU::Square1::runsweep(bool update) -> void {
|
||||
if(!sweep.enable) return;
|
||||
|
||||
sweep.negate = sweep.direction;
|
||||
unsigned delta = shadowfrequency >> sweep.shift;
|
||||
signed updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);
|
||||
uint delta = shadowfrequency >> sweep.shift;
|
||||
int updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);
|
||||
|
||||
if(updatefrequency > 2047) {
|
||||
enable = false;
|
||||
|
@ -14,7 +14,7 @@ void APU::Square1::runsweep(bool update) {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clocksweep() {
|
||||
auto APU::Square1::clocksweep() -> void {
|
||||
if(enable && sweep.frequency && --sweep.period == 0) {
|
||||
sweep.period = sweep.frequency;
|
||||
runsweep(1);
|
||||
|
@ -22,7 +22,7 @@ void APU::Square1::clocksweep() {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 APU::Square1::read(unsigned addr) const {
|
||||
auto APU::Square1::read(uint addr) const -> uint8 {
|
||||
switch(addr) {
|
||||
case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4);
|
||||
case 1: return (duty << 6);
|
||||
|
@ -32,7 +32,7 @@ uint8 APU::Square1::read(unsigned addr) const {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Square1::write(unsigned addr, uint8 byte) {
|
||||
auto APU::Square1::write(uint addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
case 0: //NR10
|
||||
if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false;
|
||||
|
@ -50,7 +50,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
|
|||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
if(!envelope.dacEnable()) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR13
|
||||
|
@ -63,7 +63,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
|
|||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
enable = envelope.dacEnable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
|
@ -78,7 +78,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
auto APU::Square1::power() -> void {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.direction = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
uint8 APU::Square2::read(unsigned addr) const {
|
||||
auto APU::Square2::read(uint addr) const -> uint8 {
|
||||
switch(addr) {
|
||||
case 1: return (duty << 6);
|
||||
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||
|
@ -7,7 +7,7 @@ uint8 APU::Square2::read(unsigned addr) const {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Square2::write(unsigned addr, uint8 byte) {
|
||||
auto APU::Square2::write(uint addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
case 1: //NR21
|
||||
length = byte >> 0;
|
||||
|
@ -18,7 +18,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
|
|||
envelope.frequency = byte >> 0;
|
||||
envelope.direction = byte >> 3;
|
||||
envelope.volume = byte >> 4;
|
||||
if(envelope.dacenable() == false) enable = false;
|
||||
if(!envelope.dacEnable()) enable = false;
|
||||
break;
|
||||
|
||||
case 3: //NR23
|
||||
|
@ -31,7 +31,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
|
|||
initialize = byte >> 7;
|
||||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
enable = envelope.dacEnable();
|
||||
period = 2 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
|
@ -41,7 +41,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
auto APU::Square2::power() -> void {
|
||||
envelope.frequency = 0;
|
||||
envelope.direction = 0;
|
||||
envelope.direction = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void APU::Wave::run() {
|
||||
auto APU::Wave::run() -> void {
|
||||
if(period && --period == 0) {
|
||||
period = 1 * (2048 - frequency);
|
||||
patternsample = pattern[patternbank * 16 + patternaddr++];
|
||||
|
@ -6,18 +6,18 @@ void APU::Wave::run() {
|
|||
}
|
||||
|
||||
output = patternsample;
|
||||
static unsigned multiplier[] = {0, 4, 2, 1, 3, 3, 3, 3};
|
||||
static uint multiplier[] = {0, 4, 2, 1, 3, 3, 3, 3};
|
||||
output = (output * multiplier[volume]) / 4;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
void APU::Wave::clocklength() {
|
||||
auto APU::Wave::clocklength() -> void {
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Wave::read(unsigned addr) const {
|
||||
auto APU::Wave::read(uint addr) const -> uint8 {
|
||||
switch(addr) {
|
||||
case 0: return (mode << 5) | (bank << 6) | (dacenable << 7);
|
||||
case 1: return 0;
|
||||
|
@ -27,7 +27,7 @@ uint8 APU::Wave::read(unsigned addr) const {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Wave::write(unsigned addr, uint8 byte) {
|
||||
auto APU::Wave::write(uint addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
case 0: //NR30
|
||||
mode = byte >> 5;
|
||||
|
@ -64,19 +64,19 @@ void APU::Wave::write(unsigned addr, uint8 byte) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 APU::Wave::readram(unsigned addr) const {
|
||||
auto APU::Wave::readram(uint addr) const -> uint8 {
|
||||
uint8 byte = 0;
|
||||
byte |= pattern[addr * 2 + 0] << 0;
|
||||
byte |= pattern[addr * 2 + 1] << 4;
|
||||
return byte;
|
||||
}
|
||||
|
||||
void APU::Wave::writeram(unsigned addr, uint8 byte) {
|
||||
auto APU::Wave::writeram(uint addr, uint8 byte) -> void {
|
||||
pattern[addr * 2 + 0] = byte >> 0;
|
||||
pattern[addr * 2 + 1] = byte >> 4;
|
||||
}
|
||||
|
||||
void APU::Wave::power() {
|
||||
auto APU::Wave::power() -> void {
|
||||
mode = 0;
|
||||
bank = 0;
|
||||
dacenable = 0;
|
||||
|
|
|
@ -12,6 +12,32 @@ namespace GameBoyAdvance {
|
|||
#include "serialization.cpp"
|
||||
CPU cpu;
|
||||
|
||||
CPU::CPU() {
|
||||
iwram = new uint8[ 32 * 1024];
|
||||
ewram = new uint8[256 * 1024];
|
||||
|
||||
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
|
||||
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
|
||||
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
|
||||
|
||||
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
|
||||
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
|
||||
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
|
||||
|
||||
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
|
||||
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
|
||||
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
|
||||
|
||||
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
|
||||
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
|
||||
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
delete[] iwram;
|
||||
delete[] ewram;
|
||||
}
|
||||
|
||||
auto CPU::Enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
|
@ -59,12 +85,12 @@ auto CPU::main() -> void {
|
|||
exec();
|
||||
}
|
||||
|
||||
auto CPU::step(unsigned clocks) -> void {
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
timer_step(clocks);
|
||||
sync_step(clocks);
|
||||
}
|
||||
|
||||
auto CPU::sync_step(unsigned clocks) -> void {
|
||||
auto CPU::sync_step(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
|
@ -125,40 +151,14 @@ auto CPU::power() -> void {
|
|||
|
||||
active.dma = false;
|
||||
|
||||
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
||||
for(unsigned n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||
for(unsigned n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
||||
for(unsigned n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
|
||||
for(unsigned n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
|
||||
for(unsigned n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
|
||||
for(unsigned n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
|
||||
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
|
||||
}
|
||||
|
||||
CPU::CPU() {
|
||||
iwram = new uint8[ 32 * 1024];
|
||||
ewram = new uint8[256 * 1024];
|
||||
|
||||
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
|
||||
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
|
||||
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
|
||||
|
||||
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
|
||||
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
|
||||
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
|
||||
|
||||
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
|
||||
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
|
||||
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
|
||||
|
||||
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
|
||||
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
|
||||
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
delete[] iwram;
|
||||
delete[] ewram;
|
||||
for(uint n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
||||
for(uint n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||
for(uint n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
||||
for(uint n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
|
||||
for(uint n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
|
||||
for(uint n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
|
||||
for(uint n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
|
||||
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,41 +2,39 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
using ARM::read;
|
||||
using ARM::write;
|
||||
|
||||
uint8* iwram = nullptr;
|
||||
uint8* ewram = nullptr;
|
||||
#include "registers.hpp"
|
||||
#include "prefetch.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
//cpu.cpp
|
||||
CPU();
|
||||
~CPU();
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
|
||||
auto step(unsigned clocks) -> void override;
|
||||
auto step(uint clocks) -> void override;
|
||||
|
||||
auto sync_step(unsigned clocks) -> void;
|
||||
auto sync_step(uint clocks) -> void;
|
||||
auto keypad_run() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
CPU();
|
||||
~CPU();
|
||||
|
||||
//bus.cpp
|
||||
auto bus_idle() -> void override;
|
||||
auto bus_read(unsigned mode, uint32 addr) -> uint32 override;
|
||||
auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void override;
|
||||
auto bus_wait(unsigned mode, uint32 addr) -> unsigned;
|
||||
auto bus_read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto bus_write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto bus_wait(uint mode, uint32 addr) -> uint;
|
||||
|
||||
//mmio.cpp
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
|
||||
auto iwram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto iwram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto iwram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto iwram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto ewram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto ewram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto ewram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto ewram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dma_run() -> void;
|
||||
|
@ -46,12 +44,15 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
auto dma_hdma() -> void;
|
||||
|
||||
//timer.cpp
|
||||
auto timer_step(unsigned clocks) -> void;
|
||||
auto timer_increment(unsigned n) -> void;
|
||||
auto timer_fifo_run(unsigned n) -> void;
|
||||
auto timer_step(uint clocks) -> void;
|
||||
auto timer_increment(uint n) -> void;
|
||||
auto timer_fifo_run(uint n) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint8* iwram = nullptr;
|
||||
uint8* ewram = nullptr;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -20,8 +20,8 @@ auto CPU::dma_run() -> void {
|
|||
}
|
||||
|
||||
auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
||||
unsigned seek = dma.control.size ? 4 : 2;
|
||||
unsigned mode = dma.control.size ? Word : Half;
|
||||
uint seek = dma.control.size ? 4 : 2;
|
||||
uint mode = dma.control.size ? Word : Half;
|
||||
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||
|
||||
if(mode & Nonsequential) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto CPU::iwram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto CPU::iwram_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
|
||||
if(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16;
|
||||
|
@ -7,7 +7,7 @@ auto CPU::iwram_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return iwram[addr & 0x7fff];
|
||||
}
|
||||
|
||||
auto CPU::iwram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto CPU::iwram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -25,7 +25,7 @@ auto CPU::iwram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
iwram[addr & 0x7fff] = word;
|
||||
}
|
||||
|
||||
auto CPU::ewram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto CPU::ewram_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
if(!regs.memory.control.ewram) return iwram_read(mode, addr);
|
||||
|
||||
|
@ -35,7 +35,7 @@ auto CPU::ewram_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return ewram[addr & 0x3ffff];
|
||||
}
|
||||
|
||||
auto CPU::ewram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto CPU::ewram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
if(!regs.memory.control.ewram) return iwram_write(mode, addr, word);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ auto CPU::prefetch_sync(uint32 addr) -> void {
|
|||
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_step(unsigned clocks) -> void {
|
||||
auto CPU::prefetch_step(uint clocks) -> void {
|
||||
step(clocks);
|
||||
if(!regs.wait.control.prefetch || active.dma) return;
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ struct {
|
|||
uint16 slot[8] = {0};
|
||||
uint32 addr = 0; //read location of slot buffer
|
||||
uint32 load = 0; //write location of slot buffer
|
||||
signed wait = 0; //number of clocks before next slot load
|
||||
int wait = 0; //number of clocks before next slot load
|
||||
|
||||
auto empty() const { return addr == load; }
|
||||
auto full() const { return load - addr == 16; }
|
||||
} prefetch;
|
||||
|
||||
auto prefetch_sync(uint32 addr) -> void;
|
||||
auto prefetch_step(unsigned clocks) -> void;
|
||||
auto prefetch_step(uint clocks) -> void;
|
||||
auto prefetch_wait() -> void;
|
||||
auto prefetch_read() -> uint16;
|
||||
|
|
|
@ -183,6 +183,6 @@ struct Registers {
|
|||
} memory;
|
||||
|
||||
uint1 postboot;
|
||||
enum class Mode : unsigned { Normal, Halt, Stop } mode;
|
||||
unsigned clock;
|
||||
enum class Mode : uint { Normal, Halt, Stop } mode;
|
||||
uint clock;
|
||||
} regs;
|
||||
|
|
|
@ -103,7 +103,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(regs.memory.control.unknown2);
|
||||
|
||||
s.integer(regs.postboot);
|
||||
s.integer((unsigned&)regs.mode);
|
||||
s.integer((uint&)regs.mode);
|
||||
s.integer(regs.clock);
|
||||
|
||||
s.integer(pending.dma.vblank);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
auto CPU::timer_step(unsigned clocks) -> void {
|
||||
for(unsigned c = 0; c < clocks; c++) {
|
||||
for(unsigned n = 0; n < 4; n++) {
|
||||
auto CPU::timer_step(uint clocks) -> void {
|
||||
for(auto c : range(clocks)) {
|
||||
for(auto n : range(4)) {
|
||||
auto& timer = regs.timer[n];
|
||||
|
||||
if(timer.pending) {
|
||||
|
@ -13,7 +13,7 @@ auto CPU::timer_step(unsigned clocks) -> void {
|
|||
|
||||
if(timer.control.enable == false || timer.control.cascade == true) continue;
|
||||
|
||||
static unsigned mask[] = {0, 63, 255, 1023};
|
||||
static uint mask[] = {0, 63, 255, 1023};
|
||||
if((regs.clock & mask[timer.control.frequency]) == 0) {
|
||||
timer_increment(n);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ auto CPU::timer_step(unsigned clocks) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::timer_increment(unsigned n) -> void {
|
||||
auto CPU::timer_increment(uint n) -> void {
|
||||
auto& timer = regs.timer[n];
|
||||
if(++timer.period == 0) {
|
||||
timer.period = timer.reload;
|
||||
|
@ -39,7 +39,7 @@ auto CPU::timer_increment(unsigned n) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::timer_fifo_run(unsigned n) -> void {
|
||||
auto CPU::timer_fifo_run(uint n) -> void {
|
||||
apu.fifo[n].read();
|
||||
if(apu.fifo[n].size > 16) return;
|
||||
|
||||
|
|
|
@ -4,23 +4,56 @@ namespace GameBoyAdvance {
|
|||
|
||||
Interface* interface = nullptr;
|
||||
|
||||
string Interface::title() {
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
|
||||
information.name = "Game Boy Advance";
|
||||
information.width = 240;
|
||||
information.height = 160;
|
||||
information.overscan = false;
|
||||
information.aspectRatio = 1.0;
|
||||
information.resettable = false;
|
||||
information.capability.states = true;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
|
||||
|
||||
{ Device device{0, ID::Device, "Controller"};
|
||||
device.input.append({ 0, 0, "A" });
|
||||
device.input.append({ 1, 0, "B" });
|
||||
device.input.append({ 2, 0, "Select"});
|
||||
device.input.append({ 3, 0, "Start" });
|
||||
device.input.append({ 4, 0, "Right" });
|
||||
device.input.append({ 5, 0, "Left" });
|
||||
device.input.append({ 6, 0, "Up" });
|
||||
device.input.append({ 7, 0, "Down" });
|
||||
device.input.append({ 8, 0, "R" });
|
||||
device.input.append({ 9, 0, "L" });
|
||||
device.input.append({10, 2, "Rumble"});
|
||||
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Device", {device[0]}});
|
||||
}
|
||||
|
||||
auto Interface::title() -> string {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
||||
double Interface::videoFrequency() {
|
||||
auto Interface::videoFrequency() -> double {
|
||||
return 16777216.0 / (228.0 * 1232.0);
|
||||
}
|
||||
|
||||
double Interface::audioFrequency() {
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 16777216.0 / 512.0;
|
||||
}
|
||||
|
||||
bool Interface::loaded() {
|
||||
auto Interface::loaded() -> bool {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
auto Interface::group(uint id) -> uint {
|
||||
switch(id) {
|
||||
case ID::SystemManifest:
|
||||
case ID::BIOS:
|
||||
|
@ -36,17 +69,17 @@ unsigned Interface::group(unsigned id) {
|
|||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id) {
|
||||
auto Interface::load(uint id) -> void {
|
||||
cartridge.load();
|
||||
}
|
||||
|
||||
void Interface::save() {
|
||||
auto Interface::save() -> void {
|
||||
for(auto& memory : cartridge.memory) {
|
||||
interface->saveRequest(memory.id, memory.name);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const stream& stream) {
|
||||
auto Interface::load(uint id, const stream& stream) -> void {
|
||||
if(id == ID::SystemManifest) {
|
||||
system.information.manifest = stream.text();
|
||||
}
|
||||
|
@ -76,7 +109,7 @@ void Interface::load(unsigned id, const stream& stream) {
|
|||
}
|
||||
}
|
||||
|
||||
void Interface::save(unsigned id, const stream& stream) {
|
||||
auto Interface::save(uint id, const stream& stream) -> void {
|
||||
if(id == ID::SRAM) {
|
||||
stream.write(cartridge.sram.data, cartridge.sram.size);
|
||||
}
|
||||
|
@ -90,68 +123,34 @@ void Interface::save(unsigned id, const stream& stream) {
|
|||
}
|
||||
}
|
||||
|
||||
void Interface::unload() {
|
||||
auto Interface::unload() -> void {
|
||||
save();
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
void Interface::power() {
|
||||
auto Interface::power() -> void {
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::reset() {
|
||||
auto Interface::reset() -> void {
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
auto Interface::run() -> void {
|
||||
system.run();
|
||||
}
|
||||
|
||||
serializer Interface::serialize() {
|
||||
auto Interface::serialize() -> serializer {
|
||||
system.runtosave();
|
||||
return system.serialize();
|
||||
}
|
||||
|
||||
bool Interface::unserialize(serializer& s) {
|
||||
auto Interface::unserialize(serializer& s) -> bool {
|
||||
return system.unserialize(s);
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate(PaletteMode mode) {
|
||||
video.generate_palette(mode);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
|
||||
information.name = "Game Boy Advance";
|
||||
information.width = 240;
|
||||
information.height = 160;
|
||||
information.overscan = false;
|
||||
information.aspectRatio = 1.0;
|
||||
information.resettable = false;
|
||||
information.capability.states = true;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
|
||||
|
||||
{
|
||||
Device device{0, ID::Device, "Controller"};
|
||||
device.input.append({ 0, 0, "A" });
|
||||
device.input.append({ 1, 0, "B" });
|
||||
device.input.append({ 2, 0, "Select"});
|
||||
device.input.append({ 3, 0, "Start" });
|
||||
device.input.append({ 4, 0, "Right" });
|
||||
device.input.append({ 5, 0, "Left" });
|
||||
device.input.append({ 6, 0, "Up" });
|
||||
device.input.append({ 7, 0, "Down" });
|
||||
device.input.append({ 8, 0, "R" });
|
||||
device.input.append({ 9, 0, "L" });
|
||||
device.input.append({10, 2, "Rumble"});
|
||||
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
port.append({0, "Device", {device[0]}});
|
||||
auto Interface::paletteUpdate(PaletteMode mode) -> void {
|
||||
video.generatePalette(mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ namespace GameBoyAdvance {
|
|||
#endif
|
||||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
enum : uint {
|
||||
System,
|
||||
GameBoyAdvance,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
enum : uint {
|
||||
SystemManifest,
|
||||
BIOS,
|
||||
|
||||
|
@ -19,35 +19,35 @@ struct ID {
|
|||
FLASH,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
enum : uint {
|
||||
Device = 1,
|
||||
};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
string title();
|
||||
double videoFrequency();
|
||||
double audioFrequency();
|
||||
|
||||
bool loaded();
|
||||
unsigned group(unsigned id);
|
||||
void load(unsigned id);
|
||||
void save();
|
||||
void load(unsigned id, const stream& stream);
|
||||
void save(unsigned id, const stream& stream);
|
||||
void unload();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void run();
|
||||
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void paletteUpdate(PaletteMode mode);
|
||||
|
||||
Interface();
|
||||
|
||||
auto title() -> string;
|
||||
auto videoFrequency() -> double;
|
||||
auto audioFrequency() -> double;
|
||||
|
||||
auto loaded() -> bool;
|
||||
auto group(uint id) -> uint;
|
||||
auto load(uint id) -> void;
|
||||
auto save() -> void;
|
||||
auto load(uint id, const stream& stream) -> void;
|
||||
auto save(uint id, const stream& stream) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
auto run() -> void;
|
||||
|
||||
auto serialize() -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
auto paletteUpdate(PaletteMode mode) -> void;
|
||||
|
||||
private:
|
||||
vector<Device> device;
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@ namespace GameBoyAdvance {
|
|||
Bus bus;
|
||||
|
||||
struct UnmappedMemory : Memory {
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 override { return 0; }
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void override {}
|
||||
auto read(uint mode, uint32 addr) -> uint32 override { return 0; }
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void override {}
|
||||
};
|
||||
|
||||
static UnmappedMemory unmappedMemory;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
struct Memory {
|
||||
virtual auto read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
virtual auto read(uint mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto write(uint mode, uint32 addr, uint32 word) -> void = 0;
|
||||
};
|
||||
|
||||
struct MMIO : Memory {
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 final;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void final;
|
||||
auto read(uint mode, uint32 addr) -> uint32 final;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void final;
|
||||
};
|
||||
|
||||
struct Bus {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto MMIO::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto MMIO::read(uint mode, uint32 addr) -> uint32 {
|
||||
uint32 word = 0;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -18,7 +18,7 @@ auto MMIO::read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return word;
|
||||
}
|
||||
|
||||
auto MMIO::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto MMIO::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
write(addr + 0, word >> 0);
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace GameBoyAdvance {
|
|||
#include "serialization.cpp"
|
||||
Player player;
|
||||
|
||||
void Player::power() {
|
||||
auto Player::power() -> void {
|
||||
status.enable = false;
|
||||
status.rumble = false;
|
||||
|
||||
|
@ -19,7 +19,7 @@ void Player::power() {
|
|||
status.recv = 0;
|
||||
}
|
||||
|
||||
void Player::frame() {
|
||||
auto Player::frame() -> void {
|
||||
uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value();
|
||||
status.logoDetected = (hash == 0x7776eb55);
|
||||
|
||||
|
@ -29,7 +29,7 @@ void Player::frame() {
|
|||
status.packet = 0;
|
||||
}
|
||||
|
||||
if(status.enable == false) return;
|
||||
if(!status.enable) return;
|
||||
|
||||
if(cpu.regs.joybus.settings == 0x0000 && cpu.regs.serial.control == 0x5088) {
|
||||
status.packet = (status.packet + 1) % 17;
|
||||
|
@ -56,7 +56,7 @@ void Player::frame() {
|
|||
}
|
||||
}
|
||||
|
||||
maybe<uint16> Player::keyinput() {
|
||||
auto Player::keyinput() -> maybe<uint16> {
|
||||
if(status.logoDetected) {
|
||||
switch(status.logoCounter) {
|
||||
case 0: return 0x03ff;
|
||||
|
@ -67,15 +67,15 @@ maybe<uint16> Player::keyinput() {
|
|||
return nothing;
|
||||
}
|
||||
|
||||
maybe<uint32> Player::read() {
|
||||
auto Player::read() -> maybe<uint32> {
|
||||
if(status.enable) return status.send;
|
||||
return nothing;
|
||||
}
|
||||
|
||||
void Player::write(uint8 byte, uint2 addr) {
|
||||
if(status.enable == false) return;
|
||||
auto Player::write(uint8 byte, uint2 addr) -> void {
|
||||
if(!status.enable) return;
|
||||
|
||||
unsigned shift = addr << 3;
|
||||
uint shift = addr << 3;
|
||||
status.recv &= ~(255 << shift);
|
||||
status.recv |= byte << shift;
|
||||
|
||||
|
|
|
@ -4,21 +4,21 @@ struct Player {
|
|||
bool rumble;
|
||||
|
||||
bool logoDetected;
|
||||
unsigned logoCounter;
|
||||
uint logoCounter;
|
||||
|
||||
unsigned packet;
|
||||
uint packet;
|
||||
uint32 send;
|
||||
uint32 recv;
|
||||
} status;
|
||||
|
||||
void power();
|
||||
void frame();
|
||||
auto power() -> void;
|
||||
auto frame() -> void;
|
||||
|
||||
maybe<uint16> keyinput();
|
||||
maybe<uint32> read();
|
||||
void write(uint8 byte, uint2 addr);
|
||||
auto keyinput() -> maybe<uint16>;
|
||||
auto read() -> maybe<uint32>;
|
||||
auto write(uint8 byte, uint2 addr) -> void;
|
||||
|
||||
void serialize(serializer& s);
|
||||
auto serialize(serializer& s) -> void;
|
||||
};
|
||||
|
||||
extern Player player;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void Player::serialize(serializer& s) {
|
||||
auto Player::serialize(serializer& s) -> void {
|
||||
s.integer(status.enable);
|
||||
s.integer(status.rumble);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void PPU::render_backgrounds() {
|
||||
auto PPU::render_backgrounds() -> void {
|
||||
switch(regs.control.bgmode) {
|
||||
case 0:
|
||||
render_background_linear(regs.bg[3]);
|
||||
|
@ -21,7 +21,7 @@ void PPU::render_backgrounds() {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::render_background_linear(Registers::Background& bg) {
|
||||
auto PPU::render_background_linear(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
|
@ -32,19 +32,19 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||
uint9 voffset = bg.vmosaic + bg.voffset;
|
||||
uint9 hoffset = bg.hoffset;
|
||||
|
||||
unsigned basemap = bg.control.screenbaseblock << 11;
|
||||
unsigned basechr = bg.control.characterbaseblock << 14;
|
||||
unsigned px = hoffset & 7, py = voffset & 7;
|
||||
uint basemap = bg.control.screenbaseblock << 11;
|
||||
uint basechr = bg.control.characterbaseblock << 14;
|
||||
uint px = hoffset & 7, py = voffset & 7;
|
||||
|
||||
Tile tile;
|
||||
uint8 data[8];
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
for(auto x : range(240)) {
|
||||
if(x == 0 || px & 8) {
|
||||
px &= 7;
|
||||
|
||||
unsigned tx = hoffset / 8, ty = voffset / 8;
|
||||
unsigned offset = (ty & 31) * 32 + (tx & 31);
|
||||
uint tx = hoffset / 8, ty = voffset / 8;
|
||||
uint offset = (ty & 31) * 32 + (tx & 31);
|
||||
if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32;
|
||||
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
||||
offset = basemap + offset * 2;
|
||||
|
@ -58,13 +58,13 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||
if(bg.control.colormode == 0) {
|
||||
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
|
||||
uint32 word = vram_read(Word, offset);
|
||||
for(unsigned n = 0; n < 8; n++) data[n] = (word >> (n * 4)) & 15;
|
||||
for(auto n : range(8)) data[n] = (word >> (n * 4)) & 15;
|
||||
} else {
|
||||
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
||||
uint32 wordlo = vram_read(Word, offset + 0);
|
||||
uint32 wordhi = vram_read(Word, offset + 4);
|
||||
for(unsigned n = 0; n < 4; n++) data[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||
for(unsigned n = 0; n < 4; n++) data[4 + n] = (wordhi >> (n * 8)) & 255;
|
||||
for(auto n : range(4)) data[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||
for(auto n : range(4)) data[4 + n] = (wordhi >> (n * 8)) & 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,14 +78,14 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::render_background_affine(Registers::Background& bg) {
|
||||
auto PPU::render_background_affine(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
unsigned basemap = bg.control.screenbaseblock << 11;
|
||||
unsigned basechr = bg.control.characterbaseblock << 14;
|
||||
unsigned screensize = 16 << bg.control.screensize;
|
||||
unsigned screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1;
|
||||
uint basemap = bg.control.screenbaseblock << 11;
|
||||
uint basechr = bg.control.characterbaseblock << 14;
|
||||
uint screensize = 16 << bg.control.screensize;
|
||||
uint screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1;
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.hmosaic = bg.lx;
|
||||
|
@ -95,9 +95,9 @@ void PPU::render_background_affine(Registers::Background& bg) {
|
|||
int28 fx = bg.hmosaic;
|
||||
int28 fy = bg.vmosaic;
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
unsigned cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7;
|
||||
unsigned cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7;
|
||||
for(auto x : range(240)) {
|
||||
uint cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7;
|
||||
uint cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7;
|
||||
|
||||
if(tx < screensize && ty < screensize) {
|
||||
uint8 character = vram[basemap + ty * screensize + tx];
|
||||
|
@ -113,16 +113,16 @@ void PPU::render_background_affine(Registers::Background& bg) {
|
|||
bg.ly += bg.pd;
|
||||
}
|
||||
|
||||
void PPU::render_background_bitmap(Registers::Background& bg) {
|
||||
auto PPU::render_background_bitmap(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5)
|
||||
unsigned basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame;
|
||||
uint basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame;
|
||||
|
||||
unsigned width = regs.control.bgmode == 5 ? 160 : 240;
|
||||
unsigned height = regs.control.bgmode == 5 ? 128 : 160;
|
||||
unsigned mode = depth ? Half : Byte;
|
||||
uint width = regs.control.bgmode == 5 ? 160 : 240;
|
||||
uint height = regs.control.bgmode == 5 ? 128 : 160;
|
||||
uint mode = depth ? Half : Byte;
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.hmosaic = bg.lx;
|
||||
|
@ -132,13 +132,13 @@ void PPU::render_background_bitmap(Registers::Background& bg) {
|
|||
int28 fx = bg.hmosaic;
|
||||
int28 fy = bg.vmosaic;
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
unsigned px = fx >> 8;
|
||||
unsigned py = fy >> 8;
|
||||
for(auto x : range(240)) {
|
||||
uint px = fx >> 8;
|
||||
uint py = fy >> 8;
|
||||
|
||||
if(px < width && py < height) {
|
||||
unsigned offset = py * width + px;
|
||||
unsigned color = vram_read(mode, basemap + (offset << depth));
|
||||
uint offset = py * width + px;
|
||||
uint color = vram_read(mode, basemap + (offset << depth));
|
||||
|
||||
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||
if(depth == 0) color = pram[color];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto PPU::vram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto PPU::vram_read(uint mode, uint32 addr) -> uint32 {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -14,7 +14,7 @@ auto PPU::vram_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return 0; //should never occur
|
||||
}
|
||||
|
||||
auto PPU::vram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::vram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -38,13 +38,13 @@ auto PPU::vram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::pram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto PPU::pram_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8);
|
||||
return pram[addr >> 1 & 511];
|
||||
}
|
||||
|
||||
auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::pram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
pram_write(Half, addr & ~2, word >> 0);
|
||||
pram_write(Half, addr | 2, word >> 16);
|
||||
|
@ -59,7 +59,7 @@ auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
|||
pram[addr >> 1 & 511] = (uint16)word;
|
||||
}
|
||||
|
||||
auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto PPU::oam_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8);
|
||||
|
||||
|
@ -103,7 +103,7 @@ auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::oam_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::oam_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
oam_write(Half, addr & ~2, word >> 0);
|
||||
oam_write(Half, addr | 2, word >> 16);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
uint8 PPU::read(uint32 addr) {
|
||||
auto PPU::read(uint32 addr) -> uint8 {
|
||||
switch(addr) {
|
||||
|
||||
//DISPCNT
|
||||
|
@ -48,7 +48,7 @@ uint8 PPU::read(uint32 addr) {
|
|||
return 0u;
|
||||
}
|
||||
|
||||
void PPU::write(uint32 addr, uint8 byte) {
|
||||
auto PPU::write(uint32 addr, uint8 byte) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//DISPCNT
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
void PPU::render_mosaic_background(unsigned id) {
|
||||
auto PPU::render_mosaic_background(uint id) -> void {
|
||||
if(regs.mosaic.bghsize == 0) return;
|
||||
unsigned width = 1 + regs.mosaic.bghsize;
|
||||
uint width = 1 + regs.mosaic.bghsize;
|
||||
auto& buffer = layer[id];
|
||||
|
||||
for(unsigned x = 0; x < 240;) {
|
||||
for(unsigned m = 1; m < width; m++) {
|
||||
for(uint x = 0; x < 240;) {
|
||||
for(uint m = 1; m < width; m++) {
|
||||
if(x + m >= 240) break;
|
||||
buffer[x + m] = buffer[x];
|
||||
}
|
||||
|
@ -12,16 +12,16 @@ void PPU::render_mosaic_background(unsigned id) {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::render_mosaic_object() {
|
||||
auto PPU::render_mosaic_object() -> void {
|
||||
if(regs.mosaic.objhsize == 0) return;
|
||||
unsigned width = 1 + regs.mosaic.objhsize;
|
||||
uint width = 1 + regs.mosaic.objhsize;
|
||||
auto& buffer = layer[OBJ];
|
||||
|
||||
Pixel mosaicPixel;
|
||||
mosaicPixel.mosaic = false;
|
||||
unsigned counter = 0;
|
||||
uint counter = 0;
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
for(auto x : range(240)) {
|
||||
if(counter == width || mosaicPixel.mosaic == false) {
|
||||
mosaicPixel = buffer[x];
|
||||
if(counter == width) counter = 0;
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
void PPU::render_objects() {
|
||||
auto PPU::render_objects() -> void {
|
||||
if(regs.control.enable[OBJ] == false) return;
|
||||
for(unsigned n = 0; n < 128; n++) render_object(object[n]);
|
||||
for(auto n : range(128)) render_object(object[n]);
|
||||
}
|
||||
|
||||
//px,py = pixel coordinates within sprite [0,0 - width,height)
|
||||
//fx,fy = affine pixel coordinates
|
||||
//pa,pb,pc,pd = affine pixel adjustments
|
||||
//x,y = adjusted coordinates within sprite (linear = vflip/hflip, affine = rotation/zoom)
|
||||
void PPU::render_object(Object& obj) {
|
||||
auto PPU::render_object(Object& obj) -> void {
|
||||
uint8 py = regs.vcounter - obj.y;
|
||||
if(obj.affine == 0 && obj.affinesize == 1) return; //hidden
|
||||
if(py >= obj.height << obj.affinesize) return; //offscreen
|
||||
|
||||
auto& output = layer[OBJ];
|
||||
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
|
||||
unsigned baseaddr = obj.character * 32;
|
||||
uint rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
|
||||
uint baseaddr = obj.character * 32;
|
||||
|
||||
if(obj.mosaic && regs.mosaic.objvsize) {
|
||||
signed mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
|
||||
int mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
|
||||
py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0;
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ void PPU::render_object(Object& obj) {
|
|||
int28 fx = originx * pa + originy * pb;
|
||||
int28 fy = originx * pc + originy * pd;
|
||||
|
||||
for(unsigned px = 0; px < (obj.width << obj.affinesize); px++) {
|
||||
unsigned x, y;
|
||||
for(uint px = 0; px < (obj.width << obj.affinesize); px++) {
|
||||
uint x, y;
|
||||
if(obj.affine == 0) {
|
||||
x = px;
|
||||
y = py;
|
||||
|
@ -52,7 +52,7 @@ void PPU::render_object(Object& obj) {
|
|||
|
||||
uint9 ox = obj.x + px;
|
||||
if(ox < 240 && x < obj.width && y < obj.height) {
|
||||
unsigned offset = (y / 8) * rowsize + (x / 8);
|
||||
uint offset = (y / 8) * rowsize + (x / 8);
|
||||
offset = offset * 64 + (y & 7) * 8 + (x & 7);
|
||||
|
||||
uint8 color = object_vram_read(baseaddr + (offset >> !obj.colors));
|
||||
|
@ -72,7 +72,7 @@ void PPU::render_object(Object& obj) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 PPU::object_vram_read(unsigned addr) const {
|
||||
auto PPU::object_vram_read(uint addr) const -> uint8 {
|
||||
if(regs.control.bgmode == 3 || regs.control.bgmode == 4 || regs.control.bgmode == 5) {
|
||||
if(addr <= 0x3fff) return 0u;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,20 @@ namespace GameBoyAdvance {
|
|||
#include "serialization.cpp"
|
||||
PPU ppu;
|
||||
|
||||
void PPU::Enter() {
|
||||
PPU::PPU() {
|
||||
output = new uint32[240 * 160];
|
||||
|
||||
regs.bg[0].id = BG0;
|
||||
regs.bg[1].id = BG1;
|
||||
regs.bg[2].id = BG2;
|
||||
regs.bg[3].id = BG3;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] output;
|
||||
}
|
||||
|
||||
auto PPU::Enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
|
@ -32,22 +45,22 @@ void PPU::Enter() {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::main() {
|
||||
auto PPU::main() -> void {
|
||||
scanline();
|
||||
}
|
||||
|
||||
void PPU::step(unsigned clocks) {
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void PPU::power() {
|
||||
auto PPU::power() -> void {
|
||||
create(PPU::Enter, 16777216);
|
||||
|
||||
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
|
||||
for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
|
||||
for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
|
||||
for(uint n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
|
||||
for(uint n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
|
||||
|
||||
regs.control = 0;
|
||||
regs.greenswap = 0;
|
||||
|
@ -84,10 +97,10 @@ void PPU::power() {
|
|||
regs.blend.evb = 0;
|
||||
regs.blend.evy = 0;
|
||||
|
||||
for(unsigned n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
|
||||
for(uint n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
|
||||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
auto PPU::scanline() -> void {
|
||||
cpu.keypad_run();
|
||||
|
||||
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
||||
|
@ -116,7 +129,7 @@ void PPU::scanline() {
|
|||
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
|
||||
render_forceblank();
|
||||
} else {
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
for(auto x : range(240)) {
|
||||
windowmask[0][x] = false;
|
||||
windowmask[1][x] = false;
|
||||
windowmask[2][x] = false;
|
||||
|
@ -148,22 +161,9 @@ void PPU::scanline() {
|
|||
if(++regs.vcounter == 228) regs.vcounter = 0;
|
||||
}
|
||||
|
||||
void PPU::frame() {
|
||||
auto PPU::frame() -> void {
|
||||
player.frame();
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
output = new uint32[240 * 160];
|
||||
|
||||
regs.bg[0].id = BG0;
|
||||
regs.bg[1].id = BG1;
|
||||
regs.bg[2].id = BG2;
|
||||
regs.bg[3].id = BG3;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] output;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,50 +1,52 @@
|
|||
struct PPU : Thread, MMIO {
|
||||
uint8 vram[96 * 1024];
|
||||
uint16 pram[512];
|
||||
#include "registers.hpp"
|
||||
#include "state.hpp"
|
||||
uint32* output;
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
void step(unsigned clocks);
|
||||
|
||||
void power();
|
||||
void scanline();
|
||||
void frame();
|
||||
|
||||
uint8 read(uint32 addr);
|
||||
void write(uint32 addr, uint8 byte);
|
||||
|
||||
auto vram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto vram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto pram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto pram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto oam_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto oam_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
void render_backgrounds();
|
||||
void render_background_linear(Registers::Background&);
|
||||
void render_background_affine(Registers::Background&);
|
||||
void render_background_bitmap(Registers::Background&);
|
||||
|
||||
void render_objects();
|
||||
void render_object(Object&);
|
||||
uint8 object_vram_read(unsigned addr) const;
|
||||
|
||||
void render_mosaic_background(unsigned id);
|
||||
void render_mosaic_object();
|
||||
|
||||
void render_forceblank();
|
||||
void render_screen();
|
||||
void render_window(unsigned window);
|
||||
unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
|
||||
|
||||
void serialize(serializer&);
|
||||
PPU();
|
||||
~PPU();
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto scanline() -> void;
|
||||
auto frame() -> void;
|
||||
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
|
||||
auto vram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto vram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto pram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto pram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto oam_read(uint mode, uint32 addr) -> uint32;
|
||||
auto oam_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto render_backgrounds() -> void;
|
||||
auto render_background_linear(Registers::Background&) -> void;
|
||||
auto render_background_affine(Registers::Background&) -> void;
|
||||
auto render_background_bitmap(Registers::Background&) -> void;
|
||||
|
||||
auto render_objects() -> void;
|
||||
auto render_object(Object&) -> void;
|
||||
auto object_vram_read(uint addr) const -> uint8;
|
||||
|
||||
auto render_mosaic_background(uint id) -> void;
|
||||
auto render_mosaic_object() -> void;
|
||||
|
||||
auto render_forceblank() -> void;
|
||||
auto render_screen() -> void;
|
||||
auto render_window(uint window) -> void;
|
||||
auto blend(uint above, uint eva, uint below, uint evb) -> uint;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint8 vram[96 * 1024];
|
||||
uint16 pram[512];
|
||||
uint32* output;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
||||
|
|
|
@ -17,7 +17,7 @@ PPU::Registers::Control::operator uint16() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint16 PPU::Registers::Control::operator=(uint16 source) {
|
||||
auto PPU::Registers::Control::operator=(uint16 source) -> uint16 {
|
||||
bgmode = source >> 0;
|
||||
cgbmode = source >> 3;
|
||||
frame = source >> 4;
|
||||
|
@ -47,7 +47,7 @@ PPU::Registers::Status::operator uint16() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint16 PPU::Registers::Status::operator=(uint16 source) {
|
||||
auto PPU::Registers::Status::operator=(uint16 source) -> uint16 {
|
||||
vblank = source >> 0;
|
||||
hblank = source >> 1;
|
||||
vcoincidence = source >> 2;
|
||||
|
@ -71,7 +71,7 @@ PPU::Registers::BackgroundControl::operator uint16() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint16 PPU::Registers::BackgroundControl::operator=(uint16 source) {
|
||||
auto PPU::Registers::BackgroundControl::operator=(uint16 source) -> uint16 {
|
||||
priority = source >> 0;
|
||||
characterbaseblock = source >> 2;
|
||||
unused = source >> 4;
|
||||
|
@ -94,7 +94,7 @@ PPU::Registers::WindowFlags::operator uint8() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint8 PPU::Registers::WindowFlags::operator=(uint8 source) {
|
||||
auto PPU::Registers::WindowFlags::operator=(uint8 source) -> uint8 {
|
||||
enable[BG0] = source >> 0;
|
||||
enable[BG1] = source >> 1;
|
||||
enable[BG2] = source >> 2;
|
||||
|
@ -122,7 +122,7 @@ PPU::Registers::BlendControl::operator uint16() const {
|
|||
);
|
||||
}
|
||||
|
||||
uint16 PPU::Registers::BlendControl::operator=(uint16 source) {
|
||||
auto PPU::Registers::BlendControl::operator=(uint16 source) -> uint16 {
|
||||
above[BG0] = source >> 0;
|
||||
above[BG1] = source >> 1;
|
||||
above[BG2] = source >> 2;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
enum : unsigned { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 };
|
||||
enum : unsigned { In0 = 0, In1 = 1, Obj = 2, Out = 3 };
|
||||
enum : uint { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 };
|
||||
enum : uint { In0 = 0, In1 = 1, Obj = 2, Out = 3 };
|
||||
|
||||
struct Registers {
|
||||
struct Control {
|
||||
|
@ -13,8 +13,8 @@ struct Registers {
|
|||
uint1 enablewindow[3];
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
Control& operator=(const Control&) = delete;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const Control&) -> Control& = delete;
|
||||
} control;
|
||||
|
||||
uint1 greenswap;
|
||||
|
@ -29,8 +29,8 @@ struct Registers {
|
|||
uint8 vcompare;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
Status& operator=(const Status&) = delete;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const Status&) -> Status& = delete;
|
||||
} status;
|
||||
|
||||
uint16 vcounter;
|
||||
|
@ -46,8 +46,8 @@ struct Registers {
|
|||
uint2 screensize;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
BackgroundControl& operator=(const BackgroundControl&) = delete;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const BackgroundControl&) -> BackgroundControl& = delete;
|
||||
};
|
||||
|
||||
struct Background {
|
||||
|
@ -61,17 +61,17 @@ struct Registers {
|
|||
|
||||
//internal
|
||||
int28 lx, ly;
|
||||
unsigned vmosaic;
|
||||
unsigned hmosaic;
|
||||
unsigned id;
|
||||
uint vmosaic;
|
||||
uint hmosaic;
|
||||
uint id;
|
||||
} bg[4];
|
||||
|
||||
struct WindowFlags {
|
||||
uint1 enable[6];
|
||||
|
||||
operator uint8() const;
|
||||
uint8 operator=(uint8 source);
|
||||
WindowFlags& operator=(const WindowFlags&) = delete;
|
||||
auto operator=(uint8 source) -> uint8;
|
||||
auto operator=(const WindowFlags&) -> WindowFlags& = delete;
|
||||
};
|
||||
|
||||
struct Window {
|
||||
|
@ -94,8 +94,8 @@ struct Registers {
|
|||
uint2 mode;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
BlendControl& operator=(const BlendControl&) = delete;
|
||||
auto operator=(uint16 source) -> uint16;
|
||||
auto operator=(const BlendControl&) -> BlendControl& = delete;
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
void PPU::render_forceblank() {
|
||||
auto PPU::render_forceblank() -> void {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
line[x] = 0x7fff;
|
||||
}
|
||||
for(auto x : range(240)) line[x] = 0x7fff;
|
||||
}
|
||||
|
||||
void PPU::render_screen() {
|
||||
auto PPU::render_screen() -> void {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
|
||||
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
|
||||
|
@ -14,7 +12,7 @@ void PPU::render_screen() {
|
|||
if(regs.bg[3].control.mosaic) render_mosaic_background(BG3);
|
||||
render_mosaic_object();
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
for(auto x : range(240)) {
|
||||
Registers::WindowFlags flags;
|
||||
flags = ~0; //enable all layers if no windows are enabled
|
||||
|
||||
|
@ -27,9 +25,9 @@ void PPU::render_screen() {
|
|||
}
|
||||
|
||||
//priority sorting: find topmost two pixels
|
||||
unsigned a = 5, b = 5;
|
||||
for(signed p = 3; p >= 0; p--) {
|
||||
for(signed l = 5; l >= 0; l--) {
|
||||
uint a = 5, b = 5;
|
||||
for(int p = 3; p >= 0; p--) {
|
||||
for(int l = 5; l >= 0; l--) {
|
||||
if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) {
|
||||
b = a;
|
||||
a = l;
|
||||
|
@ -41,18 +39,21 @@ void PPU::render_screen() {
|
|||
auto& below = layer[b];
|
||||
bool blendabove = regs.blend.control.above[a];
|
||||
bool blendbelow = regs.blend.control.below[b];
|
||||
unsigned color = above[x].color;
|
||||
uint color = above[x].color;
|
||||
auto eva = min(16u, (unsigned)regs.blend.eva);
|
||||
auto evb = min(16u, (unsigned)regs.blend.evb);
|
||||
auto evy = min(16u, (unsigned)regs.blend.evy);
|
||||
|
||||
//perform blending, if needed
|
||||
if(flags.enable[SFX] == false) {
|
||||
} else if(above[x].translucent && blendbelow) {
|
||||
color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb);
|
||||
color = blend(above[x].color, eva, below[x].color, evb);
|
||||
} else if(regs.blend.control.mode == 1 && blendabove && blendbelow) {
|
||||
color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb);
|
||||
color = blend(above[x].color, eva, below[x].color, evb);
|
||||
} else if(regs.blend.control.mode == 2 && blendabove) {
|
||||
color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy);
|
||||
color = blend(above[x].color, 16 - evy, 0x7fff, evy);
|
||||
} else if(regs.blend.control.mode == 3 && blendabove) {
|
||||
color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy);
|
||||
color = blend(above[x].color, 16 - evy, 0x0000, evy);
|
||||
}
|
||||
|
||||
//output pixel
|
||||
|
@ -60,32 +61,29 @@ void PPU::render_screen() {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::render_window(unsigned w) {
|
||||
unsigned y = regs.vcounter;
|
||||
auto PPU::render_window(uint w) -> void {
|
||||
uint y = regs.vcounter;
|
||||
|
||||
unsigned y1 = regs.window[w].y1, y2 = regs.window[w].y2;
|
||||
unsigned x1 = regs.window[w].x1, x2 = regs.window[w].x2;
|
||||
uint y1 = regs.window[w].y1, y2 = regs.window[w].y2;
|
||||
uint x1 = regs.window[w].x1, x2 = regs.window[w].x2;
|
||||
|
||||
if(y2 < y1 || y2 > 160) y2 = 160;
|
||||
if(x2 < x1 || x2 > 240) x2 = 240;
|
||||
|
||||
if(y >= y1 && y < y2) {
|
||||
for(unsigned x = x1; x < x2; x++) {
|
||||
for(uint x = x1; x < x2; x++) {
|
||||
windowmask[w][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned PPU::blend(unsigned above, unsigned eva, unsigned below, unsigned evb) {
|
||||
eva = min(16, eva);
|
||||
evb = min(16, evb);
|
||||
|
||||
auto PPU::blend(uint above, uint eva, uint below, uint evb) -> uint {
|
||||
uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10;
|
||||
uint5 br = below >> 0, bg = below >> 5, bb = below >> 10;
|
||||
|
||||
unsigned r = (ar * eva + br * evb) >> 4;
|
||||
unsigned g = (ag * eva + bg * evb) >> 4;
|
||||
unsigned b = (ab * eva + bb * evb) >> 4;
|
||||
uint r = (ar * eva + br * evb) >> 4;
|
||||
uint g = (ag * eva + bg * evb) >> 4;
|
||||
uint b = (ab * eva + bb * evb) >> 4;
|
||||
|
||||
return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
void PPU::serialize(serializer& s) {
|
||||
auto PPU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(vram, 96 * 1024);
|
||||
|
@ -72,8 +72,8 @@ void PPU::serialize(serializer& s) {
|
|||
s.integer(regs.blend.evb);
|
||||
s.integer(regs.blend.evy);
|
||||
|
||||
for(unsigned l = 0; l < 6; l++) {
|
||||
for(unsigned p = 0; p < 240; p++) {
|
||||
for(auto l : range(6)) {
|
||||
for(auto p : range(240)) {
|
||||
auto& pixel = layer[l][p];
|
||||
s.integer(pixel.enable);
|
||||
s.integer(pixel.priority);
|
||||
|
@ -83,8 +83,8 @@ void PPU::serialize(serializer& s) {
|
|||
}
|
||||
}
|
||||
|
||||
for(unsigned w = 0; w < 3; w++) {
|
||||
for(unsigned p = 0; p < 240; p++) {
|
||||
for(auto w : range(3)) {
|
||||
for(auto p : range(240)) {
|
||||
s.integer(windowmask[w][p]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,22 +4,6 @@ namespace GameBoyAdvance {
|
|||
|
||||
Scheduler scheduler;
|
||||
|
||||
void Scheduler::enter() {
|
||||
host = co_active();
|
||||
co_switch(active);
|
||||
}
|
||||
|
||||
void Scheduler::exit(ExitReason reason) {
|
||||
exit_reason = reason;
|
||||
active = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
void Scheduler::power() {
|
||||
host = co_active();
|
||||
active = cpu.thread;
|
||||
}
|
||||
|
||||
Scheduler::Scheduler() {
|
||||
sync = SynchronizeMode::None;
|
||||
exit_reason = ExitReason::UnknownEvent;
|
||||
|
@ -27,4 +11,20 @@ Scheduler::Scheduler() {
|
|||
active = nullptr;
|
||||
}
|
||||
|
||||
auto Scheduler::enter() -> void {
|
||||
host = co_active();
|
||||
co_switch(active);
|
||||
}
|
||||
|
||||
auto Scheduler::exit(ExitReason reason) -> void {
|
||||
exit_reason = reason;
|
||||
active = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
active = cpu.thread;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
struct Scheduler : property<Scheduler> {
|
||||
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
||||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||
enum class SynchronizeMode : uint { None, CPU, All } sync;
|
||||
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
|
||||
readonly<ExitReason> exit_reason;
|
||||
|
||||
cothread_t host;
|
||||
cothread_t active;
|
||||
|
||||
void enter();
|
||||
void exit(ExitReason);
|
||||
|
||||
void power();
|
||||
Scheduler();
|
||||
auto enter() -> void;
|
||||
auto exit(ExitReason) -> void;
|
||||
auto power() -> void;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
||||
|
|
|
@ -7,7 +7,7 @@ BIOS::~BIOS() {
|
|||
delete[] data;
|
||||
}
|
||||
|
||||
auto BIOS::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto BIOS::read(uint mode, uint32 addr) -> uint32 {
|
||||
//unmapped memory
|
||||
if(addr >= 0x0000'4000) return cpu.pipeline.fetch.instruction; //0000'4000-01ff'ffff
|
||||
|
||||
|
@ -21,5 +21,5 @@ auto BIOS::read(unsigned mode, uint32 addr) -> uint32 {
|
|||
return mdr = data[addr];
|
||||
}
|
||||
|
||||
auto BIOS::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto BIOS::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
serializer System::serialize() {
|
||||
auto System::serialize() -> serializer {
|
||||
serializer s(serialize_size);
|
||||
|
||||
unsigned signature = 0x31545342, version = Info::SerializerVersion;
|
||||
uint signature = 0x31545342, version = Info::SerializerVersion;
|
||||
char hash[64], description[512];
|
||||
memcpy(&hash, (const char*)cartridge.sha256(), 64);
|
||||
memset(&description, 0, sizeof description);
|
||||
|
@ -15,8 +15,8 @@ serializer System::serialize() {
|
|||
return s;
|
||||
}
|
||||
|
||||
bool System::unserialize(serializer& s) {
|
||||
unsigned signature, version;
|
||||
auto System::unserialize(serializer& s) -> bool {
|
||||
uint signature, version;
|
||||
char hash[64], description[512];
|
||||
|
||||
s.integer(signature);
|
||||
|
@ -32,12 +32,12 @@ bool System::unserialize(serializer& s) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void System::serialize(serializer& s) {
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
s.integer(bios.size);
|
||||
s.integer(bios.mdr);
|
||||
}
|
||||
|
||||
void System::serialize_all(serializer& s) {
|
||||
auto System::serialize_all(serializer& s) -> void {
|
||||
cartridge.serialize(s);
|
||||
system.serialize(s);
|
||||
cpu.serialize(s);
|
||||
|
@ -46,10 +46,10 @@ void System::serialize_all(serializer& s) {
|
|||
player.serialize(s);
|
||||
}
|
||||
|
||||
void System::serialize_init() {
|
||||
auto System::serialize_init() -> void {
|
||||
serializer s;
|
||||
|
||||
unsigned signature = 0, version = 0;
|
||||
uint signature = 0, version = 0;
|
||||
char hash[64], description[512];
|
||||
|
||||
s.integer(signature);
|
||||
|
|
|
@ -7,13 +7,13 @@ namespace GameBoyAdvance {
|
|||
BIOS bios;
|
||||
System system;
|
||||
|
||||
void System::init() {
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
||||
void System::term() {
|
||||
auto System::term() -> void {
|
||||
}
|
||||
|
||||
void System::power() {
|
||||
auto System::power() -> void {
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
|
@ -23,7 +23,7 @@ void System::power() {
|
|||
scheduler.power();
|
||||
}
|
||||
|
||||
void System::load() {
|
||||
auto System::load() -> void {
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
|
@ -34,7 +34,7 @@ void System::load() {
|
|||
serialize_init();
|
||||
}
|
||||
|
||||
void System::run() {
|
||||
auto System::run() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
|
||||
|
@ -42,7 +42,7 @@ void System::run() {
|
|||
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
|
||||
}
|
||||
|
||||
void System::runtosave() {
|
||||
auto System::runtosave() -> void {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runthreadtosave();
|
||||
|
||||
|
@ -57,7 +57,7 @@ void System::runtosave() {
|
|||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
void System::runthreadtosave() {
|
||||
auto System::runthreadtosave() -> void {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
enum class Input : unsigned {
|
||||
enum class Input : uint {
|
||||
A, B, Select, Start, Right, Left, Up, Down, R, L,
|
||||
};
|
||||
|
||||
|
@ -6,35 +6,35 @@ struct BIOS : Memory {
|
|||
BIOS();
|
||||
~BIOS();
|
||||
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 override;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void override;
|
||||
auto read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
|
||||
uint8* data = nullptr;
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
uint32 mdr = 0;
|
||||
};
|
||||
|
||||
struct System {
|
||||
void init();
|
||||
void term();
|
||||
void load();
|
||||
void power();
|
||||
void run();
|
||||
void runtosave();
|
||||
void runthreadtosave();
|
||||
auto init() -> void;
|
||||
auto term() -> void;
|
||||
auto load() -> void;
|
||||
auto power() -> void;
|
||||
auto run() -> void;
|
||||
auto runtosave() -> void;
|
||||
auto runthreadtosave() -> void;
|
||||
|
||||
unsigned serialize_size;
|
||||
auto serialize() -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void serialize(serializer&);
|
||||
void serialize_all(serializer&);
|
||||
void serialize_init();
|
||||
auto serialize(serializer&) -> void;
|
||||
auto serialize_all(serializer&) -> void;
|
||||
auto serialize_init() -> void;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
} information;
|
||||
|
||||
uint serialize_size;
|
||||
};
|
||||
|
||||
extern BIOS bios;
|
||||
|
|
|
@ -4,16 +4,24 @@ namespace GameBoyAdvance {
|
|||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||
for(unsigned color = 0; color < (1 << 15); color++) {
|
||||
Video::Video() {
|
||||
palette = new uint32[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
auto Video::generatePalette(Emulator::Interface::PaletteMode mode) -> void {
|
||||
for(auto color : range(1 << 15)) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Literal) {
|
||||
palette[color] = color;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned B = (color >> 10) & 31;
|
||||
unsigned G = (color >> 5) & 31;
|
||||
unsigned R = (color >> 0) & 31;
|
||||
uint B = (color >> 10) & 31;
|
||||
uint G = (color >> 5) & 31;
|
||||
uint R = (color >> 0) & 31;
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Channel) {
|
||||
R = image::normalize(R, 5, 16);
|
||||
|
@ -32,43 +40,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
}
|
||||
|
||||
if(mode == Emulator::Interface::PaletteMode::Emulation) {
|
||||
#if 0
|
||||
R = curve[R];
|
||||
G = curve[G];
|
||||
B = curve[B];
|
||||
|
||||
unsigned Rr = R * 16;
|
||||
unsigned Gr = R * 4;
|
||||
unsigned Br = R * 4;
|
||||
|
||||
unsigned Rg = G * 8;
|
||||
unsigned Gg = G * 16;
|
||||
unsigned Bg = G * 8;
|
||||
|
||||
unsigned Rb = B * 0; //intentionally always zero
|
||||
unsigned Gb = B * 8;
|
||||
unsigned Bb = B * 16;
|
||||
|
||||
if(Rr < Rg) std::swap(Rr, Rg);
|
||||
if(Rr < Rb) std::swap(Rr, Rb);
|
||||
if(Rg < Rb) std::swap(Rg, Rb);
|
||||
|
||||
if(Gr < Gg) std::swap(Gr, Gg);
|
||||
if(Gr < Gb) std::swap(Gr, Gb);
|
||||
if(Gg < Gb) std::swap(Gg, Gb);
|
||||
|
||||
if(Br < Bg) std::swap(Br, Bg);
|
||||
if(Br < Bb) std::swap(Br, Bb);
|
||||
if(Bg < Bb) std::swap(Bg, Bb);
|
||||
|
||||
R = (((4 * Rr + 2 * Rg + Rb) * 160) >> 14) + 32;
|
||||
G = (((4 * Gr + 2 * Gg + Gb) * 160) >> 14) + 32;
|
||||
B = (((4 * Br + 2 * Bg + Bb) * 160) >> 14) + 32;
|
||||
|
||||
R = image::normalize(R, 8, 16);
|
||||
G = image::normalize(G, 8, 16);
|
||||
B = image::normalize(B, 8, 16);
|
||||
#else
|
||||
double lcdGamma = 4.0, outGamma = 2.2;
|
||||
double lb = pow(B / 31.0, lcdGamma);
|
||||
double lg = pow(G / 31.0, lcdGamma);
|
||||
|
@ -76,7 +47,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
B = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
G = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
R = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
|
||||
#endif
|
||||
|
||||
palette[color] = interface->videoColor(color, 0, R, G, B);
|
||||
continue;
|
||||
|
@ -86,14 +56,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32_t[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
const uint8 Video::curve[32] = {
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
|
||||
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
struct Video {
|
||||
uint32_t* palette = nullptr;
|
||||
void generate_palette(Emulator::Interface::PaletteMode mode);
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
auto generatePalette(Emulator::Interface::PaletteMode mode) -> void;
|
||||
|
||||
uint32* palette = nullptr;
|
||||
|
||||
private:
|
||||
static const uint8 curve[32];
|
||||
};
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
#if defined(Hiro_Object)
|
||||
struct Object : sObject {
|
||||
DeclareSharedObject(Object)
|
||||
|
||||
template<typename T> auto cast() -> T {
|
||||
if(auto pointer = dynamic_cast<typename T::internalType*>(data())) {
|
||||
if(auto shared = pointer->instance.acquire()) return T(shared);
|
||||
}
|
||||
return T();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -152,6 +159,7 @@ struct MenuCheckItem : sMenuCheckItem {
|
|||
#if defined(Hiro_MenuRadioItem)
|
||||
struct MenuRadioItem : sMenuRadioItem {
|
||||
DeclareSharedAction(MenuRadioItem)
|
||||
using internalType = mMenuRadioItem;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
@ -580,6 +588,7 @@ struct ProgressBar : sProgressBar {
|
|||
#if defined(Hiro_RadioButton)
|
||||
struct RadioButton : sRadioButton {
|
||||
DeclareSharedWidget(RadioButton)
|
||||
using internalType = mRadioButton;
|
||||
|
||||
auto bordered() const { return self().bordered(); }
|
||||
auto checked() const { return self().checked(); }
|
||||
|
@ -600,6 +609,7 @@ struct RadioButton : sRadioButton {
|
|||
#if defined(Hiro_RadioLabel)
|
||||
struct RadioLabel : sRadioLabel {
|
||||
DeclareSharedWidget(RadioLabel)
|
||||
using internalType = mRadioLabel;
|
||||
|
||||
auto checked() const { return self().checked(); }
|
||||
auto doActivate() const { return self().doActivate(); }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define NALL_ATOI_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/varint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
|
@ -22,10 +21,10 @@ constexpr inline auto octal_(const char* s, uintmax sum = 0) -> uintmax {
|
|||
);
|
||||
}
|
||||
|
||||
constexpr inline auto natural_(const char* s, uintmax sum = 0) -> uintmax {
|
||||
constexpr inline auto decimal_(const char* s, uintmax sum = 0) -> uintmax {
|
||||
return (
|
||||
*s >= '0' && *s <= '9' ? natural_(s + 1, (sum * 10) + *s - '0') :
|
||||
*s == '\'' ? natural_(s + 1, sum) :
|
||||
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||
*s == '\'' ? decimal_(s + 1, sum) :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
@ -44,44 +43,44 @@ constexpr inline auto hex_(const char* s, uintmax sum = 0) -> uintmax {
|
|||
|
||||
constexpr inline auto binary(const char* s) -> uintmax {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) :
|
||||
binary_(s)
|
||||
*s == '0' && (*(s + 1) == 'B' || *(s + 1) == 'b') ? binary_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) : binary_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline auto octal(const char* s) -> uintmax {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'O' ? octal_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'o' ? octal_(s + 2) :
|
||||
*s == '0' && (*(s + 1) == 'O' || *(s + 1) == 'o') ? octal_(s + 2) :
|
||||
octal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline auto integer(const char* s) -> intmax {
|
||||
return (
|
||||
*s == '+' ? +natural_(s + 1) :
|
||||
*s == '-' ? -natural_(s + 1) :
|
||||
natural_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline auto natural(const char* s) -> uintmax {
|
||||
return (
|
||||
natural_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline auto hex(const char* s) -> uintmax {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '$' ? hex_(s + 1) :
|
||||
hex_(s)
|
||||
*s == '0' && (*(s + 1) == 'X' || *(s + 1) == 'x') ? hex_(s + 2) :
|
||||
*s == '$' ? hex_(s + 1) : hex_(s)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
constexpr inline auto natural(const char* s) -> uintmax {
|
||||
return (
|
||||
*s == '0' && (*(s + 1) == 'B' || *(s + 1) == 'b') ? binary_(s + 2) :
|
||||
*s == '0' && (*(s + 1) == 'O' || *(s + 1) == 'o') ? octal_(s + 2) :
|
||||
*s == '0' && (*(s + 1) == 'X' || *(s + 1) == 'x') ? hex_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) : *s == '$' ? hex_(s + 1) : decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline auto integer(const char* s) -> intmax {
|
||||
return (
|
||||
*s == '+' ? +natural(s + 1) : *s == '-' ? -natural(s + 1) : natural(s)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
inline auto real(const char* s) -> double {
|
||||
return atof(s);
|
||||
}
|
||||
|
|
|
@ -5,47 +5,47 @@ namespace nall {
|
|||
|
||||
struct range_t {
|
||||
struct iterator {
|
||||
iterator(signed position, signed step = 0) : position(position), step(step) {}
|
||||
auto operator*() const -> signed { return position; }
|
||||
iterator(int position, int step = 0) : position(position), step(step) {}
|
||||
auto operator*() const -> int { return position; }
|
||||
auto operator!=(const iterator& source) const -> bool { return step > 0 ? position < source.position : position > source.position; }
|
||||
auto operator++() -> iterator& { position += step; return *this; }
|
||||
|
||||
private:
|
||||
signed position;
|
||||
const signed step;
|
||||
int position;
|
||||
const int step;
|
||||
};
|
||||
|
||||
auto begin() const -> const iterator { return iterator(origin, stride); }
|
||||
auto end() const -> const iterator { return iterator(target); }
|
||||
|
||||
signed origin;
|
||||
signed target;
|
||||
signed stride;
|
||||
int origin;
|
||||
int target;
|
||||
int stride;
|
||||
};
|
||||
|
||||
inline auto range(signed size) {
|
||||
inline auto range(int size) {
|
||||
return range_t{0, size, 1};
|
||||
}
|
||||
|
||||
inline auto range(signed offset, signed size) {
|
||||
inline auto range(int offset, int size) {
|
||||
return range_t{offset, size, 1};
|
||||
}
|
||||
|
||||
inline auto range(signed offset, signed size, signed step) {
|
||||
inline auto range(int offset, int size, int step) {
|
||||
return range_t{offset, size, step};
|
||||
}
|
||||
|
||||
//reverse-range
|
||||
inline auto rrange(signed size) {
|
||||
inline auto rrange(int size) {
|
||||
return range_t{size - 1, -1, -1};
|
||||
}
|
||||
|
||||
template<typename T> inline auto range(const vector<T>& container) {
|
||||
return range_t{0, (signed)container.size(), 1};
|
||||
return range_t{0, (int)container.size(), 1};
|
||||
}
|
||||
|
||||
template<typename T> inline auto rrange(const vector<T>& container) {
|
||||
return range_t{(signed)container.size() - 1, -1, -1};
|
||||
return range_t{(int)container.size() - 1, -1, -1};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ struct serial {
|
|||
}
|
||||
|
||||
//-1 on error, otherwise return bytes read
|
||||
auto read(uint8_t* data, unsigned length) -> int {
|
||||
auto read(uint8_t* data, uint length) -> int {
|
||||
if(port_open == false) return -1;
|
||||
return ::read(port, (void*)data, length);
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ struct serial {
|
|||
}
|
||||
|
||||
//-1 on error, otherwise return bytes written
|
||||
auto write(const uint8_t* data, unsigned length) -> int {
|
||||
auto write(const uint8_t* data, uint length) -> int {
|
||||
if(port_open == false) return -1;
|
||||
return ::write(port, (void*)data, length);
|
||||
}
|
||||
|
||||
auto open(const string& portname, unsigned rate, bool flowcontrol) -> bool {
|
||||
auto open(const string& portname, uint rate, bool flowcontrol) -> bool {
|
||||
close();
|
||||
|
||||
port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
|
||||
|
|
|
@ -28,21 +28,21 @@ struct has_serialize {
|
|||
};
|
||||
|
||||
struct serializer {
|
||||
enum mode_t { Load, Save, Size };
|
||||
enum Mode : uint { Load, Save, Size };
|
||||
|
||||
auto mode() const -> mode_t {
|
||||
auto mode() const -> Mode {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
auto data() const -> const uint8_t* {
|
||||
auto data() const -> const uint8* {
|
||||
return _data;
|
||||
}
|
||||
|
||||
auto size() const -> unsigned {
|
||||
auto size() const -> uint {
|
||||
return _size;
|
||||
}
|
||||
|
||||
auto capacity() const -> unsigned {
|
||||
auto capacity() const -> uint {
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
|
@ -50,11 +50,11 @@ struct serializer {
|
|||
enum { size = sizeof(T) };
|
||||
//this is rather dangerous, and not cross-platform safe;
|
||||
//but there is no standardized way to export FP-values
|
||||
uint8_t* p = (uint8_t*)&value;
|
||||
auto p = (uint8*)&value;
|
||||
if(_mode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) _data[_size++] = p[n];
|
||||
for(uint n = 0; n < size; n++) _data[_size++] = p[n];
|
||||
} else if(_mode == Load) {
|
||||
for(unsigned n = 0; n < size; n++) p[n] = _data[_size++];
|
||||
for(uint n = 0; n < size; n++) p[n] = _data[_size++];
|
||||
} else {
|
||||
_size += size;
|
||||
}
|
||||
|
@ -64,10 +64,10 @@ struct serializer {
|
|||
template<typename T> auto integer(T& value) -> serializer& {
|
||||
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
|
||||
if(_mode == Save) {
|
||||
for(unsigned n = 0; n < size; n++) _data[_size++] = (uintmax_t)value >> (n << 3);
|
||||
for(uint n = 0; n < size; n++) _data[_size++] = (uintmax)value >> (n << 3);
|
||||
} else if(_mode == Load) {
|
||||
value = 0;
|
||||
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)_data[_size++] << (n << 3);
|
||||
for(uint n = 0; n < size; n++) value |= (uintmax)_data[_size++] << (n << 3);
|
||||
} else if(_mode == Size) {
|
||||
_size += size;
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ struct serializer {
|
|||
}
|
||||
|
||||
template<typename T, int N> auto array(T (&array)[N]) -> serializer& {
|
||||
for(unsigned n = 0; n < N; n++) operator()(array[n]);
|
||||
for(uint n = 0; n < N; n++) operator()(array[n]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T> auto array(T array, unsigned size) -> serializer& {
|
||||
for(unsigned n = 0; n < size; n++) operator()(array[n]);
|
||||
template<typename T> auto array(T array, uint size) -> serializer& {
|
||||
for(uint n = 0; n < size; n++) operator()(array[n]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -88,13 +88,13 @@ struct serializer {
|
|||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); }
|
||||
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_array<T>::value>::type* = 0) -> serializer& { return array(value); }
|
||||
template<typename T> auto operator()(T& value, unsigned size, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) -> serializer& { return array(value, size); }
|
||||
template<typename T> auto operator()(T& value, uint size, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) -> serializer& { return array(value, size); }
|
||||
|
||||
auto operator=(const serializer& s) -> serializer& {
|
||||
if(_data) delete[] _data;
|
||||
|
||||
_mode = s._mode;
|
||||
_data = new uint8_t[s._capacity];
|
||||
_data = new uint8[s._capacity];
|
||||
_size = s._size;
|
||||
_capacity = s._capacity;
|
||||
|
||||
|
@ -118,16 +118,16 @@ struct serializer {
|
|||
serializer(const serializer& s) { operator=(s); }
|
||||
serializer(serializer&& s) { operator=(move(s)); }
|
||||
|
||||
serializer(unsigned capacity) {
|
||||
serializer(uint capacity) {
|
||||
_mode = Save;
|
||||
_data = new uint8_t[capacity]();
|
||||
_data = new uint8[capacity]();
|
||||
_size = 0;
|
||||
_capacity = capacity;
|
||||
}
|
||||
|
||||
serializer(const uint8_t* data, unsigned capacity) {
|
||||
serializer(const uint8* data, uint capacity) {
|
||||
_mode = Load;
|
||||
_data = new uint8_t[capacity];
|
||||
_data = new uint8[capacity];
|
||||
_size = 0;
|
||||
_capacity = capacity;
|
||||
memcpy(_data, data, capacity);
|
||||
|
@ -138,10 +138,10 @@ struct serializer {
|
|||
}
|
||||
|
||||
private:
|
||||
mode_t _mode = Size;
|
||||
uint8_t* _data = nullptr;
|
||||
unsigned _size = 0;
|
||||
unsigned _capacity = 0;
|
||||
Mode _mode = Size;
|
||||
uint8* _data = nullptr;
|
||||
uint _size = 0;
|
||||
uint _capacity = 0;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef NALL_STDINT_HPP
|
||||
#define NALL_STDINT_HPP
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
|
@ -46,6 +44,26 @@ static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size");
|
|||
static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size");
|
||||
static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size");
|
||||
|
||||
using int8 = int8_t;
|
||||
using int16 = int16_t;
|
||||
using int32 = int32_t;
|
||||
using int64 = int64_t;
|
||||
using intmax = intmax_t;
|
||||
using intptr = intptr_t;
|
||||
|
||||
using uint = unsigned int;
|
||||
using uint8 = uint8_t;
|
||||
using uint16 = uint16_t;
|
||||
using uint32 = uint32_t;
|
||||
using uint64 = uint64_t;
|
||||
using uintmax = uintmax_t;
|
||||
using uintptr = uintptr_t;
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
using int128 = int128_t;
|
||||
using uint128 = uint128_t;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,7 +41,7 @@ auto string::reset() -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::reserve(unsigned capacity) -> type& {
|
||||
auto string::reserve(uint capacity) -> type& {
|
||||
if(capacity <= _capacity) return *this;
|
||||
capacity = bit::round(capacity + 1) - 1;
|
||||
if(_capacity < SSO) {
|
||||
|
@ -57,7 +57,7 @@ auto string::reserve(unsigned capacity) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::resize(unsigned size) -> type& {
|
||||
auto string::resize(uint size) -> type& {
|
||||
reserve(size);
|
||||
get()[_size = size] = 0;
|
||||
return *this;
|
||||
|
@ -94,27 +94,27 @@ auto string::operator=(string&& source) -> type& {
|
|||
auto string::_allocate() -> void {
|
||||
char _temp[SSO];
|
||||
memory::copy(_temp, _text, SSO);
|
||||
_data = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
|
||||
_data = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
|
||||
memory::copy(_data, _temp, SSO);
|
||||
_refs = (unsigned*)(_data + _capacity + 1); //always aligned by 32 via reserve()
|
||||
_refs = (uint*)(_data + _capacity + 1); //always aligned by 32 via reserve()
|
||||
*_refs = 1;
|
||||
}
|
||||
|
||||
//COW -> Unique
|
||||
auto string::_copy() -> void {
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
|
||||
memory::copy(_temp, _data, _size = min(_capacity, _size));
|
||||
_temp[_size] = 0;
|
||||
--*_refs;
|
||||
_data = _temp;
|
||||
_refs = (unsigned*)(_data + _capacity + 1);
|
||||
_refs = (uint*)(_data + _capacity + 1);
|
||||
*_refs = 1;
|
||||
}
|
||||
|
||||
//COW -> Resize
|
||||
auto string::_resize() -> void {
|
||||
_data = (char*)memory::resize(_data, _capacity + 1 + sizeof(unsigned));
|
||||
_refs = (unsigned*)(_data + _capacity + 1);
|
||||
_data = (char*)memory::resize(_data, _capacity + 1 + sizeof(uint));
|
||||
_refs = (uint*)(_data + _capacity + 1);
|
||||
*_refs = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ auto string::reset() -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::reserve(unsigned capacity) -> type& {
|
||||
auto string::reserve(uint capacity) -> type& {
|
||||
if(capacity > _capacity) {
|
||||
_capacity = bit::round(max(31u, capacity) + 1) - 1;
|
||||
_data = _data ? _copy() : _allocate();
|
||||
|
@ -36,7 +36,7 @@ auto string::reserve(unsigned capacity) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::resize(unsigned size) -> type& {
|
||||
auto string::resize(uint size) -> type& {
|
||||
reserve(size);
|
||||
get()[_size = size] = 0;
|
||||
return *this;
|
||||
|
@ -70,19 +70,19 @@ auto string::operator=(string&& source) -> string& {
|
|||
}
|
||||
|
||||
auto string::_allocate() -> char* {
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
|
||||
*_temp = 0;
|
||||
_refs = (unsigned*)(_temp + _capacity + 1); //this will always be aligned by 32 via reserve()
|
||||
_refs = (uint*)(_temp + _capacity + 1); //this will always be aligned by 32 via reserve()
|
||||
*_refs = 1;
|
||||
return _temp;
|
||||
}
|
||||
|
||||
auto string::_copy() -> char* {
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned));
|
||||
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
|
||||
memory::copy(_temp, _data, _size = min(_capacity, _size));
|
||||
_temp[_size] = 0;
|
||||
--*_refs;
|
||||
_refs = (unsigned*)(_temp + _capacity + 1);
|
||||
_refs = (uint*)(_temp + _capacity + 1);
|
||||
*_refs = 1;
|
||||
return _temp;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ auto string::reset() -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::reserve(unsigned capacity) -> type& {
|
||||
auto string::reserve(uint capacity) -> type& {
|
||||
if(capacity <= _capacity) return *this;
|
||||
capacity = bit::round(capacity + 1) - 1;
|
||||
if(_capacity < SSO) {
|
||||
|
@ -58,7 +58,7 @@ auto string::reserve(unsigned capacity) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::resize(unsigned size) -> type& {
|
||||
auto string::resize(uint size) -> type& {
|
||||
reserve(size);
|
||||
get()[_size = size] = 0;
|
||||
return *this;
|
||||
|
|
|
@ -36,7 +36,7 @@ auto string::reset() -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::reserve(unsigned capacity) -> type& {
|
||||
auto string::reserve(uint capacity) -> type& {
|
||||
if(capacity > _capacity) {
|
||||
_capacity = bit::round(capacity + 1) - 1;
|
||||
_data = (char*)memory::resize(_data, _capacity + 1);
|
||||
|
@ -45,7 +45,7 @@ auto string::reserve(unsigned capacity) -> type& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto string::resize(unsigned size) -> type& {
|
||||
auto string::resize(uint size) -> type& {
|
||||
reserve(size);
|
||||
get()[_size = size] = 0;
|
||||
return *this;
|
||||
|
|
|
@ -3,16 +3,10 @@
|
|||
namespace nall {
|
||||
|
||||
auto string::integer() const -> intmax {
|
||||
if(beginsWith("0b") || beginsWith("0B")) return nall::binary(data());
|
||||
if(beginsWith("0o") || beginsWith("0O")) return nall::octal(data());
|
||||
if(beginsWith("0x") || beginsWith("0X")) return nall::hex(data());
|
||||
return nall::integer(data());
|
||||
}
|
||||
|
||||
auto string::natural() const -> uintmax {
|
||||
if(beginsWith("0b") || beginsWith("0B")) return nall::binary(data());
|
||||
if(beginsWith("0o") || beginsWith("0O")) return nall::octal(data());
|
||||
if(beginsWith("0x") || beginsWith("0X")) return nall::hex(data());
|
||||
return nall::natural(data());
|
||||
}
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@ template<typename T> struct stringify;
|
|||
|
||||
//format.hpp
|
||||
template<typename... P> inline auto print(P&&...) -> void;
|
||||
inline auto integer(intmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto decimal(uintmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto hex(uintmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto octal(uintmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto binary(uintmax_t value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto integer(intmax value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto decimal(uintmax value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string;
|
||||
inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string;
|
||||
template<typename T> inline auto pointer(const T* value, long precision = 0) -> string;
|
||||
inline auto pointer(uintptr_t value, long precision = 0) -> string;
|
||||
inline auto pointer(uintptr value, long precision = 0) -> string;
|
||||
inline auto real(long double value) -> string;
|
||||
|
||||
//hash.hpp
|
||||
|
@ -58,11 +58,11 @@ inline auto sharedpath() -> string;
|
|||
inline auto temppath() -> string;
|
||||
|
||||
//utility.hpp
|
||||
inline auto slice(rstring self, signed offset = 0, signed length = -1) -> string;
|
||||
inline auto slice(rstring self, int offset = 0, int length = -1) -> string;
|
||||
|
||||
inline auto integer(char* result, intmax_t value) -> char*;
|
||||
inline auto decimal(char* result, uintmax_t value) -> char*;
|
||||
inline auto real(char* str, long double value) -> unsigned;
|
||||
inline auto integer(char* result, intmax value) -> char*;
|
||||
inline auto decimal(char* result, uintmax value) -> char*;
|
||||
inline auto real(char* str, long double value) -> uint;
|
||||
|
||||
struct string {
|
||||
using type = string;
|
||||
|
@ -70,11 +70,11 @@ struct string {
|
|||
|
||||
protected:
|
||||
#if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
|
||||
enum : unsigned { SSO = 24 };
|
||||
enum : uint { SSO = 24 };
|
||||
union {
|
||||
struct { //copy-on-write
|
||||
char* _data;
|
||||
unsigned* _refs;
|
||||
uint* _refs;
|
||||
};
|
||||
struct { //small-string-optimization
|
||||
char _text[SSO];
|
||||
|
@ -87,13 +87,13 @@ protected:
|
|||
|
||||
#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
|
||||
char* _data;
|
||||
mutable unsigned* _refs;
|
||||
mutable uint* _refs;
|
||||
inline auto _allocate() -> char*;
|
||||
inline auto _copy() -> char*;
|
||||
#endif
|
||||
|
||||
#if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
|
||||
enum : unsigned { SSO = 24 };
|
||||
enum : uint { SSO = 24 };
|
||||
union {
|
||||
char* _data;
|
||||
char _text[SSO];
|
||||
|
@ -104,16 +104,16 @@ protected:
|
|||
char* _data;
|
||||
#endif
|
||||
|
||||
unsigned _capacity;
|
||||
unsigned _size;
|
||||
uint _capacity;
|
||||
uint _size;
|
||||
|
||||
public:
|
||||
inline string();
|
||||
inline auto get() -> char*;
|
||||
inline auto data() const -> const char*;
|
||||
inline auto reset() -> type&;
|
||||
inline auto reserve(unsigned) -> type&;
|
||||
inline auto resize(unsigned) -> type&;
|
||||
inline auto reserve(uint) -> type&;
|
||||
inline auto resize(uint) -> type&;
|
||||
inline auto operator=(const string&) -> type&;
|
||||
inline auto operator=(string&&) -> type&;
|
||||
|
||||
|
@ -125,8 +125,8 @@ public:
|
|||
operator const char*() const { return (const char*)data(); }
|
||||
|
||||
auto binary() const -> const uint8_t* { return (const uint8_t*)data(); }
|
||||
auto size() const -> unsigned { return _size; }
|
||||
auto capacity() const -> unsigned { return _capacity; }
|
||||
auto size() const -> uint { return _size; }
|
||||
auto capacity() const -> uint { return _capacity; }
|
||||
|
||||
auto operator==(const string& source) const -> bool { return size() == source.size() && memory::compare(data(), source.data(), size()) == 0; }
|
||||
auto operator!=(const string& source) const -> bool { return size() != source.size() || memory::compare(data(), source.data(), size()) != 0; }
|
||||
|
@ -155,14 +155,14 @@ public:
|
|||
inline auto real() const -> double;
|
||||
|
||||
//core.hpp
|
||||
inline auto operator[](signed) const -> const char&;
|
||||
inline auto operator[](int) const -> const char&;
|
||||
template<typename... P> inline auto assign(P&&...) -> type&;
|
||||
template<typename T, typename... P> inline auto append(const T&, P&&...) -> type&;
|
||||
template<typename... P> inline auto append(const nall::format&, P&&...) -> type&;
|
||||
inline auto append() -> type&;
|
||||
template<typename T> inline auto _append(const stringify<T>&) -> string&;
|
||||
inline auto empty() const -> bool;
|
||||
inline auto length() const -> unsigned;
|
||||
inline auto length() const -> uint;
|
||||
|
||||
//datetime.hpp
|
||||
inline static auto date(time_t = 0) -> string;
|
||||
|
@ -170,21 +170,21 @@ public:
|
|||
inline static auto datetime(time_t = 0) -> string;
|
||||
|
||||
//find.hpp
|
||||
template<bool, bool> inline auto _find(signed, rstring) const -> maybe<unsigned>;
|
||||
template<bool, bool> inline auto _find(int, rstring) const -> maybe<uint>;
|
||||
|
||||
inline auto find(rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto qfind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto iqfind(rstring source) const -> maybe<unsigned>;
|
||||
|
||||
inline auto findFrom(signed offset, rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifindFrom(signed offset, rstring source) const -> maybe<unsigned>;
|
||||
inline auto findFrom(int offset, rstring source) const -> maybe<uint>;
|
||||
inline auto ifindFrom(int offset, rstring source) const -> maybe<uint>;
|
||||
|
||||
//format.hpp
|
||||
inline auto format(const nall::format& params) -> type&;
|
||||
|
||||
//compare.hpp
|
||||
template<bool> inline static auto _compare(const char*, unsigned, const char*, unsigned) -> signed;
|
||||
template<bool> inline static auto _compare(const char*, uint, const char*, uint) -> signed;
|
||||
|
||||
inline static auto compare(rstring, rstring) -> signed;
|
||||
inline static auto icompare(rstring, rstring) -> signed;
|
||||
|
@ -242,12 +242,12 @@ public:
|
|||
|
||||
//utility.hpp
|
||||
inline static auto read(rstring filename) -> string;
|
||||
inline static auto repeat(rstring pattern, unsigned times) -> string;
|
||||
inline static auto repeat(rstring pattern, uint times) -> string;
|
||||
inline auto fill(char fill = ' ') -> type&;
|
||||
inline auto hash() const -> unsigned;
|
||||
inline auto remove(unsigned offset, unsigned length) -> type&;
|
||||
inline auto hash() const -> uint;
|
||||
inline auto remove(uint offset, uint length) -> type&;
|
||||
inline auto reverse() -> type&;
|
||||
inline auto size(signed length, char fill = ' ') -> type&;
|
||||
inline auto size(int length, char fill = ' ') -> type&;
|
||||
};
|
||||
|
||||
struct lstring : vector<string> {
|
||||
|
@ -255,7 +255,7 @@ struct lstring : vector<string> {
|
|||
|
||||
lstring(const lstring& source) { vector::operator=(source); }
|
||||
lstring(lstring& source) { vector::operator=(source); }
|
||||
lstring(lstring&& source) { vector::operator=(std::move(source)); }
|
||||
lstring(lstring&& source) { vector::operator=(move(source)); }
|
||||
template<typename... P> lstring(P&&... p) { append(forward<P>(p)...); }
|
||||
|
||||
//list.hpp
|
||||
|
@ -264,15 +264,15 @@ struct lstring : vector<string> {
|
|||
|
||||
inline auto operator=(const lstring& source) -> type& { return vector::operator=(source), *this; }
|
||||
inline auto operator=(lstring& source) -> type& { return vector::operator=(source), *this; }
|
||||
inline auto operator=(lstring&& source) -> type& { return vector::operator=(std::move(source)), *this; }
|
||||
inline auto operator=(lstring&& source) -> type& { return vector::operator=(move(source)), *this; }
|
||||
|
||||
inline auto isort() -> type&;
|
||||
|
||||
template<typename... P> inline auto append(const string&, P&&...) -> type&;
|
||||
inline auto append() -> type&;
|
||||
|
||||
inline auto find(rstring source) const -> maybe<unsigned>;
|
||||
inline auto ifind(rstring source) const -> maybe<unsigned>;
|
||||
inline auto find(rstring source) const -> maybe<uint>;
|
||||
inline auto ifind(rstring source) const -> maybe<uint>;
|
||||
inline auto match(rstring pattern) const -> lstring;
|
||||
inline auto merge(rstring separator) const -> string;
|
||||
inline auto strip() -> type&;
|
||||
|
|
|
@ -3,25 +3,25 @@
|
|||
namespace nall {
|
||||
|
||||
template<bool Insensitive>
|
||||
auto string::_compare(const char* target, unsigned capacity, const char* source, unsigned size) -> signed {
|
||||
auto string::_compare(const char* target, uint capacity, const char* source, uint size) -> signed {
|
||||
if(Insensitive) return memory::icompare(target, capacity, source, size);
|
||||
return memory::compare(target, capacity, source, size);
|
||||
}
|
||||
|
||||
//size() + 1 includes null-terminator; required to properly compare strings of differing lengths
|
||||
auto string::compare(rstring x, rstring y) -> signed {
|
||||
auto string::compare(rstring x, rstring y) -> int {
|
||||
return memory::compare(x.data(), x.size() + 1, y.data(), y.size() + 1);
|
||||
}
|
||||
|
||||
auto string::icompare(rstring x, rstring y) -> signed {
|
||||
auto string::icompare(rstring x, rstring y) -> int {
|
||||
return memory::icompare(x.data(), x.size() + 1, y.data(), y.size() + 1);
|
||||
}
|
||||
|
||||
auto string::compare(rstring source) const -> signed {
|
||||
auto string::compare(rstring source) const -> int {
|
||||
return memory::compare(data(), size() + 1, source.data(), source.size() + 1);
|
||||
}
|
||||
|
||||
auto string::icompare(rstring source) const -> signed {
|
||||
auto string::icompare(rstring source) const -> int {
|
||||
return memory::icompare(data(), size() + 1, source.data(), source.size() + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
auto string::operator[](signed position) const -> const char& {
|
||||
auto string::operator[](int position) const -> const char& {
|
||||
if(position > size() + 1) throw exception_out_of_bounds{};
|
||||
return data()[position];
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ auto string::empty() const -> bool {
|
|||
return size() == 0;
|
||||
}
|
||||
|
||||
auto string::length() const -> unsigned {
|
||||
auto string::length() const -> uint {
|
||||
return strlen(data());
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace nall {
|
||||
namespace Eval {
|
||||
|
||||
inline string evaluateExpression(Node* node) {
|
||||
inline auto evaluateExpression(Node* node) -> string {
|
||||
#define p(n) evaluateExpression(node->link[n])
|
||||
switch(node->type) {
|
||||
case Node::Type::Null: return "Null";
|
||||
|
@ -37,15 +37,8 @@ inline string evaluateExpression(Node* node) {
|
|||
throw "invalid operator";
|
||||
}
|
||||
|
||||
inline int64_t evaluateInteger(Node* node) {
|
||||
if(node->type == Node::Type::Literal) {
|
||||
if(node->literal.beginsWith("0b")) return nall::binary(node->literal);
|
||||
if(node->literal.beginsWith("0o")) return nall::octal(node->literal);
|
||||
if(node->literal.beginsWith("0x")) return nall::hex(node->literal);
|
||||
if(node->literal.beginsWith("%")) return nall::binary(node->literal);
|
||||
if(node->literal.beginsWith("$")) return nall::hex(node->literal);
|
||||
return nall::integer(node->literal);
|
||||
}
|
||||
inline auto evaluateInteger(Node* node) -> int64 {
|
||||
if(node->type == Node::Type::Literal) return nall::integer(node->literal);
|
||||
|
||||
#define p(n) evaluateInteger(node->link[n])
|
||||
switch(node->type) {
|
||||
|
@ -93,7 +86,7 @@ inline int64_t evaluateInteger(Node* node) {
|
|||
throw "invalid operator";
|
||||
}
|
||||
|
||||
inline maybe<int64_t> integer(const string& expression) {
|
||||
inline auto integer(const string& expression) -> maybe<int64> {
|
||||
try {
|
||||
auto tree = new Node;
|
||||
const char* p = expression;
|
||||
|
@ -106,7 +99,7 @@ inline maybe<int64_t> integer(const string& expression) {
|
|||
}
|
||||
}
|
||||
|
||||
inline long double evaluateReal(Node* node) {
|
||||
inline auto evaluateReal(Node* node) -> long double {
|
||||
if(node->type == Node::Type::Literal) return nall::real(node->literal);
|
||||
|
||||
#define p(n) evaluateReal(node->link[n])
|
||||
|
@ -138,7 +131,7 @@ inline long double evaluateReal(Node* node) {
|
|||
throw "invalid operator";
|
||||
}
|
||||
|
||||
inline maybe<long double> real(const string& expression) {
|
||||
inline auto real(const string& expression) -> maybe<long double> {
|
||||
try {
|
||||
auto tree = new Node;
|
||||
const char* p = expression;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace nall {
|
||||
namespace Eval {
|
||||
|
||||
inline bool isLiteral(const char*& s) {
|
||||
inline auto isLiteral(const char*& s) -> bool {
|
||||
char n = s[0];
|
||||
return (n >= 'A' && n <= 'Z')
|
||||
|| (n >= 'a' && n <= 'z')
|
||||
|
@ -12,7 +12,7 @@ inline bool isLiteral(const char*& s) {
|
|||
|| (n == '\'' || n == '\"');
|
||||
}
|
||||
|
||||
inline string literalNumber(const char*& s) {
|
||||
inline auto literalNumber(const char*& s) -> string {
|
||||
const char* p = s;
|
||||
|
||||
//binary
|
||||
|
@ -64,7 +64,7 @@ inline string literalNumber(const char*& s) {
|
|||
return result;
|
||||
}
|
||||
|
||||
inline string literalString(const char*& s) {
|
||||
inline auto literalString(const char*& s) -> string {
|
||||
const char* p = s;
|
||||
char escape = *p++;
|
||||
|
||||
|
@ -76,7 +76,7 @@ inline string literalString(const char*& s) {
|
|||
return result;
|
||||
}
|
||||
|
||||
inline string literalVariable(const char*& s) {
|
||||
inline auto literalVariable(const char*& s) -> string {
|
||||
const char* p = s;
|
||||
|
||||
while(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++;
|
||||
|
@ -86,7 +86,7 @@ inline string literalVariable(const char*& s) {
|
|||
return result;
|
||||
}
|
||||
|
||||
inline string literal(const char*& s) {
|
||||
inline auto literal(const char*& s) -> string {
|
||||
const char* p = s;
|
||||
|
||||
if(p[0] >= '0' && p[0] <= '9') return literalNumber(s);
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace nall {
|
|||
namespace Eval {
|
||||
|
||||
struct Node {
|
||||
enum class Type : unsigned {
|
||||
enum class Type : uint {
|
||||
Null,
|
||||
Literal,
|
||||
Function, Subscript, Member, SuffixIncrement, SuffixDecrement,
|
||||
|
|
|
@ -3,32 +3,32 @@
|
|||
namespace nall {
|
||||
namespace Eval {
|
||||
|
||||
inline bool whitespace(char n) {
|
||||
inline auto whitespace(char n) -> bool {
|
||||
return n == ' ' || n == '\t' || n == '\r' || n == '\n';
|
||||
}
|
||||
|
||||
inline void parse(Node*& node, const char*& s, unsigned depth) {
|
||||
auto unaryPrefix = [&](Node::Type type, unsigned seek, unsigned depth) {
|
||||
inline auto parse(Node*& node, const char*& s, uint depth) -> void {
|
||||
auto unaryPrefix = [&](Node::Type type, uint seek, uint depth) {
|
||||
auto parent = new Node(type);
|
||||
parse(parent->link(0) = new Node, s += seek, depth);
|
||||
node = parent;
|
||||
};
|
||||
|
||||
auto unarySuffix = [&](Node::Type type, unsigned seek, unsigned depth) {
|
||||
auto unarySuffix = [&](Node::Type type, uint seek, uint depth) {
|
||||
auto parent = new Node(type);
|
||||
parent->link(0) = node;
|
||||
parse(parent, s += seek, depth);
|
||||
node = parent;
|
||||
};
|
||||
|
||||
auto binary = [&](Node::Type type, unsigned seek, unsigned depth) {
|
||||
auto binary = [&](Node::Type type, uint seek, uint depth) {
|
||||
auto parent = new Node(type);
|
||||
parent->link(0) = node;
|
||||
parse(parent->link(1) = new Node, s += seek, depth);
|
||||
node = parent;
|
||||
};
|
||||
|
||||
auto ternary = [&](Node::Type type, unsigned seek, unsigned depth) {
|
||||
auto ternary = [&](Node::Type type, uint seek, uint depth) {
|
||||
auto parent = new Node(type);
|
||||
parent->link(0) = node;
|
||||
parse(parent->link(1) = new Node, s += seek, depth);
|
||||
|
@ -37,9 +37,9 @@ inline void parse(Node*& node, const char*& s, unsigned depth) {
|
|||
node = parent;
|
||||
};
|
||||
|
||||
auto separator = [&](Node::Type type, unsigned seek, unsigned depth) {
|
||||
auto separator = [&](Node::Type type, uint seek, uint depth) {
|
||||
if(node->type != Node::Type::Separator) return binary(type, seek, depth);
|
||||
unsigned n = node->link.size();
|
||||
uint n = node->link.size();
|
||||
parse(node->link(n) = new Node, s += seek, depth);
|
||||
};
|
||||
|
||||
|
@ -155,7 +155,7 @@ inline void parse(Node*& node, const char*& s, unsigned depth) {
|
|||
#undef p
|
||||
}
|
||||
|
||||
inline Node* parse(const string& expression) {
|
||||
inline auto parse(const string& expression) -> Node* {
|
||||
auto result = new Node;
|
||||
const char* p = expression;
|
||||
parse(result, p, 0);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
template<bool Insensitive, bool Quoted> auto string::_find(signed offset, rstring source) const -> maybe<unsigned> {
|
||||
template<bool Insensitive, bool Quoted> auto string::_find(int offset, rstring source) const -> maybe<uint> {
|
||||
if(source.size() == 0) return nothing;
|
||||
|
||||
auto p = data();
|
||||
for(unsigned n = offset, quoted = 0; n < size();) {
|
||||
for(uint n = offset, quoted = 0; n < size();) {
|
||||
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
|
||||
if(_compare<Insensitive>(p + n, size() - n, source.data(), source.size())) { n++; continue; }
|
||||
return n - offset;
|
||||
|
@ -15,13 +15,13 @@ template<bool Insensitive, bool Quoted> auto string::_find(signed offset, rstrin
|
|||
return nothing;
|
||||
}
|
||||
|
||||
auto string::find(rstring source) const -> maybe<unsigned> { return _find<0, 0>(0, source); }
|
||||
auto string::ifind(rstring source) const -> maybe<unsigned> { return _find<1, 0>(0, source); }
|
||||
auto string::qfind(rstring source) const -> maybe<unsigned> { return _find<0, 1>(0, source); }
|
||||
auto string::iqfind(rstring source) const -> maybe<unsigned> { return _find<1, 1>(0, source); }
|
||||
auto string::find(rstring source) const -> maybe<uint> { return _find<0, 0>(0, source); }
|
||||
auto string::ifind(rstring source) const -> maybe<uint> { return _find<1, 0>(0, source); }
|
||||
auto string::qfind(rstring source) const -> maybe<uint> { return _find<0, 1>(0, source); }
|
||||
auto string::iqfind(rstring source) const -> maybe<uint> { return _find<1, 1>(0, source); }
|
||||
|
||||
auto string::findFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<0, 0>(offset, source); }
|
||||
auto string::ifindFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<1, 0>(offset, source); }
|
||||
auto string::findFrom(int offset, rstring source) const -> maybe<uint> { return _find<0, 0>(offset, source); }
|
||||
auto string::ifindFrom(int offset, rstring source) const -> maybe<uint> { return _find<1, 0>(offset, source); }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ namespace nall {
|
|||
//each {#} token will be replaced with its appropriate format parameter
|
||||
|
||||
auto string::format(const nall::format& params) -> type& {
|
||||
signed size = this->size();
|
||||
char* data = (char*)memory::allocate(size);
|
||||
auto size = (int)this->size();
|
||||
auto data = (char*)memory::allocate(size);
|
||||
memory::copy(data, this->data(), size);
|
||||
|
||||
signed x = 0;
|
||||
|
@ -32,19 +32,19 @@ auto string::format(const nall::format& params) -> type& {
|
|||
};
|
||||
if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; }
|
||||
|
||||
unsigned index = nall::natural(&data[x + 1]);
|
||||
uint index = nall::natural(&data[x + 1]);
|
||||
if(index >= params.size()) { x++; continue; }
|
||||
|
||||
unsigned sourceSize = y - x;
|
||||
unsigned targetSize = params[index].size();
|
||||
unsigned remaining = size - x;
|
||||
uint sourceSize = y - x;
|
||||
uint targetSize = params[index].size();
|
||||
uint remaining = size - x;
|
||||
|
||||
if(sourceSize > targetSize) {
|
||||
unsigned difference = sourceSize - targetSize;
|
||||
uint difference = sourceSize - targetSize;
|
||||
memory::move(&data[x], &data[x + difference], remaining);
|
||||
size -= difference;
|
||||
} else if(targetSize > sourceSize) {
|
||||
unsigned difference = targetSize - sourceSize;
|
||||
uint difference = targetSize - sourceSize;
|
||||
data = (char*)realloc(data, size + difference);
|
||||
size += difference;
|
||||
memory::move(&data[x + difference], &data[x], remaining);
|
||||
|
@ -73,14 +73,14 @@ template<typename... P> auto print(P&&... p) -> void {
|
|||
fputs(s.data(), stdout);
|
||||
}
|
||||
|
||||
auto integer(intmax_t value, long precision, char padchar) -> string {
|
||||
auto integer(intmax value, long precision, char padchar) -> string {
|
||||
string buffer;
|
||||
buffer.resize(1 + sizeof(intmax_t) * 3);
|
||||
buffer.resize(1 + sizeof(intmax) * 3);
|
||||
char* p = buffer.get();
|
||||
|
||||
bool negative = value < 0;
|
||||
value = abs(value);
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
do {
|
||||
p[size++] = '0' + (value % 10);
|
||||
value /= 10;
|
||||
|
@ -92,12 +92,12 @@ auto integer(intmax_t value, long precision, char padchar) -> string {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
auto decimal(uintmax_t value, long precision, char padchar) -> string {
|
||||
auto decimal(uintmax value, long precision, char padchar) -> string {
|
||||
string buffer;
|
||||
buffer.resize(sizeof(uintmax_t) * 3);
|
||||
buffer.resize(sizeof(uintmax) * 3);
|
||||
char* p = buffer.get();
|
||||
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
do {
|
||||
p[size++] = '0' + (value % 10);
|
||||
value /= 10;
|
||||
|
@ -108,14 +108,14 @@ auto decimal(uintmax_t value, long precision, char padchar) -> string {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
auto hex(uintmax_t value, long precision, char padchar) -> string {
|
||||
auto hex(uintmax value, long precision, char padchar) -> string {
|
||||
string buffer;
|
||||
buffer.resize(sizeof(uintmax_t) * 2);
|
||||
buffer.resize(sizeof(uintmax) * 2);
|
||||
char* p = buffer.get();
|
||||
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
do {
|
||||
unsigned n = value & 15;
|
||||
uint n = value & 15;
|
||||
p[size++] = n < 10 ? '0' + n : 'a' + n - 10;
|
||||
value >>= 4;
|
||||
} while(value);
|
||||
|
@ -125,12 +125,12 @@ auto hex(uintmax_t value, long precision, char padchar) -> string {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
auto octal(uintmax_t value, long precision, char padchar) -> string {
|
||||
auto octal(uintmax value, long precision, char padchar) -> string {
|
||||
string buffer;
|
||||
buffer.resize(sizeof(uintmax_t) * 3);
|
||||
buffer.resize(sizeof(uintmax) * 3);
|
||||
char* p = buffer.get();
|
||||
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
do {
|
||||
p[size++] = '0' + (value & 7);
|
||||
value >>= 3;
|
||||
|
@ -141,12 +141,12 @@ auto octal(uintmax_t value, long precision, char padchar) -> string {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
auto binary(uintmax_t value, long precision, char padchar) -> string {
|
||||
auto binary(uintmax value, long precision, char padchar) -> string {
|
||||
string buffer;
|
||||
buffer.resize(sizeof(uintmax_t) * 8);
|
||||
buffer.resize(sizeof(uintmax) * 8);
|
||||
char* p = buffer.get();
|
||||
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
do {
|
||||
p[size++] = '0' + (value & 1);
|
||||
value >>= 1;
|
||||
|
@ -158,12 +158,12 @@ auto binary(uintmax_t value, long precision, char padchar) -> string {
|
|||
}
|
||||
|
||||
template<typename T> auto pointer(const T* value, long precision) -> string {
|
||||
if(value == nullptr) return "(null)";
|
||||
return {"0x", hex((uintptr_t)value, precision)};
|
||||
if(value == nullptr) return "(nullptr)";
|
||||
return {"0x", hex((uintptr)value, precision)};
|
||||
}
|
||||
|
||||
auto pointer(uintptr_t value, long precision) -> string {
|
||||
if(value == 0) return "(null)";
|
||||
auto pointer(uintptr value, long precision) -> string {
|
||||
if(value == 0) return "(nullptr)";
|
||||
return {"0x", hex(value, precision)};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace nall {
|
|||
auto lstring::operator==(const lstring& source) const -> bool {
|
||||
if(this == &source) return true;
|
||||
if(size() != source.size()) return false;
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
if(operator[](n) != source[n]) return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -32,15 +32,15 @@ auto lstring::append() -> lstring& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto lstring::find(rstring source) const -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
auto lstring::find(rstring source) const -> maybe<uint> {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
if(operator[](n).equals(source)) return n;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto lstring::ifind(rstring source) const -> maybe<unsigned> {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
auto lstring::ifind(rstring source) const -> maybe<uint> {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
if(operator[](n).iequals(source)) return n;
|
||||
}
|
||||
return nothing;
|
||||
|
@ -48,7 +48,7 @@ auto lstring::ifind(rstring source) const -> maybe<unsigned> {
|
|||
|
||||
auto lstring::match(rstring pattern) const -> lstring {
|
||||
lstring result;
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
if(operator[](n).match(pattern)) result.append(operator[](n));
|
||||
}
|
||||
return result;
|
||||
|
@ -56,7 +56,7 @@ auto lstring::match(rstring pattern) const -> lstring {
|
|||
|
||||
auto lstring::merge(rstring separator) const -> string {
|
||||
string output;
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
output.append(operator[](n));
|
||||
if(n < size() - 1) output.append(separator.data());
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ auto lstring::merge(rstring separator) const -> string {
|
|||
}
|
||||
|
||||
auto lstring::strip() -> lstring& {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
for(uint n = 0; n < size(); n++) {
|
||||
operator[](n).strip();
|
||||
}
|
||||
return *this;
|
||||
|
|
|
@ -19,22 +19,22 @@ protected:
|
|||
}
|
||||
|
||||
//determine indentation level, without incrementing pointer
|
||||
auto readDepth(const char* p) -> unsigned {
|
||||
unsigned depth = 0;
|
||||
auto readDepth(const char* p) -> uint {
|
||||
uint depth = 0;
|
||||
while(p[depth] == '\t' || p[depth] == ' ') depth++;
|
||||
return depth;
|
||||
}
|
||||
|
||||
//determine indentation level
|
||||
auto parseDepth(const char*& p) -> unsigned {
|
||||
unsigned depth = readDepth(p);
|
||||
auto parseDepth(const char*& p) -> uint {
|
||||
uint depth = readDepth(p);
|
||||
p += depth;
|
||||
return depth;
|
||||
}
|
||||
|
||||
//read name
|
||||
auto parseName(const char*& p) -> void {
|
||||
unsigned length = 0;
|
||||
uint length = 0;
|
||||
while(valid(p[length])) length++;
|
||||
if(length == 0) throw "Invalid node name";
|
||||
_name = slice(p, 0, length);
|
||||
|
@ -43,19 +43,19 @@ protected:
|
|||
|
||||
auto parseData(const char*& p) -> void {
|
||||
if(*p == '=' && *(p + 1) == '\"') {
|
||||
unsigned length = 2;
|
||||
uint length = 2;
|
||||
while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
|
||||
if(p[length] != '\"') throw "Unescaped value";
|
||||
_value = {slice(p, 2, length - 2), "\n"};
|
||||
p += length + 1;
|
||||
} else if(*p == '=') {
|
||||
unsigned length = 1;
|
||||
uint length = 1;
|
||||
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
|
||||
if(p[length] == '\"') throw "Illegal character in value";
|
||||
_value = {slice(p, 1, length - 1), "\n"};
|
||||
p += length;
|
||||
} else if(*p == ':') {
|
||||
unsigned length = 1;
|
||||
uint length = 1;
|
||||
while(p[length] && p[length] != '\n') length++;
|
||||
_value = {slice(p, 1, length - 1), "\n"};
|
||||
p += length;
|
||||
|
@ -70,7 +70,7 @@ protected:
|
|||
if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments
|
||||
|
||||
SharedNode node(new ManagedNode);
|
||||
unsigned length = 0;
|
||||
uint length = 0;
|
||||
while(valid(p[length])) length++;
|
||||
if(length == 0) throw "Invalid attribute name";
|
||||
node->_name = slice(p, 0, length);
|
||||
|
@ -81,7 +81,7 @@ protected:
|
|||
}
|
||||
|
||||
//read a node and all of its child nodes
|
||||
auto parseNode(const lstring& text, unsigned& y) -> void {
|
||||
auto parseNode(const lstring& text, uint& y) -> void {
|
||||
const char* p = text[y++];
|
||||
_metadata = parseDepth(p);
|
||||
parseName(p);
|
||||
|
@ -89,7 +89,7 @@ protected:
|
|||
parseAttributes(p);
|
||||
|
||||
while(y < text.size()) {
|
||||
unsigned depth = readDepth(text[y]);
|
||||
uint depth = readDepth(text[y]);
|
||||
if(depth <= _metadata) break;
|
||||
|
||||
if(text[y][depth] == ':') {
|
||||
|
@ -132,7 +132,7 @@ protected:
|
|||
if(document.size() == 0) return; //empty document
|
||||
|
||||
auto text = document.split("\n");
|
||||
unsigned y = 0;
|
||||
uint y = 0;
|
||||
while(y < text.size()) {
|
||||
SharedNode node(new ManagedNode);
|
||||
node->parseNode(text, y);
|
||||
|
@ -154,7 +154,7 @@ inline auto unserialize(const string& markup) -> Markup::Node {
|
|||
return (Markup::SharedNode&)node;
|
||||
}
|
||||
|
||||
inline auto serialize(const Markup::Node& node, unsigned depth = 0) -> string {
|
||||
inline auto serialize(const Markup::Node& node, uint depth = 0) -> string {
|
||||
if(!node.name()) {
|
||||
string result;
|
||||
for(auto leaf : node) {
|
||||
|
|
|
@ -7,7 +7,7 @@ auto ManagedNode::_evaluate(string query) const -> bool {
|
|||
if(!query) return true;
|
||||
|
||||
for(auto& rule : query.replace(" ", "").split(",")) {
|
||||
enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE };
|
||||
enum class Comparator : uint { ID, EQ, NE, LT, LE, GT, GE };
|
||||
auto comparator = Comparator::ID;
|
||||
if(rule.match("*!=*")) comparator = Comparator::NE;
|
||||
else if(rule.match("*<=*")) comparator = Comparator::LE;
|
||||
|
@ -58,7 +58,7 @@ auto ManagedNode::_find(const string& query) const -> vector<Node> {
|
|||
|
||||
lstring path = query.split("/");
|
||||
string name = path.take(0), rule;
|
||||
unsigned lo = 0u, hi = ~0u;
|
||||
uint lo = 0u, hi = ~0u;
|
||||
|
||||
if(name.match("*[*]")) {
|
||||
auto p = name.rtrim("]", 1L).split("[", 1L);
|
||||
|
@ -78,7 +78,7 @@ auto ManagedNode::_find(const string& query) const -> vector<Node> {
|
|||
rule = p(1);
|
||||
}
|
||||
|
||||
unsigned position = 0;
|
||||
uint position = 0;
|
||||
for(auto& node : _children) {
|
||||
if(!node->_name.match(name)) continue;
|
||||
if(!node->_evaluate(rule)) continue;
|
||||
|
|
|
@ -62,12 +62,13 @@ struct Node {
|
|||
auto boolean() const -> bool { return text() == "true"; }
|
||||
auto integer() const -> intmax { return text().integer(); }
|
||||
auto natural() const -> uintmax { return text().natural(); }
|
||||
auto real() const -> double { return text().real(); }
|
||||
|
||||
auto setName(const string& name = "") -> Node& { shared->_name = name; return *this; }
|
||||
auto setValue(const string& value = "") -> Node& { shared->_value = value; return *this; }
|
||||
|
||||
auto reset() -> void { shared->_children.reset(); }
|
||||
auto size() const -> unsigned { return shared->_children.size(); }
|
||||
auto size() const -> uint { return shared->_children.size(); }
|
||||
|
||||
auto prepend(const Node& node) -> void { shared->_children.prepend(node.shared); }
|
||||
auto append(const Node& node) -> void { shared->_children.append(node.shared); }
|
||||
|
@ -80,17 +81,17 @@ struct Node {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto insert(unsigned position, const Node& node) -> bool {
|
||||
auto insert(uint position, const Node& node) -> bool {
|
||||
if(position > size()) return false; //used > instead of >= to allow indexed-equivalent of append()
|
||||
return shared->_children.insert(position, node.shared), true;
|
||||
}
|
||||
|
||||
auto remove(unsigned position) -> bool {
|
||||
auto remove(uint position) -> bool {
|
||||
if(position >= size()) return false;
|
||||
return shared->_children.remove(position), true;
|
||||
}
|
||||
|
||||
auto swap(unsigned x, unsigned y) -> bool {
|
||||
auto swap(uint x, uint y) -> bool {
|
||||
if(x >= size() || y >= size()) return false;
|
||||
return std::swap(shared->_children[x], shared->_children[y]), true;
|
||||
}
|
||||
|
@ -103,7 +104,7 @@ struct Node {
|
|||
});
|
||||
}
|
||||
|
||||
auto operator[](signed position) -> Node {
|
||||
auto operator[](int position) -> Node {
|
||||
if(position >= size()) return {};
|
||||
return shared->_children[position];
|
||||
}
|
||||
|
@ -116,11 +117,11 @@ struct Node {
|
|||
auto operator*() -> Node { return {source.shared->_children[position]}; }
|
||||
auto operator!=(const iterator& source) const -> bool { return position != source.position; }
|
||||
auto operator++() -> iterator& { return position++, *this; }
|
||||
iterator(const Node& source, unsigned position) : source(source), position(position) {}
|
||||
iterator(const Node& source, uint position) : source(source), position(position) {}
|
||||
|
||||
private:
|
||||
const Node& source;
|
||||
unsigned position;
|
||||
uint position;
|
||||
};
|
||||
|
||||
auto begin() const -> iterator { return iterator(*this, 0); }
|
||||
|
@ -136,7 +137,7 @@ protected:
|
|||
namespace nall {
|
||||
|
||||
inline range_t range(const Markup::Node& node) {
|
||||
return range_t{0, (signed)node.size(), 1};
|
||||
return range_t{0, (int)node.size(), 1};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ protected:
|
|||
}
|
||||
|
||||
//copy part of string from source document into target string; decode markup while copying
|
||||
inline void copy(string& target, const char* source, unsigned length) {
|
||||
inline void copy(string& target, const char* source, uint length) {
|
||||
target.reserve(length + 1);
|
||||
|
||||
#if defined(NALL_XML_LITERAL)
|
||||
|
@ -106,7 +106,7 @@ protected:
|
|||
|
||||
//DOCTYPE
|
||||
if(!memory::compare(p, "<!DOCTYPE", 9)) {
|
||||
unsigned counter = 0;
|
||||
uint counter = 0;
|
||||
do {
|
||||
char n = *p++;
|
||||
if(!n) throw "unclosed DOCTYPE";
|
||||
|
|
|
@ -29,7 +29,7 @@ private:
|
|||
};
|
||||
vector<Variable> variables;
|
||||
|
||||
auto parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool;
|
||||
auto parseDocument(const string& filedata, const string& pathname, uint depth) -> bool;
|
||||
};
|
||||
|
||||
auto CML::parse(const string& filename) -> string {
|
||||
|
@ -45,7 +45,7 @@ auto CML::parse(const string& filedata, const string& pathname) -> string {
|
|||
return state.output;
|
||||
}
|
||||
|
||||
auto CML::parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool {
|
||||
auto CML::parseDocument(const string& filedata, const string& pathname, uint depth) -> bool {
|
||||
if(depth >= 100) return false; //prevent infinite recursion
|
||||
|
||||
auto vendorAppend = [&](const string& name, const string& value) {
|
||||
|
|
|
@ -27,12 +27,12 @@ private:
|
|||
|
||||
struct State {
|
||||
string output;
|
||||
unsigned sections = 0;
|
||||
uint sections = 0;
|
||||
} state;
|
||||
|
||||
auto parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool;
|
||||
auto parseBlock(string& block, const string& pathname, unsigned depth) -> bool;
|
||||
auto count(const string& text, char value) -> unsigned;
|
||||
auto parseDocument(const string& filedata, const string& pathname, uint depth) -> bool;
|
||||
auto parseBlock(string& block, const string& pathname, uint depth) -> bool;
|
||||
auto count(const string& text, char value) -> uint;
|
||||
|
||||
auto escape(const string& text) -> string;
|
||||
auto markup(const string& text) -> string;
|
||||
|
@ -51,7 +51,7 @@ auto DML::parse(const string& filename) -> string {
|
|||
return state.output;
|
||||
}
|
||||
|
||||
auto DML::parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool {
|
||||
auto DML::parseDocument(const string& filedata, const string& pathname, uint depth) -> bool {
|
||||
if(depth >= 100) return false; //attempt to prevent infinite recursion with reasonable limit
|
||||
|
||||
auto blocks = filedata.split("\n\n");
|
||||
|
@ -60,7 +60,7 @@ auto DML::parseDocument(const string& filedata, const string& pathname, unsigned
|
|||
return true;
|
||||
}
|
||||
|
||||
auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> bool {
|
||||
auto DML::parseBlock(string& block, const string& pathname, uint depth) -> bool {
|
||||
if(block.rstrip().empty()) return true;
|
||||
auto lines = block.split("\n");
|
||||
|
||||
|
@ -114,7 +114,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
//navigation
|
||||
else if(count(block, '-')) {
|
||||
state.output.append("<nav>\n");
|
||||
unsigned level = 0;
|
||||
uint level = 0;
|
||||
for(auto& line : lines) {
|
||||
if(auto depth = count(line, '-')) {
|
||||
while(level < depth) level++, state.output.append("<ul>\n");
|
||||
|
@ -131,7 +131,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
|
||||
//list
|
||||
else if(count(block, '*')) {
|
||||
unsigned level = 0;
|
||||
uint level = 0;
|
||||
for(auto& line : lines) {
|
||||
if(auto depth = count(line, '*')) {
|
||||
while(level < depth) level++, state.output.append("<ul>\n");
|
||||
|
@ -145,7 +145,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
|
||||
//quote
|
||||
else if(count(block, '>')) {
|
||||
unsigned level = 0;
|
||||
uint level = 0;
|
||||
for(auto& line : lines) {
|
||||
if(auto depth = count(line, '>')) {
|
||||
while(level < depth) level++, state.output.append("<blockquote>\n");
|
||||
|
@ -180,8 +180,8 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
|
|||
return true;
|
||||
}
|
||||
|
||||
auto DML::count(const string& text, char value) -> unsigned {
|
||||
for(unsigned n = 0; n < text.size(); n++) {
|
||||
auto DML::count(const string& text, char value) -> uint {
|
||||
for(uint n = 0; n < text.size(); n++) {
|
||||
if(text[n] != value) {
|
||||
if(text[n] == ' ') return n;
|
||||
break;
|
||||
|
@ -206,8 +206,8 @@ auto DML::markup(const string& text) -> string {
|
|||
string output;
|
||||
|
||||
char match = 0;
|
||||
unsigned offset = 0;
|
||||
for(unsigned n = 0; n < text.size();) {
|
||||
uint offset = 0;
|
||||
for(uint n = 0; n < text.size();) {
|
||||
char a = n ? text[n - 1] : 0;
|
||||
char b = text[n];
|
||||
char c = text[n++ + 1];
|
||||
|
|
|
@ -13,7 +13,7 @@ auto string::read(rstring filename) -> string {
|
|||
if(!fp) return result;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
signed filesize = ftell(fp);
|
||||
int filesize = ftell(fp);
|
||||
if(filesize < 0) return fclose(fp), result;
|
||||
|
||||
rewind(fp);
|
||||
|
@ -22,7 +22,7 @@ auto string::read(rstring filename) -> string {
|
|||
return fclose(fp), result;
|
||||
}
|
||||
|
||||
auto string::repeat(rstring pattern, unsigned times) -> string {
|
||||
auto string::repeat(rstring pattern, uint times) -> string {
|
||||
string result;
|
||||
while(times--) result.append(pattern.data());
|
||||
return result;
|
||||
|
@ -35,13 +35,13 @@ auto string::fill(char fill) -> string& {
|
|||
|
||||
auto string::hash() const -> unsigned {
|
||||
const char* p = data();
|
||||
unsigned length = size();
|
||||
unsigned result = 5381;
|
||||
uint length = size();
|
||||
uint result = 5381;
|
||||
while(length--) result = (result << 5) + result + *p++;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto string::remove(unsigned offset, unsigned length) -> string& {
|
||||
auto string::remove(uint offset, uint length) -> string& {
|
||||
char* p = get();
|
||||
length = min(length, size());
|
||||
memory::move(p + offset, p + offset + length, size() - length);
|
||||
|
@ -50,16 +50,16 @@ auto string::remove(unsigned offset, unsigned length) -> string& {
|
|||
|
||||
auto string::reverse() -> string& {
|
||||
char* p = get();
|
||||
unsigned length = size();
|
||||
unsigned pivot = length >> 1;
|
||||
for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
|
||||
uint length = size();
|
||||
uint pivot = length >> 1;
|
||||
for(int x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//+length => insert/delete from start (right justify)
|
||||
//-length => insert/delete from end (left justify)
|
||||
auto string::size(signed length, char fill) -> string& {
|
||||
unsigned size = this->size();
|
||||
auto string::size(int length, char fill) -> string& {
|
||||
uint size = this->size();
|
||||
if(size == length) return *this;
|
||||
|
||||
bool right = length >= 0;
|
||||
|
@ -68,13 +68,13 @@ auto string::size(signed length, char fill) -> string& {
|
|||
if(size < length) { //expand
|
||||
resize(length);
|
||||
char* p = get();
|
||||
unsigned displacement = length - size;
|
||||
uint displacement = length - size;
|
||||
if(right) memory::move(p + displacement, p, size);
|
||||
else p += size;
|
||||
while(displacement--) *p++ = fill;
|
||||
} else { //shrink
|
||||
char* p = get();
|
||||
unsigned displacement = size - length;
|
||||
uint displacement = size - length;
|
||||
if(right) memory::move(p, p + displacement, length);
|
||||
resize(length);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ auto string::size(signed length, char fill) -> string& {
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto slice(rstring self, signed offset, signed length) -> string {
|
||||
auto slice(rstring self, int offset, int length) -> string {
|
||||
string result;
|
||||
if(offset < self.size()) {
|
||||
if(length < 0) length = self.size() - offset;
|
||||
|
@ -92,36 +92,36 @@ auto slice(rstring self, signed offset, signed length) -> string {
|
|||
return result;
|
||||
}
|
||||
|
||||
auto integer(char* result, intmax_t value) -> char* {
|
||||
auto integer(char* result, intmax value) -> char* {
|
||||
bool negative = value < 0;
|
||||
if(negative) value = -value;
|
||||
|
||||
char buffer[64];
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
|
||||
do {
|
||||
unsigned n = value % 10;
|
||||
uint n = value % 10;
|
||||
buffer[size++] = '0' + n;
|
||||
value /= 10;
|
||||
} while(value);
|
||||
if(negative) buffer[size++] = '-';
|
||||
|
||||
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
|
||||
for(int x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
|
||||
result[size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto decimal(char* result, uintmax_t value) -> char* {
|
||||
auto decimal(char* result, uintmax value) -> char* {
|
||||
char buffer[64];
|
||||
unsigned size = 0;
|
||||
uint size = 0;
|
||||
|
||||
do {
|
||||
unsigned n = value % 10;
|
||||
uint n = value % 10;
|
||||
buffer[size++] = '0' + n;
|
||||
value /= 10;
|
||||
} while(value);
|
||||
|
||||
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
|
||||
for(int x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
|
||||
result[size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ auto decimal(char* result, uintmax_t value) -> char* {
|
|||
//using sprintf is certainly not the most ideal method to convert
|
||||
//a double to a string ... but attempting to parse a double by
|
||||
//hand, digit-by-digit, results in subtle rounding errors.
|
||||
auto real(char* result, long double value) -> unsigned {
|
||||
auto real(char* result, long double value) -> uint {
|
||||
char buffer[256];
|
||||
#ifdef _WIN32
|
||||
//Windows C-runtime does not support long double via sprintf()
|
||||
|
@ -150,7 +150,7 @@ auto real(char* result, long double value) -> unsigned {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned length = strlen(buffer);
|
||||
uint length = strlen(buffer);
|
||||
if(result) strcpy(result, buffer);
|
||||
return length + 1;
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ using int4 = nall::int_t<4>;
|
|||
using int5 = nall::int_t<5>;
|
||||
using int6 = nall::int_t<6>;
|
||||
using int7 = nall::int_t<7>;
|
||||
using int8 = int8_t;
|
||||
|
||||
using int9 = nall::int_t<9>;
|
||||
using int10 = nall::int_t<10>;
|
||||
using int11 = nall::int_t<11>;
|
||||
|
@ -159,7 +159,7 @@ using int12 = nall::int_t<12>;
|
|||
using int13 = nall::int_t<13>;
|
||||
using int14 = nall::int_t<14>;
|
||||
using int15 = nall::int_t<15>;
|
||||
using int16 = int16_t;
|
||||
|
||||
using int17 = nall::int_t<17>;
|
||||
using int18 = nall::int_t<18>;
|
||||
using int19 = nall::int_t<19>;
|
||||
|
@ -175,7 +175,7 @@ using int28 = nall::int_t<28>;
|
|||
using int29 = nall::int_t<29>;
|
||||
using int30 = nall::int_t<30>;
|
||||
using int31 = nall::int_t<31>;
|
||||
using int32 = int32_t;
|
||||
|
||||
using int33 = nall::int_t<33>;
|
||||
using int34 = nall::int_t<34>;
|
||||
using int35 = nall::int_t<35>;
|
||||
|
@ -207,9 +207,6 @@ using int60 = nall::int_t<60>;
|
|||
using int61 = nall::int_t<61>;
|
||||
using int62 = nall::int_t<62>;
|
||||
using int63 = nall::int_t<63>;
|
||||
using int64 = int64_t;
|
||||
using intmax = intmax_t;
|
||||
using intptr = intptr_t;
|
||||
|
||||
using uint1 = nall::uint_t<1>;
|
||||
using uint2 = nall::uint_t<2>;
|
||||
|
@ -218,7 +215,7 @@ using uint4 = nall::uint_t<4>;
|
|||
using uint5 = nall::uint_t<5>;
|
||||
using uint6 = nall::uint_t<6>;
|
||||
using uint7 = nall::uint_t<7>;
|
||||
using uint8 = uint8_t;
|
||||
|
||||
using uint9 = nall::uint_t<9>;
|
||||
using uint10 = nall::uint_t<10>;
|
||||
using uint11 = nall::uint_t<11>;
|
||||
|
@ -226,7 +223,7 @@ using uint12 = nall::uint_t<12>;
|
|||
using uint13 = nall::uint_t<13>;
|
||||
using uint14 = nall::uint_t<14>;
|
||||
using uint15 = nall::uint_t<15>;
|
||||
using uint16 = uint16_t;
|
||||
|
||||
using uint17 = nall::uint_t<17>;
|
||||
using uint18 = nall::uint_t<18>;
|
||||
using uint19 = nall::uint_t<19>;
|
||||
|
@ -242,7 +239,7 @@ using uint28 = nall::uint_t<28>;
|
|||
using uint29 = nall::uint_t<29>;
|
||||
using uint30 = nall::uint_t<30>;
|
||||
using uint31 = nall::uint_t<31>;
|
||||
using uint32 = uint32_t;
|
||||
|
||||
using uint33 = nall::uint_t<33>;
|
||||
using uint34 = nall::uint_t<34>;
|
||||
using uint35 = nall::uint_t<35>;
|
||||
|
@ -274,13 +271,5 @@ using uint60 = nall::uint_t<60>;
|
|||
using uint61 = nall::uint_t<61>;
|
||||
using uint62 = nall::uint_t<62>;
|
||||
using uint63 = nall::uint_t<63>;
|
||||
using uint64 = uint64_t;
|
||||
using uintmax = uintmax_t;
|
||||
using uintptr = uintptr_t;
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
using int128 = int128_t;
|
||||
using uint128 = uint128_t;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Video video;
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32_t[1 << 19]();
|
||||
palette = new uint32[1 << 19]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
|
@ -88,15 +88,15 @@ auto Video::draw_cursor(uint16 color, int x, int y) -> void {
|
|||
for(int cx = 0; cx < 15; cx++) {
|
||||
int vx = x + cx - 7;
|
||||
if(vx < 0 || vx >= 256) continue; //do not draw offscreen
|
||||
uint8_t pixel = cursor[cy * 15 + cx];
|
||||
uint8 pixel = cursor[cy * 15 + cx];
|
||||
if(pixel == 0) continue;
|
||||
uint32_t pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color);
|
||||
uint32 pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color);
|
||||
|
||||
if(hires == false) {
|
||||
*((uint32_t*)data + vy * 1024 + vx) = pixelcolor;
|
||||
*((uint32*)data + vy * 1024 + vx) = pixelcolor;
|
||||
} else {
|
||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
||||
*((uint32_t*)data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
||||
*((uint32*)data + vy * 1024 + vx * 2 + 0) = pixelcolor;
|
||||
*((uint32*)data + vy * 1024 + vx * 2 + 1) = pixelcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,14 +106,14 @@ auto Video::update() -> void {
|
|||
switch(configuration.controllerPort2) {
|
||||
case Device::ID::SuperScope:
|
||||
if(dynamic_cast<SuperScope*>(device.controllerPort2)) {
|
||||
auto controller = (SuperScope&)*device.controllerPort2;
|
||||
auto& controller = (SuperScope&)*device.controllerPort2;
|
||||
draw_cursor(0x7c00, controller.x, controller.y);
|
||||
}
|
||||
break;
|
||||
case Device::ID::Justifier:
|
||||
case Device::ID::Justifiers:
|
||||
if(dynamic_cast<Justifier*>(device.controllerPort2)) {
|
||||
auto controller = (Justifier&)*device.controllerPort2;
|
||||
auto& controller = (Justifier&)*device.controllerPort2;
|
||||
draw_cursor(0x001f, controller.player1.x, controller.player1.y);
|
||||
if(!controller.chained) break;
|
||||
draw_cursor(0x02e0, controller.player2.x, controller.player2.y);
|
||||
|
|
|
@ -1,76 +1,49 @@
|
|||
#include "../tomoko.hpp"
|
||||
ConfigurationManager* config = nullptr;
|
||||
EmulatorSettings* emulatorSettings = nullptr;
|
||||
Settings settings;
|
||||
|
||||
ConfigurationManager::ConfigurationManager() {
|
||||
config = this;
|
||||
Settings::Settings() {
|
||||
Markup::Node::operator=(BML::unserialize(string::read(locate({configpath(), "tomoko/"}, "settings.bml"))));
|
||||
|
||||
userInterface.append(userInterface.showStatusBar, "ShowStatusBar");
|
||||
append(userInterface, "UserInterface");
|
||||
auto set = [&](const string& name, const string& value) {
|
||||
//create node and set to default value only if it does not already exist
|
||||
if(!operator[](name)) operator()(name).setValue(value);
|
||||
};
|
||||
|
||||
library.append(library.location, "Location");
|
||||
append(library, "Library");
|
||||
set("UserInterface/ShowStatusBar", true);
|
||||
|
||||
video.append(video.driver, "Driver");
|
||||
video.append(video.synchronize, "Synchronize");
|
||||
video.append(video.scale, "Scale");
|
||||
video.append(video.aspectCorrection, "AspectCorrection");
|
||||
video.append(video.filter, "Filter");
|
||||
video.append(video.shader, "Shader");
|
||||
video.append(video.colorEmulation, "ColorEmulation");
|
||||
video.append(video.saturation, "Saturation");
|
||||
video.append(video.gamma, "Gamma");
|
||||
video.append(video.luminance, "Luminance");
|
||||
video.overscan.append(video.overscan.mask, "Mask");
|
||||
video.overscan.append(video.overscan.horizontal, "Horizontal");
|
||||
video.overscan.append(video.overscan.vertical, "Vertical");
|
||||
video.append(video.overscan, "Overscan");
|
||||
append(video, "Video");
|
||||
set("Library/Location", {userpath(), "Emulation/"});
|
||||
set("Library/IgnoreManifests", false);
|
||||
|
||||
audio.append(audio.driver, "Driver");
|
||||
audio.append(audio.device, "Device");
|
||||
audio.append(audio.synchronize, "Synchronize");
|
||||
audio.append(audio.mute, "Mute");
|
||||
audio.append(audio.volume, "Volume");
|
||||
audio.append(audio.frequency, "Frequency");
|
||||
audio.append(audio.latency, "Latency");
|
||||
audio.append(audio.resampler, "Resampler");
|
||||
append(audio, "Audio");
|
||||
set("Video/Driver", ruby::Video::optimalDriver());
|
||||
set("Video/Synchronize", false);
|
||||
set("Video/Scale", "Small");
|
||||
set("Video/AspectCorrection", true);
|
||||
set("Video/Filter", "Blur");
|
||||
set("Video/Shader", "None");
|
||||
set("Video/ColorEmulation", true);
|
||||
set("Video/Saturation", 100);
|
||||
set("Video/Gamma", 100);
|
||||
set("Video/Luminance", 100);
|
||||
|
||||
input.append(input.driver, "Driver");
|
||||
append(input, "Input");
|
||||
set("Video/Overscan/Mask", false);
|
||||
set("Video/Overscan/Horizontal", 8);
|
||||
set("Video/Overscan/Vertical", 8);
|
||||
|
||||
timing.append(timing.video, "Video");
|
||||
timing.append(timing.audio, "Audio");
|
||||
append(timing, "Timing");
|
||||
set("Audio/Driver", ruby::Audio::optimalDriver());
|
||||
set("Audio/Device", "");
|
||||
set("Audio/Synchronize", true);
|
||||
set("Audio/Mute", false);
|
||||
set("Audio/Volume", 100);
|
||||
set("Audio/Frequency", 48000);
|
||||
set("Audio/Latency", 60);
|
||||
set("Audio/Resampler", "Sinc");
|
||||
|
||||
load(locate({configpath(), "tomoko/"}, "settings.bml"));
|
||||
if(!library.location) library.location = {userpath(), "Emulation/"};
|
||||
if(!video.driver) video.driver = ruby::Video::safestDriver();
|
||||
if(!audio.driver) audio.driver = ruby::Audio::safestDriver();
|
||||
if(!input.driver) input.driver = ruby::Input::safestDriver();
|
||||
save(locate({configpath(), "tomoko/"}, "settings.bml"));
|
||||
set("Input/Driver", ruby::Input::optimalDriver());
|
||||
|
||||
set("Timing/Video", 60.0);
|
||||
set("Timing/Audio", 48000.0);
|
||||
}
|
||||
|
||||
auto ConfigurationManager::quit() -> void {
|
||||
save(locate({configpath(), "tomoko/"}, "settings.bml"));
|
||||
}
|
||||
|
||||
EmulatorSettings::EmulatorSettings() {
|
||||
emulatorSettings = this;
|
||||
(Markup::Node&)*this = BML::unserialize(string::read(locate({configpath(), "tomoko/"}, "emulators.bml")));
|
||||
}
|
||||
|
||||
auto EmulatorSettings::quit() -> void {
|
||||
file::write(locate({configpath(), "tomoko/"}, "emulators.bml"), BML::serialize(*this));
|
||||
}
|
||||
|
||||
auto EmulatorSettings::get(string name) -> string {
|
||||
name.replace(" ", "");
|
||||
return (*this)(name).text();
|
||||
}
|
||||
|
||||
auto EmulatorSettings::set(string name, string value) -> void {
|
||||
name.replace(" ", "");
|
||||
(*this)(name).setValue(value);
|
||||
auto Settings::quit() -> void {
|
||||
file::write(locate({configpath(), "tomoko/"}, "settings.bml"), BML::serialize(*this));
|
||||
}
|
||||
|
|
|
@ -1,62 +1,6 @@
|
|||
struct ConfigurationManager : Configuration::Document {
|
||||
ConfigurationManager();
|
||||
struct Settings : Markup::Node {
|
||||
Settings();
|
||||
auto quit() -> void;
|
||||
|
||||
struct UserInterface : Configuration::Node {
|
||||
bool showStatusBar = true;
|
||||
} userInterface;
|
||||
|
||||
struct Library : Configuration::Node {
|
||||
string location;
|
||||
} library;
|
||||
|
||||
struct Video : Configuration::Node {
|
||||
string driver;
|
||||
bool synchronize = false;
|
||||
string scale = "Small";
|
||||
bool aspectCorrection = true;
|
||||
string filter = "Blur";
|
||||
string shader = "None";
|
||||
bool colorEmulation = true;
|
||||
unsigned saturation = 100;
|
||||
unsigned gamma = 100;
|
||||
unsigned luminance = 100;
|
||||
|
||||
struct Overscan : Configuration::Node {
|
||||
bool mask = false;
|
||||
unsigned horizontal = 8;
|
||||
unsigned vertical = 8;
|
||||
} overscan;
|
||||
} video;
|
||||
|
||||
struct Audio : Configuration::Node {
|
||||
string driver;
|
||||
string device;
|
||||
bool synchronize = true;
|
||||
bool mute = false;
|
||||
unsigned volume = 100;
|
||||
unsigned frequency = 48000;
|
||||
unsigned latency = 60;
|
||||
string resampler = "Sinc";
|
||||
} audio;
|
||||
|
||||
struct Input : Configuration::Node {
|
||||
string driver;
|
||||
} input;
|
||||
|
||||
struct Timing : Configuration::Node {
|
||||
double video = 60.0;
|
||||
double audio = 48000.0;
|
||||
} timing;
|
||||
};
|
||||
|
||||
struct EmulatorSettings : Markup::Node {
|
||||
EmulatorSettings();
|
||||
auto quit() -> void;
|
||||
|
||||
auto get(string name) -> string;
|
||||
auto set(string name, string value) -> void;
|
||||
};
|
||||
|
||||
extern ConfigurationManager* config;
|
||||
extern EmulatorSettings* emulatorSettings;
|
||||
extern Settings settings;
|
||||
|
|
|
@ -46,8 +46,8 @@ auto InputManager::appendHotkeys() -> void {
|
|||
audio->set(Audio::Synchronize, false);
|
||||
};
|
||||
hotkey->release = [] {
|
||||
video->set(Video::Synchronize, ::config->video.synchronize);
|
||||
audio->set(Audio::Synchronize, ::config->audio.synchronize);
|
||||
video->set(Video::Synchronize, settings["Video/Synchronize"].boolean());
|
||||
audio->set(Audio::Synchronize, settings["Audio/Synchronize"].boolean());
|
||||
};
|
||||
hotkeys.append(hotkey);
|
||||
}
|
||||
|
@ -68,11 +68,11 @@ auto InputManager::appendHotkeys() -> void {
|
|||
hotkeys.append(hotkey);
|
||||
}
|
||||
|
||||
Configuration::Node nodeHotkeys;
|
||||
for(auto& hotkey : hotkeys) {
|
||||
nodeHotkeys.append(hotkey->assignment, string{hotkey->name}.replace(" ", ""));
|
||||
hotkey->path = string{"Hotkey/", hotkey->name}.replace(" ", "");
|
||||
hotkey->assignment = settings(hotkey->path).text();
|
||||
hotkey->bind();
|
||||
}
|
||||
config.append(nodeHotkeys, "Hotkeys");
|
||||
}
|
||||
|
||||
auto InputManager::pollHotkeys() -> void {
|
||||
|
|
|
@ -22,6 +22,8 @@ auto InputMapping::bind() -> void {
|
|||
if(qualifier == "Rumble") this->qualifier = Qualifier::Rumble;
|
||||
break;
|
||||
}
|
||||
|
||||
settings[path].setValue(assignment);
|
||||
}
|
||||
|
||||
auto InputMapping::bind(shared_pointer<HID::Device> device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool {
|
||||
|
@ -110,11 +112,12 @@ auto InputMapping::rumble(bool enable) -> void {
|
|||
}
|
||||
|
||||
auto InputMapping::unbind() -> void {
|
||||
this->assignment = "None";
|
||||
this->device = nullptr;
|
||||
this->group = 0;
|
||||
this->input = 0;
|
||||
this->qualifier = Qualifier::None;
|
||||
assignment = "None";
|
||||
device = nullptr;
|
||||
group = 0;
|
||||
input = 0;
|
||||
qualifier = Qualifier::None;
|
||||
settings[path].setValue(assignment);
|
||||
}
|
||||
|
||||
auto InputMapping::assignmentName() -> string {
|
||||
|
@ -140,21 +143,15 @@ InputManager::InputManager() {
|
|||
inputManager = this;
|
||||
|
||||
for(auto& emulator : program->emulators) {
|
||||
Configuration::Node nodeEmulator;
|
||||
|
||||
emulators.append(InputEmulator());
|
||||
auto& inputEmulator = emulators.last();
|
||||
inputEmulator.name = emulator->information.name;
|
||||
|
||||
for(auto& port : emulator->port) {
|
||||
Configuration::Node nodePort;
|
||||
|
||||
inputEmulator.ports.append(InputPort());
|
||||
auto& inputPort = inputEmulator.ports.last();
|
||||
inputPort.name = port.name;
|
||||
for(auto& device : port.device) {
|
||||
Configuration::Node nodeDevice;
|
||||
|
||||
inputPort.devices.append(InputDevice());
|
||||
auto& inputDevice = inputPort.devices.last();
|
||||
inputDevice.name = device.name;
|
||||
|
@ -164,23 +161,17 @@ InputManager::InputManager() {
|
|||
auto& inputMapping = inputDevice.mappings.last();
|
||||
inputMapping->name = input.name;
|
||||
inputMapping->link = &input;
|
||||
input.guid = (uintptr_t)inputMapping;
|
||||
input.guid = (uintptr)inputMapping;
|
||||
|
||||
nodeDevice.append(inputMapping->assignment, string{inputMapping->name}.replace(" ", ""));
|
||||
inputMapping->path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping->name}.replace(" ", "");
|
||||
inputMapping->assignment = settings(inputMapping->path).text();
|
||||
inputMapping->bind();
|
||||
}
|
||||
|
||||
nodePort.append(nodeDevice, string{inputDevice.name}.replace(" ", ""));
|
||||
}
|
||||
|
||||
nodeEmulator.append(nodePort, string{inputPort.name}.replace(" ", ""));
|
||||
}
|
||||
|
||||
config.append(nodeEmulator, string{inputEmulator.name}.replace(" ", ""));
|
||||
}
|
||||
|
||||
appendHotkeys();
|
||||
config.load(locate({configpath(), "tomoko/"}, "input.bml"));
|
||||
config.save(locate({configpath(), "tomoko/"}, "input.bml"));
|
||||
}
|
||||
|
||||
auto InputManager::bind() -> void {
|
||||
|
@ -224,7 +215,6 @@ auto InputManager::onChange(shared_pointer<HID::Device> device, unsigned group,
|
|||
}
|
||||
|
||||
auto InputManager::quit() -> void {
|
||||
config.save(locate({configpath(), "tomoko/"}, "input.bml"));
|
||||
emulators.reset();
|
||||
hotkeys.reset();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ struct InputMapping {
|
|||
auto assignmentName() -> string;
|
||||
auto deviceName() -> string;
|
||||
|
||||
string name;
|
||||
string path; //configuration file key path
|
||||
string name; //input name (human readable)
|
||||
string assignment = "None";
|
||||
Emulator::Interface::Device::Input* link = nullptr;
|
||||
shared_pointer<HID::Device> device;
|
||||
|
@ -59,7 +60,6 @@ struct InputManager {
|
|||
vector<shared_pointer<HID::Device>> devices;
|
||||
vector<InputEmulator> emulators;
|
||||
vector<InputHotkey*> hotkeys;
|
||||
Configuration::Document config;
|
||||
};
|
||||
|
||||
extern InputManager* inputManager;
|
||||
|
|
|
@ -10,10 +10,10 @@ Presentation::Presentation() {
|
|||
if(!media.bootable) continue;
|
||||
auto item = new MenuItem{&libraryMenu};
|
||||
item->setText({media.name, " ..."}).onActivate([=] {
|
||||
directory::create({config->library.location, media.name});
|
||||
directory::create({settings["Library/Location"].text(), media.name});
|
||||
auto location = BrowserDialog()
|
||||
.setTitle({"Load ", media.name})
|
||||
.setPath({config->library.location, media.name})
|
||||
.setPath({settings["Library/Location"].text(), media.name})
|
||||
.setFilters(string{media.name, "|*.", media.type})
|
||||
.openFolder();
|
||||
if(directory::exists(location)) {
|
||||
|
@ -31,53 +31,59 @@ Presentation::Presentation() {
|
|||
|
||||
settingsMenu.setText("Settings");
|
||||
videoScaleMenu.setText("Video Scale");
|
||||
if(config->video.scale == "Small") videoScaleSmall.setChecked();
|
||||
if(config->video.scale == "Medium") videoScaleMedium.setChecked();
|
||||
if(config->video.scale == "Large") videoScaleLarge.setChecked();
|
||||
if(settings["Video/Scale"].text() == "Small") videoScaleSmall.setChecked();
|
||||
if(settings["Video/Scale"].text() == "Medium") videoScaleMedium.setChecked();
|
||||
if(settings["Video/Scale"].text() == "Large") videoScaleLarge.setChecked();
|
||||
videoScaleSmall.setText("Small").onActivate([&] {
|
||||
config->video.scale = "Small";
|
||||
settings["Video/Scale"].setValue("Small");
|
||||
resizeViewport();
|
||||
});
|
||||
videoScaleMedium.setText("Medium").onActivate([&] {
|
||||
config->video.scale = "Medium";
|
||||
settings["Video/Scale"].setValue("Medium");
|
||||
resizeViewport();
|
||||
});
|
||||
videoScaleLarge.setText("Large").onActivate([&] {
|
||||
config->video.scale = "Large";
|
||||
settings["Video/Scale"].setValue("Large");
|
||||
resizeViewport();
|
||||
});
|
||||
aspectCorrection.setText("Aspect Correction").setChecked(config->video.aspectCorrection).onToggle([&] {
|
||||
config->video.aspectCorrection = aspectCorrection.checked();
|
||||
aspectCorrection.setText("Aspect Correction").setChecked(settings["Video/AspectCorrection"].boolean()).onToggle([&] {
|
||||
settings["Video/AspectCorrection"].setValue(aspectCorrection.checked());
|
||||
resizeViewport();
|
||||
});
|
||||
videoFilterMenu.setText("Video Filter");
|
||||
if(config->video.filter == "None") videoFilterNone.setChecked();
|
||||
if(config->video.filter == "Blur") videoFilterBlur.setChecked();
|
||||
videoFilterNone.setText("None").onActivate([&] { config->video.filter = "None"; program->updateVideoFilter(); });
|
||||
videoFilterBlur.setText("Blur").onActivate([&] { config->video.filter = "Blur"; program->updateVideoFilter(); });
|
||||
colorEmulation.setText("Color Emulation").setChecked(config->video.colorEmulation).onToggle([&] {
|
||||
config->video.colorEmulation = colorEmulation.checked();
|
||||
if(settings["Video/Filter"].text() == "None") videoFilterNone.setChecked();
|
||||
if(settings["Video/Filter"].text() == "Blur") videoFilterBlur.setChecked();
|
||||
videoFilterNone.setText("None").onActivate([&] {
|
||||
settings["Video/Filter"].setValue("None");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
videoFilterBlur.setText("Blur").onActivate([&] {
|
||||
settings["Video/Filter"].setValue("Blur");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
colorEmulation.setText("Color Emulation").setChecked(settings["Video/ColorEmulation"].boolean()).onToggle([&] {
|
||||
settings["Video/ColorEmulation"].setValue(colorEmulation.checked());
|
||||
program->updateVideoPalette();
|
||||
});
|
||||
maskOverscan.setText("Mask Overscan").setChecked(config->video.overscan.mask).onToggle([&] {
|
||||
config->video.overscan.mask = maskOverscan.checked();
|
||||
maskOverscan.setText("Mask Overscan").setChecked(settings["Video/Overscan/Mask"].boolean()).onToggle([&] {
|
||||
settings["Video/Overscan/Mask"].setValue(maskOverscan.checked());
|
||||
});
|
||||
loadShaders();
|
||||
synchronizeVideo.setText("Synchronize Video").setChecked(config->video.synchronize).onToggle([&] {
|
||||
config->video.synchronize = synchronizeVideo.checked();
|
||||
video->set(Video::Synchronize, config->video.synchronize);
|
||||
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Synchronize"].boolean()).onToggle([&] {
|
||||
settings["Video/Synchronize"].setValue(synchronizeVideo.checked());
|
||||
video->set(Video::Synchronize, synchronizeVideo.checked());
|
||||
});
|
||||
synchronizeAudio.setText("Synchronize Audio").setChecked(config->audio.synchronize).onToggle([&] {
|
||||
config->audio.synchronize = synchronizeAudio.checked();
|
||||
audio->set(Audio::Synchronize, config->audio.synchronize);
|
||||
synchronizeAudio.setText("Synchronize Audio").setChecked(settings["Audio/Synchronize"].boolean()).onToggle([&] {
|
||||
settings["Audio/Synchronize"].setValue(synchronizeAudio.checked());
|
||||
audio->set(Audio::Synchronize, synchronizeVideo.checked());
|
||||
});
|
||||
muteAudio.setText("Mute Audio").setChecked(config->audio.mute).onToggle([&] {
|
||||
config->audio.mute = muteAudio.checked();
|
||||
program->dsp.setVolume(config->audio.mute ? 0.0 : 1.0);
|
||||
muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
|
||||
settings["Audio/Mute"].setValue(muteAudio.checked());
|
||||
program->updateAudioVolume();
|
||||
});
|
||||
showStatusBar.setText("Show Status Bar").setChecked(config->userInterface.showStatusBar).onToggle([&] {
|
||||
config->userInterface.showStatusBar = showStatusBar.checked();
|
||||
statusBar.setVisible(config->userInterface.showStatusBar);
|
||||
showStatusBar.setText("Show Status Bar").setChecked(settings["UserInterface/ShowStatusBar"].boolean()).onToggle([&] {
|
||||
settings["UserInterface/ShowStatusBar"].setValue(showStatusBar.checked());
|
||||
statusBar.setVisible(showStatusBar.checked());
|
||||
if(visible()) resizeViewport();
|
||||
});
|
||||
showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(2); });
|
||||
|
@ -99,7 +105,7 @@ Presentation::Presentation() {
|
|||
stateManager.setText("State Manager").onActivate([&] { toolsManager->show(1); });
|
||||
|
||||
statusBar.setFont(Font().setBold());
|
||||
statusBar.setVisible(config->userInterface.showStatusBar);
|
||||
statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
|
||||
|
||||
onClose([&] { program->quit(); });
|
||||
|
||||
|
@ -127,18 +133,18 @@ auto Presentation::updateEmulator() -> void {
|
|||
for(auto& device : port.device) {
|
||||
MenuRadioItem item{&menu};
|
||||
item.setText(device.name).onActivate([=] {
|
||||
emulatorSettings->set({emulator->information.name, "/", port.name}, device.name);
|
||||
auto path = string{emulator->information.name, "/", port.name}.replace(" ", "");
|
||||
settings[path].setValue(device.name);
|
||||
emulator->connect(port.id, device.id);
|
||||
});
|
||||
devices.append(item);
|
||||
}
|
||||
if(devices.objectCount() > 1) {
|
||||
string device = emulatorSettings->get({emulator->information.name, "/", port.name});
|
||||
auto path = string{emulator->information.name, "/", port.name}.replace(" ", "");
|
||||
auto device = settings(path).text();
|
||||
for(auto object : devices.objects()) {
|
||||
if(auto item = dynamic_cast<mMenuRadioItem*>(object.data())) {
|
||||
if(item->text() == device) {
|
||||
item->setChecked().doActivate();
|
||||
}
|
||||
if(auto item = object.cast<MenuRadioItem>()) {
|
||||
if(item.text() == device) item.setChecked().doActivate();
|
||||
}
|
||||
}
|
||||
menu.setVisible();
|
||||
|
@ -154,17 +160,17 @@ auto Presentation::resizeViewport() -> void {
|
|||
double stretch = emulator ? emulator->information.aspectRatio : 1.0;
|
||||
if(stretch != 1.0) {
|
||||
//aspect correction is always enabled in fullscreen mode
|
||||
if(!fullScreen() && !config->video.aspectCorrection) stretch = 1.0;
|
||||
if(!fullScreen() && !settings["Video/AspectCorrection"].boolean()) stretch = 1.0;
|
||||
}
|
||||
|
||||
signed scale = 2;
|
||||
if(config->video.scale == "Small" ) scale = 2;
|
||||
if(config->video.scale == "Medium") scale = 3;
|
||||
if(config->video.scale == "Large" ) scale = 4;
|
||||
if(settings["Video/Scale"].text() == "Small" ) scale = 2;
|
||||
if(settings["Video/Scale"].text() == "Medium") scale = 3;
|
||||
if(settings["Video/Scale"].text() == "Large" ) scale = 4;
|
||||
|
||||
signed windowWidth = 0, windowHeight = 0;
|
||||
if(!fullScreen()) {
|
||||
windowWidth = 256 * scale * (config->video.aspectCorrection ? 8.0 / 7.0 : 1.0);
|
||||
windowWidth = 256 * scale * (settings["Video/AspectCorrection"].boolean() ? 8.0 / 7.0 : 1.0);
|
||||
windowHeight = 240 * scale;
|
||||
} else {
|
||||
windowWidth = geometry().width();
|
||||
|
@ -193,7 +199,7 @@ auto Presentation::toggleFullScreen() -> void {
|
|||
setFullScreen(false);
|
||||
setResizable(false);
|
||||
menuBar.setVisible(true);
|
||||
statusBar.setVisible(config->userInterface.showStatusBar);
|
||||
statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
|
||||
}
|
||||
|
||||
Application::processEvents();
|
||||
|
@ -215,7 +221,7 @@ auto Presentation::drawSplashScreen() -> void {
|
|||
}
|
||||
|
||||
auto Presentation::loadShaders() -> void {
|
||||
if(config->video.driver != "OpenGL") {
|
||||
if(settings["Video/Driver"].text() != "OpenGL") {
|
||||
videoShaderMenu.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
@ -224,7 +230,7 @@ auto Presentation::loadShaders() -> void {
|
|||
for(auto shader : directory::folders(pathname, "*.shader")) {
|
||||
MenuRadioItem item{&videoShaderMenu};
|
||||
item.setText(string{shader}.rtrim(".shader/", 1L)).onActivate([=] {
|
||||
config->video.shader = {pathname, shader};
|
||||
settings["Video/Shader"].setValue({pathname, shader});
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
videoShaders.append(item);
|
||||
|
@ -232,13 +238,13 @@ auto Presentation::loadShaders() -> void {
|
|||
|
||||
videoShaderMenu.setText("Video Shaders");
|
||||
videoShaderNone.setChecked().setText("None").onActivate([=] {
|
||||
config->video.shader = "None";
|
||||
settings["Video/Shader"].setValue("None");
|
||||
program->updateVideoFilter();
|
||||
});
|
||||
|
||||
for(auto object : videoShaders.objects()) {
|
||||
if(auto radioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
|
||||
if(config->video.shader == string{pathname, radioItem->text(), ".shader/"}) {
|
||||
if(settings["Video/Shader"].text() == string{pathname, radioItem->text(), ".shader/"}) {
|
||||
radioItem->setChecked();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
auto Program::loadRequest(unsigned id, string name, string type, bool required) -> void {
|
||||
string location = BrowserDialog()
|
||||
.setTitle({"Load ", name})
|
||||
.setPath({config->library.location, name})
|
||||
.setPath({settings["Library/Location"].text(), name})
|
||||
.setFilters({string{name, "|*.", type}})
|
||||
.openFolder();
|
||||
if(!directory::exists(location)) return;
|
||||
|
@ -16,25 +16,30 @@ auto Program::loadRequest(unsigned id, string name, string type, bool required)
|
|||
auto Program::loadRequest(unsigned id, string filename, bool required) -> void {
|
||||
string pathname = mediaPaths(emulator->group(id));
|
||||
string location = {pathname, filename};
|
||||
|
||||
if(filename == "manifest.bml" && !pathname.find(".sys/")) {
|
||||
if(!file::exists(location) || settings["Library/IgnoreManifests"].boolean()) {
|
||||
string manifest;
|
||||
if(auto fp = popen(string{"icarus -m \"", pathname, "\""}, "r")) {
|
||||
while(true) {
|
||||
auto byte = fgetc(fp);
|
||||
if(byte == EOF) break;
|
||||
manifest.append((char)byte);
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
if(manifest) {
|
||||
memorystream stream{(const uint8*)manifest.data(), manifest.size()};
|
||||
return emulator->load(id, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(file::exists(location)) {
|
||||
mmapstream stream{location};
|
||||
return emulator->load(id, stream);
|
||||
}
|
||||
if(filename == "manifest.bml") {
|
||||
string manifest;
|
||||
if(auto fp = popen(string{"icarus -m \"", pathname, "\""}, "r")) {
|
||||
while(true) {
|
||||
auto byte = fgetc(fp);
|
||||
if(byte == EOF) break;
|
||||
manifest.append((char)byte);
|
||||
}
|
||||
pclose(fp);
|
||||
}
|
||||
if(manifest) {
|
||||
memorystream stream{manifest.binary(), manifest.size()};
|
||||
return emulator->load(id, stream);
|
||||
}
|
||||
}
|
||||
|
||||
if(required) MessageDialog().setTitle("higan").setText({
|
||||
"Missing required file: ", nall::filename(location), "\n\n",
|
||||
"From location:\n", nall::pathname(location)
|
||||
|
@ -50,25 +55,25 @@ auto Program::saveRequest(unsigned id, string filename) -> void {
|
|||
}
|
||||
|
||||
auto Program::videoColor(unsigned source, uint16 a, uint16 r, uint16 g, uint16 b) -> uint32 {
|
||||
if(config->video.saturation != 100) {
|
||||
if(settings["Video/Saturation"].natural() != 100) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double saturation = config->video.saturation * 0.01;
|
||||
double saturation = settings["Video/Saturation"].natural() * 0.01;
|
||||
double inverse = max(0.0, 1.0 - saturation);
|
||||
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(config->video.gamma != 100) {
|
||||
double exponent = config->video.gamma * 0.01;
|
||||
if(settings["Video/Gamma"].natural() != 100) {
|
||||
double exponent = settings["Video/Gamma"].natural() * 0.01;
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent);
|
||||
g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent);
|
||||
b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent);
|
||||
}
|
||||
|
||||
if(config->video.luminance != 100) {
|
||||
double luminance = config->video.luminance * 0.01;
|
||||
if(settings["Video/Luminance"].natural() != 100) {
|
||||
double luminance = settings["Video/Luminance"].natural() * 0.01;
|
||||
r = r * luminance;
|
||||
g = g * luminance;
|
||||
b = b * luminance;
|
||||
|
@ -96,9 +101,9 @@ auto Program::videoRefresh(const uint32* palette, const uint32* data, unsigned p
|
|||
}
|
||||
}
|
||||
|
||||
if(emulator->information.overscan && config->video.overscan.mask) {
|
||||
unsigned h = config->video.overscan.horizontal;
|
||||
unsigned v = config->video.overscan.vertical;
|
||||
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
||||
auto h = settings["Video/Overscan/Horizontal"].natural();
|
||||
auto v = settings["Video/Overscan/Vertical"].natural();
|
||||
|
||||
if(h) for(auto y : range(height)) {
|
||||
memory::fill(output + y * length, 4 * h);
|
||||
|
|
|
@ -16,7 +16,7 @@ auto Program::loadMedia(string location) -> void {
|
|||
auto Program::loadMedia(Emulator::Interface& emulator_, Emulator::Interface::Media& media, const string& location) -> void {
|
||||
unloadMedia();
|
||||
|
||||
mediaPaths(0) = locate({config->library.location, "System/"}, {media.name, ".sys/"});
|
||||
mediaPaths(0) = locate({settings["Library/Location"].text(), "System/"}, {media.name, ".sys/"});
|
||||
mediaPaths(media.id) = location;
|
||||
folderPaths.append(location);
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ Program::Program(lstring args) {
|
|||
emulators.append(new GameBoyAdvance::Interface);
|
||||
for(auto& emulator : emulators) emulator->bind = this;
|
||||
|
||||
new ConfigurationManager;
|
||||
new EmulatorSettings;
|
||||
new InputManager;
|
||||
new SettingsManager;
|
||||
new CheatDatabase;
|
||||
|
@ -31,18 +29,18 @@ Program::Program(lstring args) {
|
|||
|
||||
presentation->setVisible();
|
||||
|
||||
video = Video::create(config->video.driver);
|
||||
video = Video::create(settings["Video/Driver"].text());
|
||||
video->set(Video::Handle, presentation->viewport.handle());
|
||||
video->set(Video::Synchronize, config->video.synchronize);
|
||||
video->set(Video::Synchronize, settings["Video/Synchronize"].boolean());
|
||||
if(!video->init()) {
|
||||
delete video;
|
||||
video = Video::create("None");
|
||||
}
|
||||
|
||||
audio = Audio::create(config->audio.driver);
|
||||
audio->set(Audio::Device, config->audio.device);
|
||||
audio = Audio::create(settings["Audio/Driver"].text());
|
||||
audio->set(Audio::Device, settings["Audio/Device"].text());
|
||||
audio->set(Audio::Handle, presentation->viewport.handle());
|
||||
audio->set(Audio::Synchronize, config->audio.synchronize);
|
||||
audio->set(Audio::Synchronize, settings["Audio/Synchronize"].boolean());
|
||||
audio->set(Audio::Frequency, 96000u);
|
||||
audio->set(Audio::Latency, 80u);
|
||||
if(!audio->init()) {
|
||||
|
@ -50,7 +48,7 @@ Program::Program(lstring args) {
|
|||
audio = Audio::create("None");
|
||||
}
|
||||
|
||||
input = Input::create(config->input.driver);
|
||||
input = Input::create(settings["Input/Driver"].text());
|
||||
input->set(Input::Handle, presentation->viewport.handle());
|
||||
input->onChange({&InputManager::onChange, inputManager});
|
||||
if(!input->init()) {
|
||||
|
@ -60,7 +58,6 @@ Program::Program(lstring args) {
|
|||
|
||||
dsp.setPrecision(16);
|
||||
dsp.setBalance(0.0);
|
||||
dsp.setVolume(config->audio.mute ? 0.0 : 1.0);
|
||||
dsp.setFrequency(32040);
|
||||
dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dsp.setResamplerFrequency(96000);
|
||||
|
@ -68,6 +65,7 @@ Program::Program(lstring args) {
|
|||
presentation->drawSplashScreen();
|
||||
|
||||
updateVideoFilter();
|
||||
updateAudioVolume();
|
||||
|
||||
args.takeFirst(); //ignore program location in argument parsing
|
||||
for(auto& argument : args) {
|
||||
|
@ -96,8 +94,7 @@ auto Program::main() -> void {
|
|||
|
||||
auto Program::quit() -> void {
|
||||
unloadMedia();
|
||||
config->quit();
|
||||
emulatorSettings->quit();
|
||||
settings.quit();
|
||||
inputManager->quit();
|
||||
delete video;
|
||||
delete audio;
|
||||
|
|
|
@ -35,6 +35,7 @@ struct Program : Emulator::Interface::Bind {
|
|||
auto updateVideoFilter() -> void;
|
||||
auto updateVideoPalette() -> void;
|
||||
auto updateAudio() -> void;
|
||||
auto updateAudioVolume() -> void;
|
||||
auto updateDSP() -> void;
|
||||
|
||||
DSP dsp;
|
||||
|
|
|
@ -36,18 +36,21 @@ auto Program::updateStatusText() -> void {
|
|||
}
|
||||
|
||||
auto Program::updateVideoFilter() -> void {
|
||||
if(config->video.driver == "OpenGL" && config->video.shader != "None" && directory::exists(config->video.shader)) {
|
||||
if(settings["Video/Driver"].text() == "OpenGL"
|
||||
&& settings["Video/Shader"].text() != "None"
|
||||
&& directory::exists(settings["Video/Shader"].text())
|
||||
) {
|
||||
video->set(Video::Filter, Video::FilterNearest);
|
||||
video->set(Video::Shader, (string)config->video.shader);
|
||||
video->set(Video::Shader, settings["Video/Shader"].text());
|
||||
} else {
|
||||
video->set(Video::Filter, config->video.filter == "Blur" ? Video::FilterLinear : Video::FilterNearest);
|
||||
video->set(Video::Filter, settings["Video/Filter"].text() == "Blur" ? Video::FilterLinear : Video::FilterNearest);
|
||||
video->set(Video::Shader, (string)"");
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::updateVideoPalette() -> void {
|
||||
if(!emulator) return;
|
||||
emulator->paletteUpdate(config->video.colorEmulation
|
||||
emulator->paletteUpdate(settings["Video/ColorEmulation"].boolean()
|
||||
? Emulator::Interface::PaletteMode::Emulation
|
||||
: Emulator::Interface::PaletteMode::Standard
|
||||
);
|
||||
|
@ -56,23 +59,25 @@ auto Program::updateVideoPalette() -> void {
|
|||
auto Program::updateAudio() -> void {
|
||||
if(!audio) return;
|
||||
audio->clear();
|
||||
audio->set(Audio::Frequency, config->audio.frequency);
|
||||
audio->set(Audio::Latency, config->audio.latency);
|
||||
if(auto resampler = config->audio.resampler) {
|
||||
if(resampler == "Linear" ) dsp.setResampler(DSP::ResampleEngine::Linear);
|
||||
if(resampler == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite);
|
||||
if(resampler == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||
}
|
||||
dsp.setResamplerFrequency(config->audio.frequency);
|
||||
dsp.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
||||
audio->set(Audio::Frequency, settings["Audio/Frequency"].natural());
|
||||
audio->set(Audio::Latency, settings["Audio/Latency"].natural());
|
||||
if(settings["Audio/Resampler"].text() == "Linear" ) dsp.setResampler(DSP::ResampleEngine::Linear);
|
||||
if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite);
|
||||
if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dsp.setResamplerFrequency(settings["Audio/Frequency"].natural());
|
||||
updateAudioVolume();
|
||||
updateDSP();
|
||||
}
|
||||
|
||||
auto Program::updateAudioVolume() -> void {
|
||||
dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01);
|
||||
}
|
||||
|
||||
auto Program::updateDSP() -> void {
|
||||
if(!emulator) return;
|
||||
if(!config->video.synchronize) return dsp.setFrequency(emulator->audioFrequency());
|
||||
if(!settings["Video/Synchronize"].boolean()) return dsp.setFrequency(emulator->audioFrequency());
|
||||
|
||||
double inputRatio = emulator->audioFrequency() / emulator->videoFrequency();
|
||||
double outputRatio = config->timing.audio / config->timing.video;
|
||||
dsp.setFrequency(inputRatio / outputRatio * config->audio.frequency);
|
||||
double outputRatio = settings["Timing/Audio"].real() / settings["Timing/Video"].real();
|
||||
dsp.setFrequency(inputRatio / outputRatio * settings["Audio/Frequency"].natural());
|
||||
}
|
||||
|
|
|
@ -6,36 +6,41 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
|
||||
driverLabel.setText("Driver Selection").setFont(Font().setBold());
|
||||
videoLabel.setText("Video:");
|
||||
videoDriver.onChange([&] { config->video.driver = videoDriver.selected()->text(); });
|
||||
videoDriver.onChange([&] { settings["Video/Driver"].setValue(videoDriver.selected().text()); });
|
||||
for(auto& driver : Video::availableDrivers()) {
|
||||
ComboButtonItem item;
|
||||
item.setText(driver);
|
||||
videoDriver.append(item);
|
||||
if(config->video.driver == driver) item.setSelected();
|
||||
if(settings["Video/Driver"].text() == driver) item.setSelected();
|
||||
}
|
||||
audioLabel.setText("Audio:");
|
||||
audioDriver.onChange([&] { config->audio.driver = audioDriver.selected()->text(); });
|
||||
audioDriver.onChange([&] { settings["Audio/Driver"].setValue(audioDriver.selected().text()); });
|
||||
for(auto& driver : Audio::availableDrivers()) {
|
||||
ComboButtonItem item;
|
||||
item.setText(driver);
|
||||
audioDriver.append(item);
|
||||
if(config->audio.driver == driver) item.setSelected();
|
||||
if(settings["Audio/Driver"].text() == driver) item.setSelected();
|
||||
}
|
||||
inputLabel.setText("Input:");
|
||||
inputDriver.onChange([&] { config->input.driver = inputDriver.selected()->text(); });
|
||||
inputDriver.onChange([&] { settings["Input/Driver"].setValue(inputDriver.selected().text()); });
|
||||
for(auto& driver : Input::availableDrivers()) {
|
||||
ComboButtonItem item;
|
||||
item.setText(driver);
|
||||
inputDriver.append(item);
|
||||
if(config->input.driver == driver) item.setSelected();
|
||||
if(settings["Input/Driver"].text() == driver) item.setSelected();
|
||||
}
|
||||
|
||||
libraryLabel.setText("Game Library").setFont(Font().setBold());
|
||||
libraryPrefix.setText("Location:");
|
||||
libraryLocation.setEditable(false).setText(config->library.location);
|
||||
libraryLocation.setEditable(false).setText(settings["Library/Location"].text());
|
||||
libraryChange.setText("Change ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setTitle("Select Library Location").selectFolder()) {
|
||||
libraryLocation.setText(config->library.location = location);
|
||||
settings["Library/Location"].setValue(location);
|
||||
libraryLocation.setText(location);
|
||||
}
|
||||
});
|
||||
|
||||
ignoreManifests.setText("Ignore Manifests").setChecked(settings["Library/IgnoreManifests"].boolean()).onToggle([&] {
|
||||
settings["Library/IgnoreManifests"].setValue(ignoreManifests.checked());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
frequencyCombo.append(ComboButtonItem().setText("44100hz"));
|
||||
frequencyCombo.append(ComboButtonItem().setText("48000hz"));
|
||||
frequencyCombo.append(ComboButtonItem().setText("96000hz"));
|
||||
switch(config->audio.frequency) {
|
||||
switch(settings["Audio/Frequency"].natural()) {
|
||||
case 32000: frequencyCombo.item(0)->setSelected(); break;
|
||||
case 44100: frequencyCombo.item(1)->setSelected(); break;
|
||||
case 48000: frequencyCombo.item(2)->setSelected(); break;
|
||||
|
@ -23,7 +23,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
latencyCombo.append(ComboButtonItem().setText("60ms"));
|
||||
latencyCombo.append(ComboButtonItem().setText("80ms"));
|
||||
latencyCombo.append(ComboButtonItem().setText("100ms"));
|
||||
switch(config->audio.latency) {
|
||||
switch(settings["Audio/Latency"].natural()) {
|
||||
case 20: latencyCombo.item(0)->setSelected(); break;
|
||||
case 40: latencyCombo.item(1)->setSelected(); break;
|
||||
case 60: latencyCombo.item(2)->setSelected(); break;
|
||||
|
@ -36,42 +36,48 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
resamplerCombo.append(ComboButtonItem().setText("Linear"));
|
||||
resamplerCombo.append(ComboButtonItem().setText("Hermite"));
|
||||
resamplerCombo.append(ComboButtonItem().setText("Sinc"));
|
||||
if(config->audio.resampler == "Linear" ) resamplerCombo.item(0)->setSelected();
|
||||
if(config->audio.resampler == "Hermite") resamplerCombo.item(1)->setSelected();
|
||||
if(config->audio.resampler == "Sinc" ) resamplerCombo.item(2)->setSelected();
|
||||
if(settings["Audio/Resampler"].text() == "Linear" ) resamplerCombo.item(0)->setSelected();
|
||||
if(settings["Audio/Resampler"].text() == "Hermite") resamplerCombo.item(1)->setSelected();
|
||||
if(settings["Audio/Resampler"].text() == "Sinc" ) resamplerCombo.item(2)->setSelected();
|
||||
resamplerCombo.onChange([&] { update(); });
|
||||
|
||||
volumeLabel.setText("Volume:");
|
||||
volumeSlider.setLength(201).setPosition(config->audio.volume).onChange([&] { updateVolume(); });
|
||||
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); });
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
auto AudioSettings::update() -> void {
|
||||
if(auto item = frequencyCombo.selected()) {
|
||||
if(item->offset() == 0) config->audio.frequency = 32000;
|
||||
if(item->offset() == 1) config->audio.frequency = 44100;
|
||||
if(item->offset() == 2) config->audio.frequency = 48000;
|
||||
if(item->offset() == 3) config->audio.frequency = 96000;
|
||||
uint frequency = 48000;
|
||||
if(item->offset() == 0) frequency = 32000;
|
||||
if(item->offset() == 1) frequency = 44100;
|
||||
if(item->offset() == 2) frequency = 48000;
|
||||
if(item->offset() == 3) frequency = 96000;
|
||||
settings["Audio/Frequency"].setValue(frequency);
|
||||
}
|
||||
if(auto item = latencyCombo.selected()) {
|
||||
if(item->offset() == 0) config->audio.latency = 20;
|
||||
if(item->offset() == 1) config->audio.latency = 40;
|
||||
if(item->offset() == 2) config->audio.latency = 60;
|
||||
if(item->offset() == 3) config->audio.latency = 80;
|
||||
if(item->offset() == 4) config->audio.latency = 100;
|
||||
uint latency = 60;
|
||||
if(item->offset() == 0) latency = 20;
|
||||
if(item->offset() == 1) latency = 40;
|
||||
if(item->offset() == 2) latency = 60;
|
||||
if(item->offset() == 3) latency = 80;
|
||||
if(item->offset() == 4) latency = 100;
|
||||
settings["Audio/Latency"].setValue(latency);
|
||||
}
|
||||
if(auto item = resamplerCombo.selected()) {
|
||||
if(item->offset() == 0) config->audio.resampler = "Linear";
|
||||
if(item->offset() == 1) config->audio.resampler = "Hermite";
|
||||
if(item->offset() == 2) config->audio.resampler = "Sinc";
|
||||
string resampler = "Sinc";
|
||||
if(item->offset() == 0) resampler = "Linear";
|
||||
if(item->offset() == 1) resampler = "Hermite";
|
||||
if(item->offset() == 2) resampler = "Sinc";
|
||||
settings["Audio/Resampler"].setValue(resampler);
|
||||
}
|
||||
updateVolume();
|
||||
program->updateAudio();
|
||||
}
|
||||
|
||||
auto AudioSettings::updateVolume() -> void {
|
||||
config->audio.volume = volumeSlider.position();
|
||||
volumeValue.setText({config->audio.volume, "%"});
|
||||
program->dsp.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
|
||||
settings["Audio/Volume"].setValue(volumeSlider.position());
|
||||
volumeValue.setText({volumeSlider.position(), "%"});
|
||||
program->updateAudioVolume();
|
||||
}
|
||||
|
|
|
@ -129,6 +129,7 @@ struct AdvancedSettings : TabFrameItem {
|
|||
Label libraryPrefix{&libraryLayout, Size{0, 0}};
|
||||
LineEdit libraryLocation{&libraryLayout, Size{~0, 0}};
|
||||
Button libraryChange{&libraryLayout, Size{0, 0}};
|
||||
CheckLabel ignoreManifests{&layout, Size{~0, 0}};
|
||||
};
|
||||
|
||||
struct SettingsManager : Window {
|
||||
|
|
|
@ -4,15 +4,15 @@ TimingSettings::TimingSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
|
||||
layout.setMargin(5);
|
||||
videoLabel.setText("Video:");
|
||||
videoValue.setText(config->timing.video).onActivate([&] { update(); });
|
||||
videoValue.setText(settings["Timing/Video"].real()).onActivate([&] { update(); });
|
||||
videoAssign.setText("Assign").onActivate([&] { update(); });
|
||||
audioLabel.setText("Audio:");
|
||||
audioValue.setText(config->timing.audio).onActivate([&] { update(); });
|
||||
audioValue.setText(settings["Timing/Audio"].real()).onActivate([&] { update(); });
|
||||
audioAssign.setText("Assign").onActivate([&] { update(); });
|
||||
}
|
||||
|
||||
auto TimingSettings::update() -> void {
|
||||
config->timing.video = real(videoValue.text());
|
||||
config->timing.audio = real(audioValue.text());
|
||||
settings["Timing/Video"].setValue(videoValue.text().real());
|
||||
settings["Timing/Audio"].setValue(audioValue.text().real());
|
||||
program->updateDSP();
|
||||
}
|
||||
|
|
|
@ -6,31 +6,31 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
|
||||
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
||||
saturationLabel.setText("Saturation:");
|
||||
saturationSlider.setLength(201).setPosition(config->video.saturation).onChange([&] { update(); });
|
||||
saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] { update(); });
|
||||
gammaLabel.setText("Gamma:");
|
||||
gammaSlider.setLength(101).setPosition(config->video.gamma - 100).onChange([&] { update(); });
|
||||
gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] { update(); });
|
||||
luminanceLabel.setText("Luminance:");
|
||||
luminanceSlider.setLength(101).setPosition(config->video.luminance).onChange([&] { update(); });
|
||||
luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] { update(); });
|
||||
|
||||
overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask");
|
||||
horizontalMaskLabel.setText("Horizontal:");
|
||||
horizontalMaskSlider.setLength(17).setPosition(config->video.overscan.horizontal).onChange([&] { update(); });
|
||||
horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { update(); });
|
||||
verticalMaskLabel.setText("Vertical:");
|
||||
verticalMaskSlider.setLength(17).setPosition(config->video.overscan.vertical).onChange([&] { update(); });
|
||||
verticalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { update(); });
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
auto VideoSettings::update() -> void {
|
||||
config->video.saturation = saturationSlider.position();
|
||||
config->video.gamma = 100 + gammaSlider.position();
|
||||
config->video.luminance = luminanceSlider.position();
|
||||
config->video.overscan.horizontal = horizontalMaskSlider.position();
|
||||
config->video.overscan.vertical = verticalMaskSlider.position();
|
||||
saturationValue.setText({config->video.saturation, "%"});
|
||||
gammaValue.setText({config->video.gamma, "%"});
|
||||
luminanceValue.setText({config->video.luminance, "%"});
|
||||
horizontalMaskValue.setText({config->video.overscan.horizontal, "px"});
|
||||
verticalMaskValue.setText({config->video.overscan.vertical, "px"});
|
||||
settings["Video/Saturation"].setValue(saturationSlider.position());
|
||||
settings["Video/Gamma"].setValue(100 + gammaSlider.position());
|
||||
settings["Video/Luminance"].setValue(luminanceSlider.position());
|
||||
settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position());
|
||||
settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position());
|
||||
saturationValue.setText({saturationSlider.position(), "%"});
|
||||
gammaValue.setText({100 + gammaSlider.position(), "%"});
|
||||
luminanceValue.setText({luminanceSlider.position(), "%"});
|
||||
horizontalMaskValue.setText({horizontalMaskSlider.position(), "px"});
|
||||
verticalMaskValue.setText({verticalMaskSlider.position(), "px"});
|
||||
program->updateVideoPalette();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue