mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-20 20:11:42 +02:00
Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7fa8ad755d | ||
|
ef85f7ccb0 | ||
|
b8d607d16b | ||
|
4c47cc203f | ||
|
4cbaf4e4ec | ||
|
21f9fe4cd5 | ||
|
b629a46779 | ||
|
ba2e6b5789 | ||
|
7115047d85 | ||
|
e8b1af0917 | ||
|
1d4f778176 | ||
|
875ed46d79 | ||
|
046e478d86 | ||
|
82a17ac0f5 | ||
|
979aa640af | ||
|
98ec338285 | ||
|
5b4dcbfdfe | ||
|
101c9507b1 | ||
|
69ed35db99 | ||
|
5c2d16828c | ||
|
382ae1e61e | ||
|
7619805266 | ||
|
e3c7bbfb63 | ||
|
5f099b8ad0 | ||
|
cb3460a673 | ||
|
278cf8462c | ||
|
7f4381b505 | ||
|
c668d10ac7 | ||
|
7fc78dae07 | ||
|
4ca051a22f | ||
|
8618334356 | ||
|
ec7e4087fb | ||
|
496708cffe | ||
|
a86c5ee59d | ||
|
d8f9204e18 | ||
|
e8775319c8 | ||
|
095181af62 | ||
|
b28c54770c | ||
|
71763f2d98 | ||
|
423d9ba00d | ||
|
064ca4c626 | ||
|
10906d8418 | ||
|
e88ab60663 | ||
|
564e38ea9f | ||
|
0c3f0834ab | ||
|
f38af85e0a | ||
|
8276700381 | ||
|
ec69109c0b | ||
|
8ae6444af7 | ||
|
5fc86eae6d | ||
|
927c97eb06 | ||
|
cf09d41669 | ||
|
724747ac9e | ||
|
e1e275eb38 | ||
|
e30fcade43 | ||
|
42dbf73d18 | ||
|
2a90e12999 | ||
|
d129b72ced | ||
|
bc0b86891a | ||
|
52443936e6 | ||
|
6694a1c986 | ||
|
7ffaeb2ac1 | ||
|
67e6a6e742 | ||
|
9a3650c6ab | ||
|
0a3d6e4c53 | ||
|
378b78dad7 | ||
|
721e0b1762 | ||
|
2bf3dbf375 | ||
|
396003e7f6 | ||
|
a92a554d7b |
@@ -1,11 +1,13 @@
|
||||
include nall/Makefile
|
||||
|
||||
nes := nes
|
||||
snes := snes
|
||||
gameboy := gameboy
|
||||
profile := accuracy
|
||||
ui := ui
|
||||
|
||||
# debugger
|
||||
options :=
|
||||
# options += console
|
||||
# options += debugger
|
||||
|
||||
# compiler
|
||||
c := $(compiler) -std=gnu99
|
||||
@@ -14,28 +16,33 @@ flags := -O3 -fomit-frame-pointer -I.
|
||||
link :=
|
||||
objects := libco
|
||||
|
||||
# profile-guided instrumentation
|
||||
# flags += -fprofile-generate
|
||||
# link += -lgcov
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
# pgo := optimize
|
||||
|
||||
# profile-guided optimization
|
||||
# flags += -fprofile-use
|
||||
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
ifeq ($(pgo),instrument)
|
||||
flags += -fprofile-generate
|
||||
link += -lgcov
|
||||
else ifeq ($(pgo),optimize)
|
||||
flags += -fprofile-use
|
||||
endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
# tree vectorization causes code generation errors with Linux/GCC 4.6.1
|
||||
flags += -fno-tree-vectorize
|
||||
link += -s -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
else ifeq ($(platform),win)
|
||||
link += -mwindows
|
||||
# link += -mconsole
|
||||
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
||||
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
else
|
||||
unknown_platform: help;
|
||||
endif
|
||||
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
@@ -70,6 +77,6 @@ clean:
|
||||
-@$(call delete,*.manifest)
|
||||
|
||||
archive-all:
|
||||
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
|
||||
tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes Makefile cc.bat clean.bat sync.sh
|
||||
|
||||
help:;
|
||||
|
79635
bsnes/data/cheats.bml
Executable file
79635
bsnes/data/cheats.bml
Executable file
File diff suppressed because it is too large
Load Diff
57650
bsnes/data/cheats.xml
57650
bsnes/data/cheats.xml
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
||||
gameboy_objects := gameboy-system gameboy-scheduler
|
||||
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
|
||||
gameboy_objects += gameboy-memory gameboy-cartridge
|
||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
||||
gameboy_objects += gameboy-cheat
|
||||
objects += $(gameboy_objects)
|
||||
|
||||
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
|
||||
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
||||
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
||||
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||
@@ -10,3 +12,4 @@ obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/m
|
||||
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
||||
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
|
||||
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
|
||||
obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/)
|
||||
|
@@ -46,7 +46,7 @@ void APU::main() {
|
||||
noise.run();
|
||||
master.run();
|
||||
|
||||
system.interface->audio_sample(master.center, master.left, master.right);
|
||||
interface->audioSample(master.center, master.left, master.right);
|
||||
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ void APU::power() {
|
||||
create(Main, 4194304);
|
||||
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
||||
foreach(n, mmio_data) n = 0x00;
|
||||
for(auto &n : mmio_data) n = 0x00;
|
||||
sequencer_base = 0;
|
||||
sequencer_step = 0;
|
||||
|
||||
@@ -80,10 +80,10 @@ uint8 APU::mmio_read(uint16 addr) {
|
||||
|
||||
if(addr == 0xff26) {
|
||||
uint8 data = master.enable << 7;
|
||||
if(square1.counter && square1.length) data |= 0x01;
|
||||
if(square2.counter && square2.length) data |= 0x02;
|
||||
if( wave.counter && wave.length) data |= 0x04;
|
||||
if( noise.counter && noise.length) data |= 0x08;
|
||||
if(square1.enable) data |= 0x01;
|
||||
if(square2.enable) data |= 0x02;
|
||||
if( wave.enable) data |= 0x04;
|
||||
if( noise.enable) data |= 0x08;
|
||||
return data | table[addr - 0xff10];
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
void APU::Master::run() {
|
||||
static int16_t volume[] = {
|
||||
-16384, -14336, -12288, -10240, -8192, -6144, -4096, -2048,
|
||||
+2048, +4096, +6144, +8192, +10240, +12288, +14336, +16384,
|
||||
};
|
||||
|
||||
if(enable == false) {
|
||||
center = 0;
|
||||
left = 0;
|
||||
@@ -14,13 +19,7 @@ void APU::Master::run() {
|
||||
sample += apu.wave.output;
|
||||
sample += apu.noise.output;
|
||||
sample >>= 2;
|
||||
center = sclamp<16>(sample);
|
||||
|
||||
if(left_enable == false && right_enable == false) {
|
||||
left = center;
|
||||
right = center;
|
||||
return;
|
||||
}
|
||||
center = volume[sample];
|
||||
|
||||
sample = 0;
|
||||
channels = 0;
|
||||
@@ -29,7 +28,7 @@ void APU::Master::run() {
|
||||
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
|
||||
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
|
||||
if(channels) sample /= channels;
|
||||
left = sclamp<16>(sample);
|
||||
left = volume[sample];
|
||||
|
||||
switch(left_volume) {
|
||||
case 0: left >>= 3; break; // 12.5%
|
||||
@@ -41,7 +40,6 @@ void APU::Master::run() {
|
||||
case 6: left -= (left >> 3); break; // 87.5%
|
||||
//case 7: break; //100.0%
|
||||
}
|
||||
if(left_enable == false) left = 0;
|
||||
|
||||
sample = 0;
|
||||
channels = 0;
|
||||
@@ -50,7 +48,7 @@ void APU::Master::run() {
|
||||
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
|
||||
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
|
||||
if(channels) sample /= channels;
|
||||
right = sclamp<16>(sample);
|
||||
right = volume[sample];
|
||||
|
||||
switch(right_volume) {
|
||||
case 0: right >>= 3; break; // 12.5%
|
||||
@@ -62,18 +60,17 @@ void APU::Master::run() {
|
||||
case 6: right -= (right >> 3); break; // 87.5%
|
||||
//case 7: break; //100.0%
|
||||
}
|
||||
if(right_enable == false) right = 0;
|
||||
}
|
||||
|
||||
void APU::Master::write(unsigned r, uint8 data) {
|
||||
if(r == 0) {
|
||||
left_enable = data & 0x80;
|
||||
left_volume = (data >> 4) & 7;
|
||||
right_enable = data & 0x08;
|
||||
right_volume = (data >> 0) & 7;
|
||||
if(r == 0) { //$ff24 NR50
|
||||
left_in_enable = data & 0x80;
|
||||
left_volume = (data >> 4) & 7;
|
||||
right_in_enable = data & 0x08;
|
||||
right_volume = (data >> 0) & 7;
|
||||
}
|
||||
|
||||
if(r == 1) {
|
||||
if(r == 1) { //$ff25 NR51
|
||||
channel4_left_enable = data & 0x80;
|
||||
channel3_left_enable = data & 0x40;
|
||||
channel2_left_enable = data & 0x20;
|
||||
@@ -84,15 +81,15 @@ void APU::Master::write(unsigned r, uint8 data) {
|
||||
channel1_right_enable = data & 0x01;
|
||||
}
|
||||
|
||||
if(r == 2) {
|
||||
if(r == 2) { //$ff26 NR52
|
||||
enable = data & 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Master::power() {
|
||||
left_enable = 0;
|
||||
left_in_enable = 0;
|
||||
left_volume = 0;
|
||||
right_enable = 0;
|
||||
right_in_enable = 0;
|
||||
right_volume = 0;
|
||||
channel4_left_enable = 0;
|
||||
channel3_left_enable = 0;
|
||||
@@ -110,9 +107,9 @@ void APU::Master::power() {
|
||||
}
|
||||
|
||||
void APU::Master::serialize(serializer &s) {
|
||||
s.integer(left_enable);
|
||||
s.integer(left_in_enable);
|
||||
s.integer(left_volume);
|
||||
s.integer(right_enable);
|
||||
s.integer(right_in_enable);
|
||||
s.integer(right_volume);
|
||||
s.integer(channel4_left_enable);
|
||||
s.integer(channel3_left_enable);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
struct Master {
|
||||
bool left_enable;
|
||||
unsigned left_volume;
|
||||
bool right_enable;
|
||||
unsigned right_volume;
|
||||
bool left_in_enable;
|
||||
uint3 left_volume;
|
||||
bool right_in_enable;
|
||||
uint3 right_volume;
|
||||
bool channel4_left_enable;
|
||||
bool channel3_left_enable;
|
||||
bool channel2_left_enable;
|
||||
|
@@ -1,27 +1,32 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Noise::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Noise::run() {
|
||||
if(period && --period == 0) {
|
||||
period = divisor << frequency;
|
||||
if(frequency < 14) {
|
||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||
lfsr = (lfsr >> 1) ^ (bit << 14);
|
||||
if(narrow_lfsr) lfsr |= (bit << 6);
|
||||
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (lfsr & 1) ? 0 : volume;
|
||||
if(counter && length == 0) sample = 0;
|
||||
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = (sample * 4369) - 32768;
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Noise::clock_length() {
|
||||
if(counter && length) length--;
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::clock_envelope() {
|
||||
if(envelope_period && --envelope_period == 0) {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
@@ -29,42 +34,42 @@ void APU::Noise::clock_envelope() {
|
||||
}
|
||||
|
||||
void APU::Noise::write(unsigned r, uint8 data) {
|
||||
if(r == 1) {
|
||||
initial_length = 64 - (data & 0x3f);
|
||||
|
||||
length = initial_length;
|
||||
if(r == 1) { //$ff20 NR41
|
||||
length = 64 - (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) {
|
||||
if(r == 2) { //$ff21 NR42
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) {
|
||||
if(r == 3) { //$ff22 NR43
|
||||
frequency = data >> 4;
|
||||
narrow_lfsr = data & 0x08;
|
||||
divisor = (data & 0x07) << 4;
|
||||
if(divisor == 0) divisor = 8;
|
||||
|
||||
period = divisor << frequency;
|
||||
}
|
||||
|
||||
if(r == 4) {
|
||||
if(r == 4) { //$ff34 NR44
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
lfsr = ~0U;
|
||||
length = initial_length;
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::power() {
|
||||
initial_length = 0;
|
||||
enable = 0;
|
||||
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
@@ -82,7 +87,8 @@ void APU::Noise::power() {
|
||||
}
|
||||
|
||||
void APU::Noise::serialize(serializer &s) {
|
||||
s.integer(initial_length);
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
|
@@ -1,20 +1,23 @@
|
||||
struct Noise {
|
||||
unsigned initial_length;
|
||||
unsigned envelope_volume;
|
||||
bool enable;
|
||||
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
unsigned envelope_frequency;
|
||||
unsigned frequency;
|
||||
uint3 envelope_frequency;
|
||||
uint4 frequency;
|
||||
bool narrow_lfsr;
|
||||
unsigned divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
unsigned length;
|
||||
unsigned envelope_period;
|
||||
unsigned volume;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
unsigned period;
|
||||
uint15 lfsr;
|
||||
|
||||
bool dac_enable();
|
||||
|
||||
void run();
|
||||
void clock_length();
|
||||
void clock_envelope();
|
||||
|
@@ -1,9 +1,13 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Square1::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Square1::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 4 * (2048 - frequency);
|
||||
phase = (phase + 1) & 7;
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
@@ -12,45 +16,44 @@ void APU::Square1::run() {
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : 0);
|
||||
if(counter && length == 0) sample = 0;
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = (sample * 4369) - 32768;
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square1::sweep() {
|
||||
if(enable == false) return;
|
||||
void APU::Square1::sweep(bool update) {
|
||||
if(sweep_enable == false) return;
|
||||
|
||||
signed offset = frequency_shadow >> sweep_shift;
|
||||
if(sweep_direction) offset = -offset;
|
||||
frequency_shadow += offset;
|
||||
sweep_negate = sweep_direction;
|
||||
unsigned delta = frequency_shadow >> sweep_shift;
|
||||
signed freq = frequency_shadow + (sweep_negate ? -delta : delta);
|
||||
|
||||
if(frequency_shadow < 0) {
|
||||
frequency_shadow = 0;
|
||||
} else if(frequency_shadow > 2047) {
|
||||
frequency_shadow = 2048;
|
||||
if(freq > 2047) {
|
||||
enable = false;
|
||||
}
|
||||
|
||||
if(frequency_shadow <= 2047 && sweep_shift) {
|
||||
frequency = frequency_shadow;
|
||||
} else if(sweep_shift && update) {
|
||||
frequency_shadow = freq;
|
||||
frequency = freq & 2047;
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_length() {
|
||||
if(counter && length) length--;
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_sweep() {
|
||||
if(sweep_frequency && sweep_period && --sweep_period == 0) {
|
||||
if(enable && sweep_frequency && --sweep_period == 0) {
|
||||
sweep_period = sweep_frequency;
|
||||
sweep();
|
||||
sweep(1);
|
||||
sweep(0);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_envelope() {
|
||||
if(envelope_period && --envelope_period == 0) {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
@@ -58,39 +61,44 @@ void APU::Square1::clock_envelope() {
|
||||
}
|
||||
|
||||
void APU::Square1::write(unsigned r, uint8 data) {
|
||||
if(r == 0) {
|
||||
if(r == 0) { //$ff10 NR10
|
||||
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
|
||||
sweep_frequency = (data >> 4) & 7;
|
||||
sweep_direction = data & 0x08;
|
||||
sweep_shift = data & 0x07;
|
||||
}
|
||||
|
||||
if(r == 1) {
|
||||
if(r == 1) { //$ff11 NR11
|
||||
duty = data >> 6;
|
||||
length = data & 0x3f;
|
||||
length = 64 - (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) {
|
||||
if(r == 2) { //$ff12 NR12
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) {
|
||||
if(r == 3) { //$ff13 NR13
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) {
|
||||
if(r == 4) { //$ff14 NR14
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
frequency_shadow = frequency;
|
||||
sweep_period = sweep_frequency;
|
||||
enable = sweep_period || sweep_shift;
|
||||
if(sweep_shift) sweep();
|
||||
sweep_enable = sweep_period || sweep_shift;
|
||||
sweep_negate = false;
|
||||
if(sweep_shift) sweep(0);
|
||||
if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +106,12 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
enable = 0;
|
||||
|
||||
sweep_frequency = 0;
|
||||
sweep_direction = 0;
|
||||
sweep_shift = 0;
|
||||
sweep_negate = 0;
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
@@ -116,14 +127,17 @@ void APU::Square1::power() {
|
||||
envelope_period = 0;
|
||||
sweep_period = 0;
|
||||
frequency_shadow = 0;
|
||||
enable = 0;
|
||||
sweep_enable = 0;
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
void APU::Square1::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(sweep_frequency);
|
||||
s.integer(sweep_direction);
|
||||
s.integer(sweep_shift);
|
||||
s.integer(sweep_negate);
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
@@ -139,7 +153,7 @@ void APU::Square1::serialize(serializer &s) {
|
||||
s.integer(envelope_period);
|
||||
s.integer(sweep_period);
|
||||
s.integer(frequency_shadow);
|
||||
s.integer(enable);
|
||||
s.integer(sweep_enable);
|
||||
s.integer(volume);
|
||||
}
|
||||
|
||||
|
@@ -1,27 +1,32 @@
|
||||
struct Square1 {
|
||||
unsigned sweep_frequency;
|
||||
unsigned sweep_direction;
|
||||
unsigned sweep_shift;
|
||||
unsigned duty;
|
||||
bool enable;
|
||||
|
||||
uint3 sweep_frequency;
|
||||
bool sweep_direction;
|
||||
uint3 sweep_shift;
|
||||
bool sweep_negate;
|
||||
uint2 duty;
|
||||
unsigned length;
|
||||
unsigned envelope_volume;
|
||||
unsigned envelope_direction;
|
||||
unsigned envelope_frequency;
|
||||
unsigned frequency;
|
||||
unsigned counter;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool duty_output;
|
||||
unsigned phase;
|
||||
uint3 phase;
|
||||
unsigned period;
|
||||
unsigned envelope_period;
|
||||
unsigned sweep_period;
|
||||
uint3 envelope_period;
|
||||
uint3 sweep_period;
|
||||
signed frequency_shadow;
|
||||
bool enable;
|
||||
unsigned volume;
|
||||
bool sweep_enable;
|
||||
uint4 volume;
|
||||
|
||||
bool dac_enable();
|
||||
|
||||
void run();
|
||||
void sweep();
|
||||
void sweep(bool update);
|
||||
void clock_length();
|
||||
void clock_sweep();
|
||||
void clock_envelope();
|
||||
|
@@ -1,9 +1,13 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Square2::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Square2::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 4 * (2048 - frequency);
|
||||
phase = (phase + 1) & 7;
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
@@ -12,18 +16,20 @@ void APU::Square2::run() {
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : 0);
|
||||
if(counter && length == 0) sample = 0;
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = (sample * 4369) - 32768;
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square2::clock_length() {
|
||||
if(counter && length) length--;
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::clock_envelope() {
|
||||
if(envelope_period && --envelope_period == 0) {
|
||||
if(enable && envelope_frequency && --envelope_period == 0) {
|
||||
envelope_period = envelope_frequency;
|
||||
if(envelope_direction == 0 && volume > 0) volume--;
|
||||
if(envelope_direction == 1 && volume < 15) volume++;
|
||||
@@ -31,29 +37,32 @@ void APU::Square2::clock_envelope() {
|
||||
}
|
||||
|
||||
void APU::Square2::write(unsigned r, uint8 data) {
|
||||
if(r == 1) {
|
||||
if(r == 1) { //$ff16 NR21
|
||||
duty = data >> 6;
|
||||
length = data & 0x3f;
|
||||
length = 64 - (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) {
|
||||
if(r == 2) { //$ff17 NR22
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) {
|
||||
if(r == 3) { //$ff18 NR23
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) {
|
||||
if(r == 4) { //$ff19 NR24
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +70,8 @@ void APU::Square2::write(unsigned r, uint8 data) {
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
enable = 0;
|
||||
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
@@ -78,6 +89,8 @@ void APU::Square2::power() {
|
||||
}
|
||||
|
||||
void APU::Square2::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
|
@@ -1,18 +1,22 @@
|
||||
struct Square2 {
|
||||
unsigned duty;
|
||||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
unsigned length;
|
||||
unsigned envelope_volume;
|
||||
unsigned envelope_direction;
|
||||
unsigned envelope_frequency;
|
||||
unsigned frequency;
|
||||
unsigned counter;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool duty_output;
|
||||
unsigned phase;
|
||||
uint3 phase;
|
||||
unsigned period;
|
||||
unsigned envelope_period;
|
||||
unsigned volume;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
|
||||
bool dac_enable();
|
||||
|
||||
void run();
|
||||
void clock_length();
|
||||
|
@@ -3,57 +3,53 @@
|
||||
void APU::Wave::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
pattern_offset = (pattern_offset + 1) & 31;
|
||||
pattern_sample = pattern[pattern_offset];
|
||||
pattern_sample = pattern[++pattern_offset];
|
||||
}
|
||||
|
||||
uint4 sample = pattern_sample;
|
||||
if(counter && length == 0) sample = 0;
|
||||
uint4 sample = pattern_sample >> volume_shift;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = (sample * 4369) - 32768;
|
||||
output >>= volume;
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Wave::clock_length() {
|
||||
if(counter && length) length--;
|
||||
if(counter && length) {
|
||||
if(--length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Wave::write(unsigned r, uint8 data) {
|
||||
if(r == 0) {
|
||||
if(r == 0) { //$ff1a NR30
|
||||
dac_enable = data & 0x80;
|
||||
|
||||
if(dac_enable == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 1) {
|
||||
initial_length = 256 - data;
|
||||
|
||||
length = initial_length;
|
||||
if(r == 1) { //$ff1b NR31
|
||||
length = 256 - data;
|
||||
}
|
||||
|
||||
if(r == 2) {
|
||||
if(r == 2) { //$ff1c NR32
|
||||
switch((data >> 5) & 3) {
|
||||
case 0: volume = 16; break; // 0%
|
||||
case 1: volume = 0; break; //100%
|
||||
case 2: volume = 1; break; // 50%
|
||||
case 3: volume = 2; break; // 25%
|
||||
case 0: volume_shift = 4; break; // 0%
|
||||
case 1: volume_shift = 0; break; //100%
|
||||
case 2: volume_shift = 1; break; // 50%
|
||||
case 3: volume_shift = 2; break; // 25%
|
||||
}
|
||||
}
|
||||
|
||||
if(r == 3) {
|
||||
if(r == 3) { //$ff1d NR33
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) {
|
||||
if(r == 4) { //$ff1e NR34
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize && dac_enable) {
|
||||
enable = true;
|
||||
if(initialize) {
|
||||
enable = dac_enable;
|
||||
pattern_offset = 0;
|
||||
length = initial_length;
|
||||
if(length == 0) length = 256;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,17 +63,17 @@ void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
||||
}
|
||||
|
||||
void APU::Wave::power() {
|
||||
enable = 0;
|
||||
|
||||
dac_enable = 0;
|
||||
initial_length = 0;
|
||||
volume = 0;
|
||||
volume_shift = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
random_cyclic r;
|
||||
foreach(n, pattern) n = r() & 15;
|
||||
random_lfsr r;
|
||||
for(auto &n : pattern) n = r() & 15;
|
||||
|
||||
output = 0;
|
||||
enable = 0;
|
||||
length = 0;
|
||||
period = 0;
|
||||
pattern_offset = 0;
|
||||
@@ -85,15 +81,15 @@ void APU::Wave::power() {
|
||||
}
|
||||
|
||||
void APU::Wave::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(dac_enable);
|
||||
s.integer(initial_length);
|
||||
s.integer(volume);
|
||||
s.integer(volume_shift);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
s.array(pattern);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(enable);
|
||||
s.integer(length);
|
||||
s.integer(period);
|
||||
s.integer(pattern_offset);
|
||||
|
@@ -1,17 +1,17 @@
|
||||
struct Wave {
|
||||
bool enable;
|
||||
|
||||
bool dac_enable;
|
||||
unsigned initial_length;
|
||||
unsigned volume;
|
||||
unsigned frequency;
|
||||
unsigned volume_shift;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[32];
|
||||
|
||||
int16 output;
|
||||
bool enable;
|
||||
unsigned length;
|
||||
unsigned period;
|
||||
unsigned pattern_offset;
|
||||
unsigned pattern_sample;
|
||||
uint5 pattern_offset;
|
||||
uint4 pattern_sample;
|
||||
|
||||
void run();
|
||||
void clock_length();
|
||||
|
@@ -16,7 +16,7 @@ namespace GameBoy {
|
||||
#include "serialization.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
||||
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
||||
if(size == 0) size = 32768;
|
||||
romdata = allocate<uint8>(romsize = size, 0xff);
|
||||
if(data) memcpy(romdata, data, size);
|
||||
@@ -33,42 +33,24 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
xml_element document = xml_parse(xml);
|
||||
foreach(head, document.element) {
|
||||
if(head.name == "cartridge") {
|
||||
foreach(attr, head.attribute) {
|
||||
if(attr.name == "mapper") {
|
||||
if(attr.content == "none") info.mapper = Mapper::MBC0;
|
||||
if(attr.content == "MBC1") info.mapper = Mapper::MBC1;
|
||||
if(attr.content == "MBC2") info.mapper = Mapper::MBC2;
|
||||
if(attr.content == "MBC3") info.mapper = Mapper::MBC3;
|
||||
if(attr.content == "MBC5") info.mapper = Mapper::MBC5;
|
||||
if(attr.content == "MMM01") info.mapper = Mapper::MMM01;
|
||||
if(attr.content == "HuC1") info.mapper = Mapper::HuC1;
|
||||
if(attr.content == "HuC3") info.mapper = Mapper::HuC3;
|
||||
}
|
||||
BML::Document document(markup);
|
||||
|
||||
if(attr.name == "rtc") info.rtc = (attr.content == "true" ? true : false);
|
||||
if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false);
|
||||
}
|
||||
auto &mapperid = document["cartridge"]["mapper"].value;
|
||||
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
|
||||
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
|
||||
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
|
||||
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
|
||||
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
|
||||
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
|
||||
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
|
||||
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
|
||||
|
||||
foreach(elem, head.element) {
|
||||
if(elem.name == "rom") {
|
||||
foreach(attr, elem.attribute) {
|
||||
if(attr.name == "size") info.romsize = hex(attr.content);
|
||||
}
|
||||
}
|
||||
info.rtc = document["cartridge"]["rtc"].exists();
|
||||
info.rumble = document["cartridge"]["rumble"].exists();
|
||||
|
||||
if(elem.name == "ram") {
|
||||
info.ram = true;
|
||||
foreach(attr, elem.attribute) {
|
||||
if(attr.name == "size") info.ramsize = hex(attr.content);
|
||||
if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
info.romsize = hex(document["cartridge"]["rom"]["size"].value);
|
||||
info.ramsize = hex(document["cartridge"]["ram"]["size"].value);
|
||||
info.battery = document["cartridge"]["ram"]["non-volatile"].exists();
|
||||
|
||||
switch(info.mapper) { default:
|
||||
case Mapper::MBC0: mapper = &mbc0; break;
|
||||
@@ -83,7 +65,9 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
||||
|
||||
ramdata = new uint8_t[ramsize = info.ramsize]();
|
||||
system.load();
|
||||
|
||||
loaded = true;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
|
@@ -34,6 +34,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
||||
} info;
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
uint8_t *romdata;
|
||||
unsigned romsize;
|
||||
@@ -44,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
||||
MMIO *mapper;
|
||||
bool bootrom_enable;
|
||||
|
||||
void load(const string &xml, const uint8_t *data, unsigned size);
|
||||
void load(const string &markup, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
|
||||
uint8 rom_read(unsigned addr);
|
||||
|
@@ -55,7 +55,7 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
|
||||
|
||||
void Cartridge::MMM01::power() {
|
||||
rom_mode = 0;
|
||||
rom_base = 0x00;
|
||||
rom_base = 0;
|
||||
|
||||
ram_enable = false;
|
||||
rom_select = 0x01;
|
||||
|
91
bsnes/gameboy/cheat/cheat.cpp
Executable file
91
bsnes/gameboy/cheat/cheat.cpp
Executable file
@@ -0,0 +1,91 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) {
|
||||
static bool initialize = false;
|
||||
static uint8 mapProActionReplay[256], mapGameGenie[256];
|
||||
|
||||
if(initialize == false) {
|
||||
initialize = true;
|
||||
|
||||
for(auto &n : mapProActionReplay) n = ~0;
|
||||
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
|
||||
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
|
||||
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
|
||||
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
|
||||
|
||||
for(auto &n : mapGameGenie) n = ~0;
|
||||
mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3;
|
||||
mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7;
|
||||
mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11;
|
||||
mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15;
|
||||
}
|
||||
|
||||
string code = code_;
|
||||
code.upper();
|
||||
unsigned length = code.length(), bits = 0;
|
||||
|
||||
if(code.wildcard("????:??")) {
|
||||
code = { substr(code, 0, 4), substr(code, 5, 2) };
|
||||
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||
bits = hex(code);
|
||||
addr = (bits >> 8) & 0xffff;
|
||||
data = (bits >> 0) & 0xff;
|
||||
comp = ~0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(code.wildcard("????:??:??")) {
|
||||
code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) };
|
||||
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||
bits = hex(code);
|
||||
addr = (bits >> 16) & 0xffff;
|
||||
data = (bits >> 8) & 0xff;
|
||||
comp = (bits >> 0) & 0xff;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(code.wildcard("???" "-" "???")) {
|
||||
code = { substr(code, 0, 3), substr(code, 4, 3) };
|
||||
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
|
||||
|
||||
addr = (bits >> 0) & 0xffff;
|
||||
data = (bits >> 16) & 0xff;
|
||||
comp = ~0;
|
||||
|
||||
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(code.wildcard("???" "-" "???" "-" "???")) {
|
||||
code = { substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) };
|
||||
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
|
||||
|
||||
addr = (bits >> 8) & 0xffff;
|
||||
data = (bits >> 24) & 0xff;
|
||||
comp = (bits >> 0) & 0xff;
|
||||
|
||||
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
|
||||
comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cheat::synchronize() {
|
||||
for(auto &n : override) n = false;
|
||||
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
override[operator[](n).addr] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
bsnes/gameboy/cheat/cheat.hpp
Executable file
14
bsnes/gameboy/cheat/cheat.hpp
Executable file
@@ -0,0 +1,14 @@
|
||||
struct CheatCode {
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
unsigned comp;
|
||||
};
|
||||
|
||||
struct Cheat : public linear_vector<CheatCode> {
|
||||
static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp);
|
||||
|
||||
void synchronize();
|
||||
bool override[65536];
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
@@ -580,7 +580,8 @@ void CPU::op_di() {
|
||||
}
|
||||
|
||||
void CPU::op_ei() {
|
||||
status.ime = 1;
|
||||
status.ei = true;
|
||||
//status.ime = 1;
|
||||
}
|
||||
|
||||
//jump commands
|
||||
|
@@ -114,6 +114,7 @@ void CPU::power() {
|
||||
status.clock = 0;
|
||||
status.halt = false;
|
||||
status.stop = false;
|
||||
status.ei = false;
|
||||
status.ime = 0;
|
||||
|
||||
status.p15 = 0;
|
||||
|
@@ -17,6 +17,7 @@ struct CPU : Processor, MMIO {
|
||||
unsigned clock;
|
||||
bool halt;
|
||||
bool stop;
|
||||
bool ei;
|
||||
bool ime;
|
||||
|
||||
//$ff00 JOYP
|
||||
|
@@ -3,15 +3,15 @@
|
||||
void CPU::mmio_joyp_poll() {
|
||||
unsigned button = 0, dpad = 0;
|
||||
|
||||
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
|
||||
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
|
||||
button |= system.interface->input_poll((unsigned)Input::B) << 1;
|
||||
button |= system.interface->input_poll((unsigned)Input::A) << 0;
|
||||
button |= interface->inputPoll((unsigned)Input::Start) << 3;
|
||||
button |= interface->inputPoll((unsigned)Input::Select) << 2;
|
||||
button |= interface->inputPoll((unsigned)Input::B) << 1;
|
||||
button |= interface->inputPoll((unsigned)Input::A) << 0;
|
||||
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
|
||||
|
||||
status.joyp = 0x0f;
|
||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||
@@ -84,7 +84,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr == 0xff00) { //JOYP
|
||||
status.p15 = data & 0x20;
|
||||
status.p14 = data & 0x10;
|
||||
system.interface->joyp_write(status.p15, status.p14);
|
||||
interface->joypWrite(status.p15, status.p14);
|
||||
mmio_joyp_poll();
|
||||
return;
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ void CPU::serialize(serializer &s) {
|
||||
s.integer(status.clock);
|
||||
s.integer(status.halt);
|
||||
s.integer(status.stop);
|
||||
s.integer(status.ei);
|
||||
s.integer(status.ime);
|
||||
|
||||
s.integer(status.p15);
|
||||
|
@@ -1,18 +1,28 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
void CPU::op_io() {
|
||||
cycle_edge();
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
uint8 CPU::op_read(uint16 addr) {
|
||||
cycle_edge();
|
||||
uint8 r = bus.read(addr);
|
||||
add_clocks(4);
|
||||
return r;
|
||||
}
|
||||
|
||||
void CPU::op_write(uint16 addr, uint8 data) {
|
||||
cycle_edge();
|
||||
bus.write(addr, data);
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
void CPU::cycle_edge() {
|
||||
if(status.ei) {
|
||||
status.ei = false;
|
||||
status.ime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -9,3 +9,4 @@ void timer_4096hz();
|
||||
void op_io();
|
||||
uint8 op_read(uint16 addr);
|
||||
void op_write(uint16 addr, uint8 data);
|
||||
void cycle_edge();
|
||||
|
@@ -1,18 +1,22 @@
|
||||
//bgameboy
|
||||
//author: byuu
|
||||
//project started: 2010-12-27
|
||||
#ifndef GAMEBOY_HPP
|
||||
#define GAMEBOY_HPP
|
||||
|
||||
namespace GameBoy {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgameboy";
|
||||
static const char Version[] = "000.19";
|
||||
static unsigned SerializerVersion = 1;
|
||||
static const unsigned SerializerVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bgameboy - Game Boy emulator
|
||||
author: byuu
|
||||
license: GPLv3
|
||||
project started: 2010-12-27
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/property.hpp>
|
||||
#include <nall/random.hpp>
|
||||
@@ -83,7 +87,7 @@ namespace GameBoy {
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
inline Processor() : thread(0) {}
|
||||
inline Processor() : thread(nullptr) {}
|
||||
};
|
||||
|
||||
#include <gameboy/memory/memory.hpp>
|
||||
@@ -93,4 +97,7 @@ namespace GameBoy {
|
||||
#include <gameboy/cpu/cpu.hpp>
|
||||
#include <gameboy/apu/apu.hpp>
|
||||
#include <gameboy/lcd/lcd.hpp>
|
||||
#include <gameboy/cheat/cheat.hpp>
|
||||
};
|
||||
|
||||
#endif
|
||||
|
89
bsnes/gameboy/interface/interface.cpp
Executable file
89
bsnes/gameboy/interface/interface.cpp
Executable file
@@ -0,0 +1,89 @@
|
||||
#include <gameboy/gameboy.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Interface *interface = 0;
|
||||
|
||||
void Interface::lcdScanline() {
|
||||
}
|
||||
|
||||
void Interface::joypWrite(bool p15, bool p14) {
|
||||
}
|
||||
|
||||
void Interface::videoRefresh(const uint8_t *data) {
|
||||
}
|
||||
|
||||
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||
}
|
||||
|
||||
bool Interface::inputPoll(unsigned id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Interface::initialize(Interface *derived_interface) {
|
||||
interface = derived_interface;
|
||||
system.init();
|
||||
}
|
||||
|
||||
bool Interface::cartridgeLoaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
|
||||
cartridge.load(markup, data, size);
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::unloadCartridge() {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
unsigned Interface::memorySize(Memory memory) {
|
||||
if(memory == Memory::RAM) return cartridge.ramsize;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint8_t* Interface::memoryData(Memory memory) {
|
||||
if(memory == Memory::RAM) return cartridge.ramdata;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void Interface::power() {
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
do {
|
||||
system.run();
|
||||
} while(scheduler.exit_reason() != Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
serializer Interface::serialize() {
|
||||
system.runtosave();
|
||||
return system.serialize();
|
||||
}
|
||||
|
||||
bool Interface::unserialize(serializer &s) {
|
||||
return system.unserialize(s);
|
||||
}
|
||||
|
||||
void Interface::setCheats(const lstring &list) {
|
||||
cheat.reset();
|
||||
for(auto &code : list) {
|
||||
lstring codelist;
|
||||
codelist.split("+", code);
|
||||
for(auto &part : codelist) {
|
||||
unsigned addr, data, comp;
|
||||
if(Cheat::decode(part, addr, data, comp)) {
|
||||
cheat.append({ addr, data, comp });
|
||||
}
|
||||
}
|
||||
}
|
||||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::message(const string &text) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
||||
}
|
@@ -1,11 +1,34 @@
|
||||
class Interface {
|
||||
public:
|
||||
virtual void joyp_write(bool p15, bool p14) {}
|
||||
virtual void lcdScanline();
|
||||
virtual void joypWrite(bool p15, bool p14);
|
||||
|
||||
virtual void video_refresh(const uint8_t *data) {}
|
||||
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
|
||||
virtual void input_poll() {}
|
||||
virtual bool input_poll(unsigned id) {}
|
||||
virtual void videoRefresh(const uint8_t *data);
|
||||
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||
virtual bool inputPoll(unsigned id);
|
||||
|
||||
virtual void message(const string &text) { print(text, "\n"); }
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
|
||||
virtual void unloadCartridge();
|
||||
|
||||
enum class Memory : unsigned {
|
||||
RAM,
|
||||
};
|
||||
|
||||
virtual unsigned memorySize(Memory);
|
||||
virtual uint8_t* memoryData(Memory);
|
||||
|
||||
virtual void power();
|
||||
virtual void run();
|
||||
|
||||
virtual serializer serialize();
|
||||
virtual bool unserialize(serializer&);
|
||||
|
||||
virtual void setCheats(const lstring &list = lstring{});
|
||||
|
||||
virtual void message(const string &text);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
@@ -7,6 +7,8 @@ namespace GameBoy {
|
||||
#include "serialization.cpp"
|
||||
LCD lcd;
|
||||
|
||||
static unsigned linectr;
|
||||
|
||||
void LCD::Main() {
|
||||
lcd.main();
|
||||
}
|
||||
@@ -18,21 +20,20 @@ void LCD::main() {
|
||||
}
|
||||
|
||||
add_clocks(4);
|
||||
status.lx += 4;
|
||||
if(status.lx >= 456) scanline();
|
||||
|
||||
if(status.lx == 0) {
|
||||
if(status.display_enable && status.lx == 0) {
|
||||
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
|
||||
if(status.lx == 252) {
|
||||
if(status.display_enable && status.lx == 252) {
|
||||
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LCD::add_clocks(unsigned clocks) {
|
||||
status.lx += clocks;
|
||||
if(status.lx >= 456) scanline();
|
||||
|
||||
clock += clocks;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
co_switch(scheduler.active_thread = cpu.thread);
|
||||
@@ -43,29 +44,32 @@ void LCD::scanline() {
|
||||
status.lx -= 456;
|
||||
if(++status.ly == 154) frame();
|
||||
|
||||
if(status.interrupt_lyc == true) {
|
||||
if(status.display_enable && status.interrupt_lyc == true) {
|
||||
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
|
||||
if(status.ly < 144) render();
|
||||
|
||||
if(status.ly == 144) {
|
||||
if(status.display_enable && status.ly == 144) {
|
||||
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
||||
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
}
|
||||
|
||||
void LCD::frame() {
|
||||
system.interface->video_refresh(screen);
|
||||
system.interface->input_poll();
|
||||
interface->videoRefresh(screen);
|
||||
cpu.mmio_joyp_poll();
|
||||
|
||||
status.ly = 0;
|
||||
status.wyc = 0;
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
void LCD::render() {
|
||||
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
|
||||
for(unsigned n = 0; n < 160; n++) {
|
||||
line[n] = 0x00;
|
||||
origin[n] = Origin::None;
|
||||
}
|
||||
|
||||
if(status.display_enable == true) {
|
||||
if(status.bg_enable == true) render_bg();
|
||||
@@ -74,7 +78,8 @@ void LCD::render() {
|
||||
}
|
||||
|
||||
uint8_t *output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
|
||||
@@ -97,7 +102,9 @@ void LCD::render_bg() {
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
line[ox] = status.bgp[palette];
|
||||
origin[ox] = Origin::BG;
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
@@ -107,15 +114,19 @@ void LCD::render_bg() {
|
||||
}
|
||||
|
||||
void LCD::render_window() {
|
||||
if(status.ly - status.wy >= 144U) return;
|
||||
unsigned iy = status.ly - status.wy;
|
||||
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned iy = status.wyc++;
|
||||
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
|
||||
if(ox - (status.wx - 7) < 160U) {
|
||||
line[ox] = status.bgp[palette];
|
||||
origin[ox] = Origin::Window;
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
@@ -125,6 +136,8 @@ void LCD::render_window() {
|
||||
}
|
||||
|
||||
void LCD::render_obj() {
|
||||
enum : unsigned { Priority = 0x80, YFlip = 0x40, XFlip = 0x20, Palette = 0x10 };
|
||||
|
||||
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
|
||||
|
||||
unsigned sprite[10], sprites = 0;
|
||||
@@ -164,26 +177,29 @@ void LCD::render_obj() {
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= obj_size) continue;
|
||||
if(attribute & 0x40) sy ^= (obj_size - 1);
|
||||
if(attribute & YFlip) sy ^= (obj_size - 1);
|
||||
|
||||
unsigned tdaddr = (tile << 4) + (sy << 1);
|
||||
uint8 d0 = vram[tdaddr + 0];
|
||||
uint8 d1 = vram[tdaddr + 1];
|
||||
unsigned xflip = attribute & 0x20 ? 7 : 0;
|
||||
unsigned xflip = attribute & XFlip ? 7 : 0;
|
||||
|
||||
for(unsigned tx = 0; tx < 8; tx++) {
|
||||
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
|
||||
| ((d1 & (0x80 >> tx)) ? 2 : 0);
|
||||
if(palette == 0) continue;
|
||||
|
||||
palette = status.obp[(bool)(attribute & 0x10)][palette];
|
||||
palette = status.obp[(bool)(attribute & Palette)][palette];
|
||||
unsigned ox = sx + (tx ^ xflip);
|
||||
|
||||
if(ox <= 159) {
|
||||
if(attribute & 0x80) {
|
||||
if(line[ox] > 0) continue;
|
||||
if(attribute & Priority) {
|
||||
if(origin[ox] == Origin::BG || origin[ox] == Origin::Window) {
|
||||
if(line[ox] > 0) continue;
|
||||
}
|
||||
}
|
||||
line[ox] = palette;
|
||||
origin[ox] = Origin::OBJ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +218,7 @@ void LCD::power() {
|
||||
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
|
||||
|
||||
status.lx = 0;
|
||||
status.wyc = 0;
|
||||
|
||||
status.display_enable = 0;
|
||||
status.window_tilemap_select = 0;
|
||||
|
@@ -3,6 +3,7 @@ struct LCD : Processor, MMIO {
|
||||
|
||||
struct Status {
|
||||
unsigned lx;
|
||||
unsigned wyc;
|
||||
|
||||
//$ff40 LCDC
|
||||
bool display_enable;
|
||||
@@ -51,6 +52,9 @@ struct LCD : Processor, MMIO {
|
||||
uint8 oam[160];
|
||||
uint8 line[160];
|
||||
|
||||
struct Origin { enum : unsigned { None, BG, Window, OBJ }; };
|
||||
uint8 origin[160];
|
||||
|
||||
static void Main();
|
||||
void main();
|
||||
void add_clocks(unsigned clocks);
|
||||
|
@@ -17,10 +17,10 @@ uint8 LCD::mmio_read(uint16 addr) {
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
unsigned mode;
|
||||
if(status.ly >= 144) mode = 1; //Vblank
|
||||
else if(status.lx < 80) mode = 2; //OAM
|
||||
if(status.ly >= 144) mode = 1; //Vblank
|
||||
else if(status.lx < 80) mode = 2; //OAM
|
||||
else if(status.lx < 252) mode = 3; //LCD
|
||||
else mode = 0; //Hblank
|
||||
else mode = 0; //Hblank
|
||||
|
||||
return (status.interrupt_lyc << 6)
|
||||
| (status.interrupt_oam << 5)
|
||||
@@ -83,6 +83,10 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
|
||||
|
||||
if(addr == 0xff40) { //LCDC
|
||||
if(status.display_enable == false && (data & 0x80)) {
|
||||
status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone
|
||||
}
|
||||
|
||||
status.display_enable = data & 0x80;
|
||||
status.window_tilemap_select = data & 0x40;
|
||||
status.window_display_enable = data & 0x20;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
void LCD::serialize(serializer &s) {
|
||||
s.integer(status.lx);
|
||||
s.integer(status.wyc);
|
||||
|
||||
s.integer(status.display_enable);
|
||||
s.integer(status.window_tilemap_select);
|
||||
@@ -33,6 +34,7 @@ void LCD::serialize(serializer &s) {
|
||||
s.array(vram);
|
||||
s.array(oam);
|
||||
s.array(line);
|
||||
s.array(origin);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -42,7 +42,20 @@ Memory::~Memory() {
|
||||
//
|
||||
|
||||
uint8 Bus::read(uint16 addr) {
|
||||
return mmio[addr]->mmio_read(addr);
|
||||
uint8 data = mmio[addr]->mmio_read(addr);
|
||||
|
||||
if(cheat.override[addr]) {
|
||||
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||
if(cheat[n].addr == addr) {
|
||||
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||
data = cheat[n].data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Bus::write(uint16 addr, uint8 data) {
|
||||
|
@@ -33,8 +33,8 @@ void System::runthreadtosave() {
|
||||
}
|
||||
}
|
||||
|
||||
void System::init(Interface *interface_) {
|
||||
interface = interface_;
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
}
|
||||
|
||||
void System::load() {
|
||||
|
@@ -14,11 +14,10 @@ struct System {
|
||||
void runtosave();
|
||||
void runthreadtosave();
|
||||
|
||||
void init(Interface*);
|
||||
void init();
|
||||
void load();
|
||||
void power();
|
||||
|
||||
Interface *interface;
|
||||
unsigned clocks_executed;
|
||||
|
||||
//serialization.cpp
|
||||
|
@@ -32,9 +32,9 @@ ifeq ($(compiler),)
|
||||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.5
|
||||
compiler := gcc-mp-4.6
|
||||
else
|
||||
compiler := gcc-4.5
|
||||
compiler := gcc-4.6
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@@ -2,13 +2,12 @@
|
||||
#define NALL_ARRAY_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
@@ -26,7 +25,7 @@ namespace nall {
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = 0;
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
@@ -54,15 +53,12 @@ namespace nall {
|
||||
operator[](buffersize) = data;
|
||||
}
|
||||
|
||||
template<typename U> void insert(unsigned index, const U list) {
|
||||
unsigned listsize = container_size(list);
|
||||
resize(buffersize + listsize);
|
||||
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
|
||||
foreach(item, list) pool[index++] = item;
|
||||
void append(const T data[], unsigned length) {
|
||||
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
|
||||
}
|
||||
|
||||
void insert(unsigned index, const T item) {
|
||||
insert(index, array<T>{ item });
|
||||
void remove() {
|
||||
if(size > 0) resize(size - 1); //remove last element only
|
||||
}
|
||||
|
||||
void remove(unsigned index, unsigned count = 1) {
|
||||
@@ -82,10 +78,10 @@ namespace nall {
|
||||
memset(pool, 0, buffersize * sizeof(T));
|
||||
}
|
||||
|
||||
array() : pool(0), poolsize(0), buffersize(0) {
|
||||
array() : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||
}
|
||||
|
||||
@@ -103,7 +99,7 @@ namespace nall {
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(const array &source) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
@@ -113,12 +109,12 @@ namespace nall {
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
buffersize = source.buffersize;
|
||||
source.pool = 0;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
@@ -133,9 +129,13 @@ namespace nall {
|
||||
if(index >= buffersize) throw "array[] out of bounds";
|
||||
return pool[index];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct has_size<array<T>> { enum { value = true }; };
|
||||
//iteration
|
||||
T* begin() { return &pool[0]; }
|
||||
T* end() { return &pool[buffersize]; }
|
||||
const T* begin() const { return &pool[0]; }
|
||||
const T* end() const { return &pool[buffersize]; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
88
bsnes/nall/atoi.hpp
Executable file
88
bsnes/nall/atoi.hpp
Executable file
@@ -0,0 +1,88 @@
|
||||
#ifndef NALL_ATOI_HPP
|
||||
#define NALL_ATOI_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//note: this header is intended to form the base for user-defined literals;
|
||||
//once they are supported by GCC. eg:
|
||||
//unsigned operator "" b(const char *s) { return binary(s); }
|
||||
//-> signed data = 1001b;
|
||||
//(0b1001 is nicer, but is not part of the C++ standard)
|
||||
|
||||
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
|
||||
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
|
||||
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
constexpr inline uintmax_t binary(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) :
|
||||
binary_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal(const char *s) {
|
||||
return (
|
||||
octal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline intmax_t integer(const char *s) {
|
||||
return (
|
||||
*s == '+' ? +decimal_(s + 1) :
|
||||
*s == '-' ? -decimal_(s + 1) :
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal(const char *s) {
|
||||
return (
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '$' ? hex_(s + 1) :
|
||||
hex_(s)
|
||||
);
|
||||
}
|
||||
|
||||
inline double fp(const char *s) {
|
||||
return atof(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -5,8 +5,7 @@
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class base64 {
|
||||
public:
|
||||
struct base64 {
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new char[inlength * 8 / 6 + 6]();
|
||||
|
||||
@@ -72,6 +71,7 @@ namespace nall {
|
||||
|
||||
private:
|
||||
static char enc(uint8_t n) {
|
||||
//base64 for URL encodings
|
||||
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
return lookup_table[n & 63];
|
||||
}
|
||||
|
@@ -2,39 +2,39 @@
|
||||
#define NALL_BIT_HPP
|
||||
|
||||
namespace nall {
|
||||
template<int bits> inline unsigned uclamp(const unsigned x) {
|
||||
template<int bits> constexpr inline unsigned uclamp(const unsigned x) {
|
||||
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||
}
|
||||
|
||||
template<int bits> inline unsigned uclip(const unsigned x) {
|
||||
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
|
||||
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return (x & m);
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclamp(const signed x) {
|
||||
template<int bits> constexpr inline signed sclamp(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclip(const signed x) {
|
||||
template<int bits> constexpr inline signed sclip(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
|
||||
return ((x & m) ^ b) - b;
|
||||
}
|
||||
|
||||
namespace bit {
|
||||
//lowest(0b1110) == 0b0010
|
||||
template<typename T> inline T lowest(const T x) {
|
||||
template<typename T> constexpr inline T lowest(const T x) {
|
||||
return x & -x;
|
||||
}
|
||||
|
||||
//clear_lowest(0b1110) == 0b1100
|
||||
template<typename T> inline T clear_lowest(const T x) {
|
||||
template<typename T> constexpr inline T clear_lowest(const T x) {
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
//set_lowest(0b0101) == 0b0111
|
||||
template<typename T> inline T set_lowest(const T x) {
|
||||
template<typename T> constexpr inline T set_lowest(const T x) {
|
||||
return x | (x + 1);
|
||||
}
|
||||
|
||||
|
101
bsnes/nall/bmp.hpp
Executable file
101
bsnes/nall/bmp.hpp
Executable file
@@ -0,0 +1,101 @@
|
||||
#ifndef NALL_BMP_HPP
|
||||
#define NALL_BMP_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
|
||||
//BMP reader / writer
|
||||
//author: byuu
|
||||
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bmp {
|
||||
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
|
||||
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
|
||||
};
|
||||
|
||||
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::read) == false) return false;
|
||||
if(fp.size() < 0x36) return false;
|
||||
|
||||
if(fp.readm(2) != 0x424d) return false;
|
||||
fp.seek(0x000a);
|
||||
unsigned offset = fp.readl(4);
|
||||
unsigned dibsize = fp.readl(4);
|
||||
if(dibsize != 40) return false;
|
||||
signed headerWidth = fp.readl(4);
|
||||
if(headerWidth < 0) return false;
|
||||
signed headerHeight = fp.readl(4);
|
||||
fp.readl(2);
|
||||
unsigned bitsPerPixel = fp.readl(2);
|
||||
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
|
||||
unsigned compression = fp.readl(4);
|
||||
if(compression != 0) return false;
|
||||
fp.seek(offset);
|
||||
|
||||
bool noFlip = headerHeight < 0;
|
||||
width = headerWidth, height = abs(headerHeight);
|
||||
data = new uint32_t[width * height];
|
||||
|
||||
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||
unsigned alignedWidth = width * bytesPerPixel;
|
||||
unsigned paddingLength = 0;
|
||||
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
|
||||
for(unsigned x = 0; x < width; x++, p++) {
|
||||
*p = fp.readl(bytesPerPixel);
|
||||
if(bytesPerPixel == 3) *p |= 255 << 24;
|
||||
}
|
||||
if(paddingLength) fp.readl(paddingLength);
|
||||
}
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
unsigned bitsPerPixel = alpha ? 32 : 24;
|
||||
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||
unsigned alignedWidth = width * bytesPerPixel;
|
||||
unsigned paddingLength = 0;
|
||||
unsigned imageSize = alignedWidth * height;
|
||||
unsigned fileSize = 0x36 + imageSize;
|
||||
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||
|
||||
fp.writem(0x424d, 2); //signature
|
||||
fp.writel(fileSize, 4); //file size
|
||||
fp.writel(0, 2); //reserved
|
||||
fp.writel(0, 2); //reserved
|
||||
fp.writel(0x36, 4); //offset
|
||||
|
||||
fp.writel(40, 4); //DIB size
|
||||
fp.writel(width, 4); //width
|
||||
fp.writel(-height, 4); //height
|
||||
fp.writel(1, 2); //color planes
|
||||
fp.writel(bitsPerPixel, 2); //bits per pixel
|
||||
fp.writel(0, 4); //compression method (BI_RGB)
|
||||
fp.writel(imageSize, 4); //image data size
|
||||
fp.writel(3780, 4); //horizontal resolution
|
||||
fp.writel(3780, 4); //vertical resolution
|
||||
fp.writel(0, 4); //palette size
|
||||
fp.writel(0, 4); //important color count
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
|
||||
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
|
||||
if(paddingLength) fp.writel(0, paddingLength);
|
||||
}
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
214
bsnes/nall/bps/delta.hpp
Executable file
214
bsnes/nall/bps/delta.hpp
Executable file
@@ -0,0 +1,214 @@
|
||||
#ifndef NALL_BPS_DELTA_HPP
|
||||
#define NALL_BPS_DELTA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsdelta {
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(const uint8_t *data, unsigned size);
|
||||
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
inline bool create(const string &filename, const string &metadata = "");
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
enum : unsigned { Granularity = 1 };
|
||||
|
||||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
};
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
const uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
};
|
||||
|
||||
void bpsdelta::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpsdelta::target(const uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpsdelta::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsdelta::target(const string &filename) {
|
||||
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsdelta::create(const string &filename, const string &metadata) {
|
||||
file modifyFile;
|
||||
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
|
||||
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
modifyFile.write(data);
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
write('B');
|
||||
write('P');
|
||||
write('S');
|
||||
write('1');
|
||||
|
||||
encode(sourceSize);
|
||||
encode(targetSize);
|
||||
|
||||
unsigned markupSize = metadata.length();
|
||||
encode(markupSize);
|
||||
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||
|
||||
Node *sourceTree[65536], *targetTree[65536];
|
||||
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
|
||||
|
||||
//source tree creation
|
||||
for(unsigned offset = 0; offset < sourceSize; offset++) {
|
||||
uint16_t symbol = sourceData[offset + 0];
|
||||
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
|
||||
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
|
||||
Node *node = new Node;
|
||||
node->offset = offset;
|
||||
node->next = sourceTree[symbol];
|
||||
sourceTree[symbol] = node;
|
||||
}
|
||||
|
||||
unsigned targetReadLength = 0;
|
||||
|
||||
auto targetReadFlush = [&]() {
|
||||
if(targetReadLength) {
|
||||
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||
unsigned offset = outputOffset - targetReadLength;
|
||||
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||
}
|
||||
};
|
||||
|
||||
while(outputOffset < targetSize) {
|
||||
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
|
||||
|
||||
uint16_t symbol = targetData[outputOffset + 0];
|
||||
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
|
||||
|
||||
{ //source read
|
||||
unsigned length = 0, offset = outputOffset;
|
||||
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
|
||||
length++;
|
||||
offset++;
|
||||
}
|
||||
if(length > maxLength) maxLength = length, mode = SourceRead;
|
||||
}
|
||||
|
||||
{ //source copy
|
||||
Node *node = sourceTree[symbol];
|
||||
while(node) {
|
||||
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
|
||||
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
{ //target copy
|
||||
Node *node = targetTree[symbol];
|
||||
while(node) {
|
||||
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
|
||||
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
//target tree append
|
||||
node = new Node;
|
||||
node->offset = outputOffset;
|
||||
node->next = targetTree[symbol];
|
||||
targetTree[symbol] = node;
|
||||
}
|
||||
|
||||
{ //target read
|
||||
if(maxLength < 4) {
|
||||
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
|
||||
mode = TargetRead;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode != TargetRead) targetReadFlush();
|
||||
|
||||
switch(mode) {
|
||||
case SourceRead:
|
||||
encode(SourceRead | ((maxLength - 1) << 2));
|
||||
break;
|
||||
case TargetRead:
|
||||
//delay write to group sequential TargetRead commands into one
|
||||
targetReadLength += maxLength;
|
||||
break;
|
||||
case SourceCopy:
|
||||
case TargetCopy:
|
||||
encode(mode | ((maxLength - 1) << 2));
|
||||
signed relativeOffset;
|
||||
if(mode == SourceCopy) {
|
||||
relativeOffset = maxOffset - sourceRelativeOffset;
|
||||
sourceRelativeOffset = maxOffset + maxLength;
|
||||
} else {
|
||||
relativeOffset = maxOffset - targetRelativeOffset;
|
||||
targetRelativeOffset = maxOffset + maxLength;
|
||||
}
|
||||
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
|
||||
break;
|
||||
}
|
||||
|
||||
outputOffset += maxLength;
|
||||
}
|
||||
|
||||
targetReadFlush();
|
||||
|
||||
sourceChecksum = ~sourceChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||
uint32_t outputChecksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
modifyFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
152
bsnes/nall/bps/linear.hpp
Executable file
152
bsnes/nall/bps/linear.hpp
Executable file
@@ -0,0 +1,152 @@
|
||||
#ifndef NALL_BPS_LINEAR_HPP
|
||||
#define NALL_BPS_LINEAR_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpslinear {
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(const uint8_t *data, unsigned size);
|
||||
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
inline bool create(const string &filename, const string &metadata = "");
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
enum : unsigned { Granularity = 1 };
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
const uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
};
|
||||
|
||||
void bpslinear::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpslinear::target(const uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpslinear::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpslinear::target(const string &filename) {
|
||||
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpslinear::create(const string &filename, const string &metadata) {
|
||||
file modifyFile;
|
||||
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
uint32_t modifyChecksum = ~0;
|
||||
unsigned targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
modifyFile.write(data);
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned targetReadLength = 0;
|
||||
|
||||
auto targetReadFlush = [&]() {
|
||||
if(targetReadLength) {
|
||||
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||
unsigned offset = outputOffset - targetReadLength;
|
||||
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||
}
|
||||
};
|
||||
|
||||
write('B');
|
||||
write('P');
|
||||
write('S');
|
||||
write('1');
|
||||
|
||||
encode(sourceSize);
|
||||
encode(targetSize);
|
||||
|
||||
unsigned markupSize = metadata.length();
|
||||
encode(markupSize);
|
||||
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||
|
||||
while(outputOffset < targetSize) {
|
||||
unsigned sourceLength = 0;
|
||||
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
|
||||
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
|
||||
sourceLength++;
|
||||
}
|
||||
|
||||
unsigned rleLength = 0;
|
||||
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
|
||||
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
|
||||
rleLength++;
|
||||
}
|
||||
|
||||
if(rleLength >= 4) {
|
||||
//write byte to repeat
|
||||
targetReadLength++;
|
||||
outputOffset++;
|
||||
targetReadFlush();
|
||||
|
||||
//copy starting from repetition byte
|
||||
encode(TargetCopy | ((rleLength - 1) << 2));
|
||||
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
|
||||
encode(relativeOffset << 1);
|
||||
outputOffset += rleLength;
|
||||
targetRelativeOffset = outputOffset - 1;
|
||||
} else if(sourceLength >= 4) {
|
||||
targetReadFlush();
|
||||
encode(SourceRead | ((sourceLength - 1) << 2));
|
||||
outputOffset += sourceLength;
|
||||
} else {
|
||||
targetReadLength += Granularity;
|
||||
outputOffset += Granularity;
|
||||
}
|
||||
}
|
||||
|
||||
targetReadFlush();
|
||||
|
||||
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||
uint32_t outputChecksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
modifyFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
121
bsnes/nall/bps/metadata.hpp
Executable file
121
bsnes/nall/bps/metadata.hpp
Executable file
@@ -0,0 +1,121 @@
|
||||
#ifndef NALL_BPS_METADATA_HPP
|
||||
#define NALL_BPS_METADATA_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpsmetadata {
|
||||
inline bool load(const string &filename);
|
||||
inline bool save(const string &filename, const string &metadata);
|
||||
inline string metadata() const;
|
||||
|
||||
protected:
|
||||
file sourceFile;
|
||||
string metadataString;
|
||||
};
|
||||
|
||||
bool bpsmetadata::load(const string &filename) {
|
||||
if(sourceFile.open(filename, file::mode::read) == false) return false;
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
return sourceFile.read();
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = read();
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
if(read() != 'B') return false;
|
||||
if(read() != 'P') return false;
|
||||
if(read() != 'S') return false;
|
||||
if(read() != '1') return false;
|
||||
decode();
|
||||
decode();
|
||||
unsigned metadataSize = decode();
|
||||
char data[metadataSize + 1];
|
||||
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
|
||||
data[metadataSize] = 0;
|
||||
metadataString = (const char*)data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpsmetadata::save(const string &filename, const string &metadata) {
|
||||
file targetFile;
|
||||
if(targetFile.open(filename, file::mode::write) == false) return false;
|
||||
if(sourceFile.open() == false) return false;
|
||||
sourceFile.seek(0);
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
return sourceFile.read();
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = read();
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
uint32_t checksum = ~0;
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
targetFile.write(data);
|
||||
checksum = crc32_adjust(checksum, data);
|
||||
};
|
||||
|
||||
auto encode = [&](uint64_t data) {
|
||||
while(true) {
|
||||
uint64_t x = data & 0x7f;
|
||||
data >>= 7;
|
||||
if(data == 0) {
|
||||
write(0x80 | x);
|
||||
break;
|
||||
}
|
||||
write(x);
|
||||
data--;
|
||||
}
|
||||
};
|
||||
|
||||
for(unsigned n = 0; n < 4; n++) write(read());
|
||||
encode(decode());
|
||||
encode(decode());
|
||||
unsigned sourceLength = decode();
|
||||
unsigned targetLength = metadata.length();
|
||||
encode(targetLength);
|
||||
sourceFile.seek(sourceLength, file::index::relative);
|
||||
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
|
||||
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
|
||||
for(unsigned n = 0; n < length; n++) write(read());
|
||||
uint32_t outputChecksum = ~checksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||
|
||||
targetFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
string bpsmetadata::metadata() const {
|
||||
return metadataString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
219
bsnes/nall/bps/patch.hpp
Executable file
219
bsnes/nall/bps/patch.hpp
Executable file
@@ -0,0 +1,219 @@
|
||||
#ifndef NALL_BPS_PATCH_HPP
|
||||
#define NALL_BPS_PATCH_HPP
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct bpspatch {
|
||||
inline bool modify(const uint8_t *data, unsigned size);
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void target(uint8_t *data, unsigned size);
|
||||
|
||||
inline bool modify(const string &filename);
|
||||
inline bool source(const string &filename);
|
||||
inline bool target(const string &filename);
|
||||
|
||||
inline string metadata() const;
|
||||
inline unsigned size() const;
|
||||
|
||||
enum result : unsigned {
|
||||
unknown,
|
||||
success,
|
||||
patch_too_small,
|
||||
patch_invalid_header,
|
||||
source_too_small,
|
||||
target_too_small,
|
||||
source_checksum_invalid,
|
||||
target_checksum_invalid,
|
||||
patch_checksum_invalid,
|
||||
};
|
||||
|
||||
inline result apply();
|
||||
|
||||
protected:
|
||||
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||
|
||||
filemap modifyFile;
|
||||
const uint8_t *modifyData;
|
||||
unsigned modifySize;
|
||||
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
filemap targetFile;
|
||||
uint8_t *targetData;
|
||||
unsigned targetSize;
|
||||
|
||||
unsigned modifySourceSize;
|
||||
unsigned modifyTargetSize;
|
||||
unsigned modifyMarkupSize;
|
||||
string metadataString;
|
||||
};
|
||||
|
||||
bool bpspatch::modify(const uint8_t *data, unsigned size) {
|
||||
if(size < 19) return false;
|
||||
modifyData = data;
|
||||
modifySize = size;
|
||||
|
||||
unsigned offset = 4;
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = modifyData[offset++];
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
modifySourceSize = decode();
|
||||
modifyTargetSize = decode();
|
||||
modifyMarkupSize = decode();
|
||||
|
||||
char buffer[modifyMarkupSize + 1];
|
||||
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
|
||||
buffer[modifyMarkupSize] = 0;
|
||||
metadataString = (const char*)buffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bpspatch::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
void bpspatch::target(uint8_t *data, unsigned size) {
|
||||
targetData = data;
|
||||
targetSize = size;
|
||||
}
|
||||
|
||||
bool bpspatch::modify(const string &filename) {
|
||||
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
|
||||
return modify(modifyFile.data(), modifyFile.size());
|
||||
}
|
||||
|
||||
bool bpspatch::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
source(sourceFile.data(), sourceFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpspatch::target(const string &filename) {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
fp.truncate(modifyTargetSize);
|
||||
fp.close();
|
||||
|
||||
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
|
||||
target(targetFile.data(), targetFile.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
string bpspatch::metadata() const {
|
||||
return metadataString;
|
||||
}
|
||||
|
||||
unsigned bpspatch::size() const {
|
||||
return modifyTargetSize;
|
||||
}
|
||||
|
||||
bpspatch::result bpspatch::apply() {
|
||||
if(modifySize < 19) return result::patch_too_small;
|
||||
|
||||
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
|
||||
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||
|
||||
auto read = [&]() -> uint8_t {
|
||||
uint8_t data = modifyData[modifyOffset++];
|
||||
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||
return data;
|
||||
};
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t data = 0, shift = 1;
|
||||
while(true) {
|
||||
uint8_t x = read();
|
||||
data += (x & 0x7f) * shift;
|
||||
if(x & 0x80) break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
auto write = [&](uint8_t data) {
|
||||
targetData[outputOffset++] = data;
|
||||
targetChecksum = crc32_adjust(targetChecksum, data);
|
||||
};
|
||||
|
||||
if(read() != 'B') return result::patch_invalid_header;
|
||||
if(read() != 'P') return result::patch_invalid_header;
|
||||
if(read() != 'S') return result::patch_invalid_header;
|
||||
if(read() != '1') return result::patch_invalid_header;
|
||||
|
||||
modifySourceSize = decode();
|
||||
modifyTargetSize = decode();
|
||||
modifyMarkupSize = decode();
|
||||
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
|
||||
|
||||
if(modifySourceSize > sourceSize) return result::source_too_small;
|
||||
if(modifyTargetSize > targetSize) return result::target_too_small;
|
||||
|
||||
while(modifyOffset < modifySize - 12) {
|
||||
unsigned length = decode();
|
||||
unsigned mode = length & 3;
|
||||
length = (length >> 2) + 1;
|
||||
|
||||
switch(mode) {
|
||||
case SourceRead:
|
||||
while(length--) write(sourceData[outputOffset]);
|
||||
break;
|
||||
case TargetRead:
|
||||
while(length--) write(read());
|
||||
break;
|
||||
case SourceCopy:
|
||||
case TargetCopy:
|
||||
signed offset = decode();
|
||||
bool negative = offset & 1;
|
||||
offset >>= 1;
|
||||
if(negative) offset = -offset;
|
||||
|
||||
if(mode == SourceCopy) {
|
||||
sourceRelativeOffset += offset;
|
||||
while(length--) write(sourceData[sourceRelativeOffset++]);
|
||||
} else {
|
||||
targetRelativeOffset += offset;
|
||||
while(length--) write(targetData[targetRelativeOffset++]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
|
||||
uint32_t checksum = ~modifyChecksum;
|
||||
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
|
||||
|
||||
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
|
||||
targetChecksum = ~targetChecksum;
|
||||
|
||||
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
|
||||
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
|
||||
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
|
||||
|
||||
return result::success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
79
bsnes/nall/compositor.hpp
Executable file
79
bsnes/nall/compositor.hpp
Executable file
@@ -0,0 +1,79 @@
|
||||
#ifndef NALL_COMPOSITOR_HPP
|
||||
#define NALL_COMPOSITOR_HPP
|
||||
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct compositor {
|
||||
inline static bool enabled();
|
||||
inline static bool enable(bool status);
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
|
||||
bool compositor::enabled() {
|
||||
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||
|
||||
if(!memcmp(buffer, "true", 4)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||
} else {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
|
||||
}
|
||||
if(fp == 0) return false;
|
||||
pclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
bool compositor::enabled() {
|
||||
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == 0) return false;
|
||||
|
||||
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
|
||||
if(pDwmIsCompositionEnabled == 0) return false;
|
||||
|
||||
BOOL result;
|
||||
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||
if(module == 0) return false;
|
||||
|
||||
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
|
||||
if(pDwmEnableComposition == 0) return false;
|
||||
|
||||
if(pDwmEnableComposition(status) != S_OK) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool compositor::enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,34 +0,0 @@
|
||||
#ifndef NALL_CONCEPT_HPP
|
||||
#define NALL_CONCEPT_HPP
|
||||
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
//unsigned count() const;
|
||||
template<typename T> struct has_count { enum { value = false }; };
|
||||
|
||||
//unsigned length() const;
|
||||
template<typename T> struct has_length { enum { value = false }; };
|
||||
|
||||
//unsigned size() const;
|
||||
template<typename T> struct has_size { enum { value = false }; };
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
|
||||
return object.count();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
|
||||
return object.length();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
|
||||
return object.size();
|
||||
}
|
||||
|
||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
|
||||
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -34,11 +34,11 @@ namespace nall {
|
||||
|
||||
string get() const {
|
||||
switch(type) {
|
||||
case boolean_t: return string() << *(bool*)data;
|
||||
case signed_t: return string() << *(signed*)data;
|
||||
case unsigned_t: return string() << *(unsigned*)data;
|
||||
case double_t: return string() << *(double*)data;
|
||||
case string_t: return string() << "\"" << *(string*)data << "\"";
|
||||
case boolean_t: return { *(bool*)data };
|
||||
case signed_t: return { *(signed*)data };
|
||||
case unsigned_t: return { *(unsigned*)data };
|
||||
case double_t: return { *(double*)data };
|
||||
case string_t: return { "\"", *(string*)data, "\"" };
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace nall {
|
||||
else list[n].type = unknown_t;
|
||||
}
|
||||
|
||||
virtual bool load(const char *filename) {
|
||||
virtual bool load(const string &filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == true) {
|
||||
data.replace("\r", "");
|
||||
@@ -100,14 +100,14 @@ namespace nall {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool save(const char *filename) const {
|
||||
virtual bool save(const string &filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write)) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
string output;
|
||||
output << list[i].name << " = " << list[i].get();
|
||||
if(list[i].desc != "") output << " # " << list[i].desc;
|
||||
output << "\r\n";
|
||||
output.append(list[i].name, " = ", list[i].get());
|
||||
if(list[i].desc != "") output.append(" # ", list[i].desc);
|
||||
output.append("\r\n");
|
||||
fp.print(output);
|
||||
}
|
||||
|
||||
|
@@ -1,30 +0,0 @@
|
||||
#ifndef NALL_DETECT_HPP
|
||||
#define NALL_DETECT_HPP
|
||||
|
||||
/* Compiler detection */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_VISUALC
|
||||
#endif
|
||||
|
||||
/* Platform detection */
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define PLATFORM_WIN
|
||||
#elif defined(__APPLE__)
|
||||
#define PLATFORM_OSX
|
||||
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define PLATFORM_X
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||
#define ARCH_LSB
|
||||
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
||||
#define ARCH_MSB
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -1,75 +0,0 @@
|
||||
#ifndef NALL_DICTIONARY_HPP
|
||||
#define NALL_DICTIONARY_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
class dictionary {
|
||||
public:
|
||||
string operator[](const char *input) {
|
||||
for(unsigned i = 0; i < index_input.size(); i++) {
|
||||
if(index_input[i] == input) return index_output[i];
|
||||
}
|
||||
|
||||
//no match, use input; remove input identifier, if one exists
|
||||
if(strbegin(input, "{{")) {
|
||||
if(auto pos = strpos(input, "}}")) {
|
||||
string temp = substr(input, pos() + 2);
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
bool import(const char *filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == false) return false;
|
||||
data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
|
||||
data.replace("\r", "");
|
||||
|
||||
lstring line;
|
||||
line.split("\n", data);
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
lstring part;
|
||||
//format: "Input" = "Output"
|
||||
part.qsplit("=", line[i]);
|
||||
if(part.size() != 2) continue;
|
||||
|
||||
//remove whitespace
|
||||
part[0].trim();
|
||||
part[1].trim();
|
||||
|
||||
//remove quotes
|
||||
part[0].trim<1>("\"");
|
||||
part[1].trim<1>("\"");
|
||||
|
||||
unsigned n = index_input.size();
|
||||
index_input[n] = part[0];
|
||||
index_output[n] = part[1];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
index_input.reset();
|
||||
index_output.reset();
|
||||
}
|
||||
|
||||
~dictionary() {
|
||||
reset();
|
||||
}
|
||||
|
||||
dictionary& operator=(const dictionary&) = delete;
|
||||
dictionary(const dictionary&) = delete;
|
||||
|
||||
protected:
|
||||
lstring index_input;
|
||||
lstring index_output;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,12 +1,13 @@
|
||||
#ifndef NALL_DIRECTORY_HPP
|
||||
#define NALL_DIRECTORY_HPP
|
||||
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <nall/utf8.hpp>
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
@@ -22,7 +23,7 @@ struct directory {
|
||||
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
inline bool directory::exists(const string &pathname) {
|
||||
DWORD result = GetFileAttributes(utf16_t(pathname));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
@@ -42,20 +43,21 @@ struct directory {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(string(name, "/"));
|
||||
if(wildcard(name, pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(wildcard(name, pattern)) list.append(string(name, "/"));
|
||||
if(wildcard(name, pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -88,7 +90,7 @@ struct directory {
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#else
|
||||
@@ -109,14 +111,14 @@ struct directory {
|
||||
if(!strcmp(ep->d_name, ".")) continue;
|
||||
if(!strcmp(ep->d_name, "..")) continue;
|
||||
if(ep->d_type & DT_DIR) {
|
||||
if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
|
||||
if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
inline lstring directory::files(const string &pathname, const string &pattern) {
|
||||
@@ -141,7 +143,7 @@ struct directory {
|
||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#endif
|
||||
|
@@ -3,16 +3,16 @@
|
||||
|
||||
//dynamic linking support
|
||||
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||
#include <dlfcn.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
namespace nall {
|
||||
@@ -81,7 +81,7 @@ namespace nall {
|
||||
dlclose((void*)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
||||
|
13
bsnes/nall/dsp.hpp
Executable file
13
bsnes/nall/dsp.hpp
Executable file
@@ -0,0 +1,13 @@
|
||||
#ifndef NALL_DSP_HPP
|
||||
#define NALL_DSP_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#ifdef __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define NALL_DSP_INTERNAL_HPP
|
||||
#include <nall/dsp/core.hpp>
|
||||
#undef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#endif
|
51
bsnes/nall/dsp/buffer.hpp
Executable file
51
bsnes/nall/dsp/buffer.hpp
Executable file
@@ -0,0 +1,51 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct Buffer {
|
||||
double **sample;
|
||||
uint16_t rdoffset;
|
||||
uint16_t wroffset;
|
||||
unsigned channels;
|
||||
|
||||
void setChannels(unsigned channels) {
|
||||
for(unsigned c = 0; c < this->channels; c++) {
|
||||
if(sample[c]) delete[] sample[c];
|
||||
}
|
||||
if(sample) delete[] sample;
|
||||
|
||||
this->channels = channels;
|
||||
if(channels == 0) return;
|
||||
|
||||
sample = new double*[channels];
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
sample[c] = new double[65536]();
|
||||
}
|
||||
}
|
||||
|
||||
inline double& read(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(rdoffset + offset)];
|
||||
}
|
||||
|
||||
inline double& write(unsigned channel, signed offset = 0) {
|
||||
return sample[channel][(uint16_t)(wroffset + offset)];
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
for(unsigned c = 0; c < channels; c++) {
|
||||
for(unsigned n = 0; n < 65536; n++) {
|
||||
sample[c][n] = 0;
|
||||
}
|
||||
}
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
}
|
||||
|
||||
Buffer() {
|
||||
channels = 0;
|
||||
}
|
||||
|
||||
~Buffer() {
|
||||
setChannels(0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
167
bsnes/nall/dsp/core.hpp
Executable file
167
bsnes/nall/dsp/core.hpp
Executable file
@@ -0,0 +1,167 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include <math.h>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
//precision: can be float, double or long double
|
||||
#define real float
|
||||
|
||||
struct DSP;
|
||||
|
||||
struct Resampler {
|
||||
DSP &dsp;
|
||||
real frequency;
|
||||
|
||||
virtual void setFrequency() = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void sample() = 0;
|
||||
Resampler(DSP &dsp) : dsp(dsp) {}
|
||||
};
|
||||
|
||||
struct DSP {
|
||||
enum class ResampleEngine : unsigned {
|
||||
Nearest,
|
||||
Linear,
|
||||
Cosine,
|
||||
Cubic,
|
||||
Hermite,
|
||||
Average,
|
||||
Sinc,
|
||||
};
|
||||
|
||||
inline void setChannels(unsigned channels);
|
||||
inline void setPrecision(unsigned precision);
|
||||
inline void setFrequency(real frequency); //inputFrequency
|
||||
inline void setVolume(real volume);
|
||||
inline void setBalance(real balance);
|
||||
|
||||
inline void setResampler(ResampleEngine resamplingEngine);
|
||||
inline void setResamplerFrequency(real frequency); //outputFrequency
|
||||
|
||||
inline void sample(signed channel[]);
|
||||
inline bool pending();
|
||||
inline void read(signed channel[]);
|
||||
|
||||
inline void clear();
|
||||
inline DSP();
|
||||
inline ~DSP();
|
||||
|
||||
protected:
|
||||
friend class ResampleNearest;
|
||||
friend class ResampleLinear;
|
||||
friend class ResampleCosine;
|
||||
friend class ResampleCubic;
|
||||
friend class ResampleAverage;
|
||||
friend class ResampleHermite;
|
||||
friend class ResampleSinc;
|
||||
|
||||
struct Settings {
|
||||
unsigned channels;
|
||||
unsigned precision;
|
||||
real frequency;
|
||||
real volume;
|
||||
real balance;
|
||||
|
||||
//internal
|
||||
real intensity;
|
||||
real intensityInverse;
|
||||
} settings;
|
||||
|
||||
Resampler *resampler;
|
||||
inline void write(real channel[]);
|
||||
|
||||
#include "buffer.hpp"
|
||||
Buffer buffer;
|
||||
Buffer output;
|
||||
|
||||
inline void adjustVolume();
|
||||
inline void adjustBalance();
|
||||
inline signed clamp(const unsigned bits, const signed x);
|
||||
};
|
||||
|
||||
#include "resample/nearest.hpp"
|
||||
#include "resample/linear.hpp"
|
||||
#include "resample/cosine.hpp"
|
||||
#include "resample/cubic.hpp"
|
||||
#include "resample/hermite.hpp"
|
||||
#include "resample/average.hpp"
|
||||
#include "resample/sinc.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
void DSP::sample(signed channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
|
||||
}
|
||||
buffer.wroffset++;
|
||||
resampler->sample();
|
||||
}
|
||||
|
||||
bool DSP::pending() {
|
||||
return output.rdoffset != output.wroffset;
|
||||
}
|
||||
|
||||
void DSP::read(signed channel[]) {
|
||||
adjustVolume();
|
||||
adjustBalance();
|
||||
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
|
||||
}
|
||||
output.rdoffset++;
|
||||
}
|
||||
|
||||
void DSP::write(real channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = channel[c];
|
||||
}
|
||||
output.wroffset++;
|
||||
}
|
||||
|
||||
void DSP::adjustVolume() {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.read(c) *= settings.volume;
|
||||
}
|
||||
}
|
||||
|
||||
void DSP::adjustBalance() {
|
||||
if(settings.channels != 2) return; //TODO: support > 2 channels
|
||||
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
|
||||
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
|
||||
}
|
||||
|
||||
signed DSP::clamp(const unsigned bits, const signed x) {
|
||||
const signed b = 1U << (bits - 1);
|
||||
const signed m = (1U << (bits - 1)) - 1;
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
void DSP::clear() {
|
||||
buffer.clear();
|
||||
output.clear();
|
||||
resampler->clear();
|
||||
}
|
||||
|
||||
DSP::DSP() {
|
||||
setResampler(ResampleEngine::Hermite);
|
||||
setResamplerFrequency(44100.0);
|
||||
|
||||
setChannels(2);
|
||||
setPrecision(16);
|
||||
setFrequency(44100.0);
|
||||
setVolume(1.0);
|
||||
setBalance(0.0);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
DSP::~DSP() {
|
||||
if(resampler) delete resampler;
|
||||
}
|
||||
|
||||
#undef real
|
||||
|
||||
}
|
||||
|
||||
#endif
|
72
bsnes/nall/dsp/resample/average.hpp
Executable file
72
bsnes/nall/dsp/resample/average.hpp
Executable file
@@ -0,0 +1,72 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleAverage : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline void sampleLinear();
|
||||
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleAverage::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleAverage::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleAverage::sample() {
|
||||
//can only average if input frequency >= output frequency
|
||||
if(step < 1.0) return sampleLinear();
|
||||
|
||||
fraction += 1.0;
|
||||
|
||||
real scalar = 1.0;
|
||||
if(fraction > step) scalar = 1.0 - (fraction - step);
|
||||
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
|
||||
}
|
||||
|
||||
if(fraction >= step) {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) /= step;
|
||||
}
|
||||
dsp.output.wroffset++;
|
||||
|
||||
fraction -= step;
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
|
||||
}
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
void ResampleAverage::sampleLinear() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
44
bsnes/nall/dsp/resample/cosine.hpp
Executable file
44
bsnes/nall/dsp/resample/cosine.hpp
Executable file
@@ -0,0 +1,44 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleCosine : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleCosine::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleCosine::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCosine::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
50
bsnes/nall/dsp/resample/cubic.hpp
Executable file
50
bsnes/nall/dsp/resample/cubic.hpp
Executable file
@@ -0,0 +1,50 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleCubic : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleCubic::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleCubic::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCubic::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
real A = d - c - a + b;
|
||||
real B = a - b - A;
|
||||
real C = c - a;
|
||||
real D = b;
|
||||
|
||||
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
62
bsnes/nall/dsp/resample/hermite.hpp
Executable file
62
bsnes/nall/dsp/resample/hermite.hpp
Executable file
@@ -0,0 +1,62 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleHermite : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleHermite::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleHermite::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleHermite::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
|
||||
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu1 = fraction;
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
600
bsnes/nall/dsp/resample/lib/sinc.hpp
Executable file
600
bsnes/nall/dsp/resample/lib/sinc.hpp
Executable file
@@ -0,0 +1,600 @@
|
||||
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
|
||||
// so that the SSE code is not used.
|
||||
|
||||
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
|
||||
typedef float resample_samp_t;
|
||||
|
||||
|
||||
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
|
||||
#define RESAMPLE_SSEREGPARM
|
||||
|
||||
#if defined(__SSE__)
|
||||
#define SINCRESAMPLE_USE_SSE 1
|
||||
#ifndef __x86_64__
|
||||
#undef RESAMPLE_SSEREGPARM
|
||||
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
|
||||
#endif
|
||||
#else
|
||||
// TODO: altivec here
|
||||
#endif
|
||||
|
||||
namespace ResampleUtility
|
||||
{
|
||||
inline void kaiser_window(double* io, int count, double beta);
|
||||
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
|
||||
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
|
||||
inline void normalize(double* io, int size, double gain = 1.0);
|
||||
|
||||
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
|
||||
}
|
||||
|
||||
class SincResampleHR
|
||||
{
|
||||
private:
|
||||
|
||||
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
|
||||
|
||||
unsigned ratio;
|
||||
unsigned num_convolutions;
|
||||
|
||||
resample_coeff_t *coeffs;
|
||||
std::vector<unsigned char> coeffs_mem;
|
||||
|
||||
// second half of ringbuffer should be copy of first half.
|
||||
resample_samp_t *rb;
|
||||
std::vector<unsigned char> rb_mem;
|
||||
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
signed rb_eff_size;
|
||||
|
||||
friend class SincResample;
|
||||
};
|
||||
|
||||
class SincResample
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
QUALITY_LOW = 0,
|
||||
QUALITY_MEDIUM = 2,
|
||||
QUALITY_HIGH = 4
|
||||
};
|
||||
|
||||
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
|
||||
|
||||
unsigned num_convolutions;
|
||||
unsigned num_phases;
|
||||
|
||||
unsigned step_int;
|
||||
double step_fract;
|
||||
|
||||
double input_pos_fract;
|
||||
|
||||
|
||||
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
|
||||
std::vector<unsigned char> coeff_mem;
|
||||
|
||||
|
||||
std::vector<resample_samp_t> rb; // second half should be copy of first half.
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
|
||||
bool hr_used;
|
||||
SincResampleHR hr;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Code:
|
||||
//
|
||||
//#include "resample.hpp"
|
||||
|
||||
#if 0
|
||||
namespace bit
|
||||
{
|
||||
inline unsigned round(unsigned x) {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
|
||||
{
|
||||
const unsigned align_boundary = 16;
|
||||
std::vector<double> coeffs_tmp;
|
||||
double cutoff; // 1.0 = f/2
|
||||
|
||||
ratio = ratio_arg;
|
||||
|
||||
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
|
||||
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
|
||||
|
||||
cutoff = (1.0 / ratio) - (d / num_convolutions);
|
||||
|
||||
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
|
||||
assert(num_convolutions > ratio);
|
||||
|
||||
|
||||
// Generate windowed sinc of POWER
|
||||
coeffs_tmp.resize(num_convolutions);
|
||||
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
|
||||
|
||||
// Copy from coeffs_tmp to coeffs~
|
||||
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
|
||||
// exist are allocated, zero'd mem.
|
||||
|
||||
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
|
||||
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
|
||||
|
||||
|
||||
for(unsigned i = 0; i < num_convolutions; i++)
|
||||
coeffs[i] = coeffs_tmp[i];
|
||||
|
||||
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
|
||||
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
|
||||
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
|
||||
}
|
||||
|
||||
|
||||
inline bool SincResampleHR::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (signed)num_convolutions);
|
||||
}
|
||||
|
||||
inline void SincResampleHR::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
rb[rb_writepos] = sample;
|
||||
rb[rb_writepos + rb_eff_size] = sample;
|
||||
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
|
||||
{
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
resample_samp_t accum;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
__m128 co[2];
|
||||
__m128 w[2];
|
||||
|
||||
co[i] = _mm_load_ps(&coeff[c + i * 4]);
|
||||
w[i] = _mm_load_ps(&wave[c + i * 4]);
|
||||
|
||||
w[i] = _mm_mul_ps(w[i], co[i]);
|
||||
|
||||
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
|
||||
return accum;
|
||||
#else
|
||||
resample_samp_t accum[4] = { 0, 0, 0, 0 };
|
||||
|
||||
for(unsigned c = 0; c < count; c+= 4)
|
||||
{
|
||||
accum[0] += wave[c + 0] * coeff[c + 0];
|
||||
accum[1] += wave[c + 1] * coeff[c + 1];
|
||||
accum[2] += wave[c + 2] * coeff[c + 2];
|
||||
accum[3] += wave[c + 3] * coeff[c + 3];
|
||||
}
|
||||
|
||||
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
resample_samp_t SincResampleHR::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
|
||||
|
||||
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
|
||||
rb_in -= ratio;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
|
||||
{
|
||||
const struct
|
||||
{
|
||||
double beta;
|
||||
double d;
|
||||
unsigned pn_nume;
|
||||
unsigned phases_min;
|
||||
} qtab[5] =
|
||||
{
|
||||
{ 5.658, 3.62, 4096, 4 },
|
||||
{ 6.764, 4.32, 8192, 4 },
|
||||
{ 7.865, 5.0, 16384, 8 },
|
||||
{ 8.960, 5.7, 32768, 16 },
|
||||
{ 10.056, 6.4, 65536, 32 }
|
||||
};
|
||||
|
||||
// Sanity checks
|
||||
assert(ceil(input_rate) > 0);
|
||||
assert(ceil(output_rate) > 0);
|
||||
assert(ceil(input_rate / output_rate) <= 1024);
|
||||
assert(ceil(output_rate / input_rate) <= 1024);
|
||||
|
||||
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
|
||||
// upsampling.
|
||||
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
|
||||
assert(quality >= 0 && quality <= 4);
|
||||
|
||||
hr_used = false;
|
||||
|
||||
#if 1
|
||||
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
|
||||
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
|
||||
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
|
||||
// to virtually eliminate over-attenuation.
|
||||
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
|
||||
|
||||
if(ioratio_rd >= 8)
|
||||
{
|
||||
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
|
||||
hr_used = true;
|
||||
|
||||
input_rate /= ioratio_rd;
|
||||
}
|
||||
#endif
|
||||
|
||||
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
|
||||
}
|
||||
|
||||
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
|
||||
{
|
||||
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
|
||||
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
|
||||
const unsigned conv_alignment_bytes = 16; // must be power of 2
|
||||
const double input_to_output_ratio = input_rate / output_rate;
|
||||
const double output_to_input_ratio = output_rate / input_rate;
|
||||
double cutoff; // 1.0 = input_rate / 2
|
||||
std::vector<double> coeff_init_buffer;
|
||||
|
||||
// Round up num_convolutions to be even.
|
||||
if(output_rate > input_rate)
|
||||
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
|
||||
else
|
||||
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
|
||||
|
||||
if(output_rate > input_rate) // Upsampling
|
||||
cutoff = desired_bandwidth;
|
||||
else // Downsampling
|
||||
cutoff = output_to_input_ratio * desired_bandwidth;
|
||||
|
||||
// Round up to be even.
|
||||
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
|
||||
|
||||
// Adjust cutoff to account for the multiple phases.
|
||||
cutoff = cutoff / num_phases;
|
||||
|
||||
assert((num_convolutions & 1) == 0);
|
||||
assert((num_phases & 1) == 0);
|
||||
|
||||
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
|
||||
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
|
||||
|
||||
coeff_init_buffer.resize(num_phases * num_convolutions);
|
||||
|
||||
coeffs.resize(num_phases + 1 + 1);
|
||||
|
||||
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
|
||||
|
||||
// Assign aligned pointers into coeff_mem
|
||||
{
|
||||
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
|
||||
|
||||
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
|
||||
{
|
||||
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
|
||||
}
|
||||
}
|
||||
|
||||
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
|
||||
|
||||
// Reorder coefficients to allow for more efficient convolution.
|
||||
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
|
||||
{
|
||||
for(int conv = 0; conv < (int)num_convolutions; conv++)
|
||||
{
|
||||
double coeff;
|
||||
|
||||
if(phase == -1 && conv == 0)
|
||||
coeff = 0;
|
||||
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
|
||||
coeff = 0;
|
||||
else
|
||||
coeff = coeff_init_buffer[conv * num_phases + phase];
|
||||
|
||||
coeffs[phase + 1][conv] = coeff;
|
||||
}
|
||||
}
|
||||
|
||||
// Free a bit of mem
|
||||
coeff_init_buffer.resize(0);
|
||||
|
||||
step_int = floor(input_to_output_ratio);
|
||||
step_fract = input_to_output_ratio - step_int;
|
||||
|
||||
input_pos_fract = 0;
|
||||
|
||||
// Do NOT use rb.size() later in the code, since it'll include the padding.
|
||||
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
|
||||
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
|
||||
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
|
||||
// prevent illegal memory accesses.
|
||||
rb.resize(num_convolutions * 2 + max_mult_minus1);
|
||||
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
|
||||
{
|
||||
resample_samp_t accum = 0;
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8) //8) //4)
|
||||
{
|
||||
__m128 coeff_a[2];
|
||||
__m128 coeff_b[2];
|
||||
__m128 w[2];
|
||||
__m128 result_a[2], result_b[2];
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
|
||||
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
|
||||
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
|
||||
|
||||
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
|
||||
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
|
||||
|
||||
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
|
||||
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec, av_a, av_b;
|
||||
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
|
||||
__m128 mult_b_vec = _mm_set1_ps(ffract);
|
||||
|
||||
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
|
||||
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
|
||||
|
||||
accum_vec = _mm_add_ps(av_a, av_b);
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
#else
|
||||
resample_coeff_t mult_a = 1.0 - ffract;
|
||||
resample_coeff_t mult_b = ffract;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 4)
|
||||
{
|
||||
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
|
||||
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
|
||||
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
|
||||
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
return accum;
|
||||
}
|
||||
|
||||
inline bool SincResample::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (int)num_convolutions);
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
double phase = input_pos_fract * num_phases - 0.5;
|
||||
signed phase_int = (signed)floor(phase);
|
||||
double phase_fract = phase - phase_int;
|
||||
unsigned phase_a = num_phases - 1 - phase_int;
|
||||
unsigned phase_b = phase_a - 1;
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
|
||||
|
||||
unsigned int_increment = step_int;
|
||||
|
||||
input_pos_fract += step_fract;
|
||||
int_increment += floor(input_pos_fract);
|
||||
input_pos_fract -= floor(input_pos_fract);
|
||||
|
||||
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
|
||||
rb_in -= int_increment;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void SincResample::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
if(hr_used)
|
||||
{
|
||||
hr.write(sample);
|
||||
|
||||
if(hr.output_avail())
|
||||
{
|
||||
sample = hr.read();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb[rb_writepos + 0 * num_convolutions] = sample;
|
||||
rb[rb_writepos + 1 * num_convolutions] = sample;
|
||||
rb_writepos = (rb_writepos + 1) % num_convolutions;
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
void ResampleUtility::kaiser_window( double* io, int count, double beta)
|
||||
{
|
||||
int const accuracy = 24; //16; //12;
|
||||
|
||||
double* end = io + count;
|
||||
|
||||
double beta2 = beta * beta * (double) -0.25;
|
||||
double to_fract = beta2 / ((double) count * count);
|
||||
double i = 0;
|
||||
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||
|
||||
for ( ; io < end; ++io, i += 1 )
|
||||
{
|
||||
double x = i * i * to_fract - beta2;
|
||||
double u = x;
|
||||
double k = x + 1;
|
||||
|
||||
double n = 2;
|
||||
do
|
||||
{
|
||||
u *= x / (n * n);
|
||||
n += 1;
|
||||
k += u;
|
||||
}
|
||||
while ( k <= u * (1 << accuracy) );
|
||||
|
||||
if ( !i )
|
||||
rescale = 1 / k; // otherwise values get large
|
||||
|
||||
*io *= k * rescale;
|
||||
}
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 0 ); // size must be even
|
||||
|
||||
int const half_size = size / 2;
|
||||
double* const mid = &out [half_size];
|
||||
|
||||
// Generate right half of sinc
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
{
|
||||
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||
mid [i] = sin( angle * cutoff ) / angle;
|
||||
}
|
||||
|
||||
kaiser_window( mid, half_size, kaiser );
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
out [i] = mid [half_size - 1 - i];
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 1); // size must be odd
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
if(i == (size / 2))
|
||||
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
else
|
||||
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
|
||||
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
|
||||
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
|
||||
|
||||
// printf("%d %f\n", i, out[i]);
|
||||
}
|
||||
|
||||
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < size / 2; i++ )
|
||||
out [i] = out [size - 1 - i];
|
||||
|
||||
}
|
||||
|
||||
void ResampleUtility::normalize(double* io, int size, double gain)
|
||||
{
|
||||
double sum = 0;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
sum += io [i];
|
||||
|
||||
double scale = gain / sum;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
io [i] *= scale;
|
||||
}
|
||||
|
||||
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
|
||||
{
|
||||
unsigned char* null_ptr = (unsigned char *)NULL;
|
||||
unsigned char* uc_ptr = (unsigned char *)ptr;
|
||||
|
||||
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
|
||||
|
||||
//while((uc_ptr - null_ptr) & (boundary - 1))
|
||||
// uc_ptr++;
|
||||
|
||||
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
|
||||
|
||||
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
|
||||
|
||||
return uc_ptr;
|
||||
}
|
43
bsnes/nall/dsp/resample/linear.hpp
Executable file
43
bsnes/nall/dsp/resample/linear.hpp
Executable file
@@ -0,0 +1,43 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleLinear : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleLinear::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleLinear::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleLinear::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
43
bsnes/nall/dsp/resample/nearest.hpp
Executable file
43
bsnes/nall/dsp/resample/nearest.hpp
Executable file
@@ -0,0 +1,43 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleNearest : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleNearest::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleNearest::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleNearest::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = mu < 0.5 ? a : b;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
54
bsnes/nall/dsp/resample/sinc.hpp
Executable file
54
bsnes/nall/dsp/resample/sinc.hpp
Executable file
@@ -0,0 +1,54 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include "lib/sinc.hpp"
|
||||
|
||||
struct ResampleSinc : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline ResampleSinc(DSP &dsp);
|
||||
|
||||
private:
|
||||
inline void remakeSinc();
|
||||
SincResample *sinc_resampler[8];
|
||||
};
|
||||
|
||||
void ResampleSinc::setFrequency() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::clear() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::sample() {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
sinc_resampler[c]->write(dsp.buffer.read(c));
|
||||
}
|
||||
|
||||
if(sinc_resampler[0]->output_avail()) {
|
||||
do {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = sinc_resampler[c]->read();
|
||||
}
|
||||
dsp.output.wroffset++;
|
||||
} while(sinc_resampler[0]->output_avail());
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
|
||||
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
|
||||
}
|
||||
|
||||
void ResampleSinc::remakeSinc() {
|
||||
assert(dsp.settings.channels < 8);
|
||||
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
if(sinc_resampler[c]) delete sinc_resampler[c];
|
||||
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
50
bsnes/nall/dsp/settings.hpp
Executable file
50
bsnes/nall/dsp/settings.hpp
Executable file
@@ -0,0 +1,50 @@
|
||||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::setChannels(unsigned channels) {
|
||||
assert(channels > 0);
|
||||
buffer.setChannels(channels);
|
||||
output.setChannels(channels);
|
||||
settings.channels = channels;
|
||||
}
|
||||
|
||||
void DSP::setPrecision(unsigned precision) {
|
||||
settings.precision = precision;
|
||||
settings.intensity = 1 << (settings.precision - 1);
|
||||
settings.intensityInverse = 1.0 / settings.intensity;
|
||||
}
|
||||
|
||||
void DSP::setFrequency(real frequency) {
|
||||
settings.frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
void DSP::setVolume(real volume) {
|
||||
settings.volume = volume;
|
||||
}
|
||||
|
||||
void DSP::setBalance(real balance) {
|
||||
settings.balance = balance;
|
||||
}
|
||||
|
||||
void DSP::setResampler(ResampleEngine engine) {
|
||||
if(resampler) delete resampler;
|
||||
|
||||
switch(engine) {
|
||||
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
|
||||
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
|
||||
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
|
||||
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
|
||||
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
|
||||
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
|
||||
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void DSP::setResamplerFrequency(real frequency) {
|
||||
resampler->frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,7 +1,9 @@
|
||||
#ifndef NALL_ENDIAN_HPP
|
||||
#define NALL_ENDIAN_HPP
|
||||
|
||||
#if !defined(ARCH_MSB)
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
#if defined(ENDIAN_LSB)
|
||||
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||
#define order_lsb2(a,b) a,b
|
||||
#define order_lsb3(a,b,c) a,b,c
|
||||
@@ -17,7 +19,7 @@
|
||||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#else
|
||||
#elif defined(ENDIAN_MSB)
|
||||
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||
#define order_lsb2(a,b) b,a
|
||||
#define order_lsb3(a,b,c) c,b,a
|
||||
@@ -33,6 +35,8 @@
|
||||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#else
|
||||
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -1,22 +1,14 @@
|
||||
#ifndef NALL_FILE_HPP
|
||||
#define NALL_FILE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
||||
namespace nall {
|
||||
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
|
||||
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
||||
#if !defined(_WIN32)
|
||||
return fopen(utf8_filename, mode);
|
||||
#else
|
||||
@@ -28,6 +20,30 @@ namespace nall {
|
||||
public:
|
||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||
enum class index : unsigned { absolute, relative };
|
||||
enum class time : unsigned { create, modify, access };
|
||||
|
||||
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
|
||||
data = 0;
|
||||
file fp;
|
||||
if(fp.open(filename, mode::read) == false) return false;
|
||||
size = fp.size();
|
||||
data = new uint8_t[size];
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
|
||||
return file::read(filename, (uint8_t*&)data, size);
|
||||
}
|
||||
|
||||
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
fp.write(data, size);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
if(!fp) return 0xff; //file not open
|
||||
@@ -142,52 +158,60 @@ namespace nall {
|
||||
return file_offset >= file_size;
|
||||
}
|
||||
|
||||
static bool exists(const char *fn) {
|
||||
static bool exists(const string &filename) {
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
struct stat64 data;
|
||||
return stat64(filename, &data) == 0;
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||||
struct __stat64 data;
|
||||
return _wstat64(utf16_t(filename), &data) == 0;
|
||||
#endif
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned size(const char *fn) {
|
||||
static uintmax_t size(const string &filename) {
|
||||
#if !defined(_WIN32)
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
struct stat64 data;
|
||||
stat64(filename, &data);
|
||||
#else
|
||||
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||||
struct __stat64 data;
|
||||
_wstat64(utf16_t(filename), &data);
|
||||
#endif
|
||||
unsigned filesize = 0;
|
||||
if(fp) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
filesize = ftell(fp);
|
||||
fclose(fp);
|
||||
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
||||
}
|
||||
|
||||
static time_t timestamp(const string &filename, file::time mode = file::time::create) {
|
||||
#if !defined(_WIN32)
|
||||
struct stat64 data;
|
||||
stat64(filename, &data);
|
||||
#else
|
||||
struct __stat64 data;
|
||||
_wstat64(utf16_t(filename), &data);
|
||||
#endif
|
||||
switch(mode) { default:
|
||||
case file::time::create: return data.st_ctime;
|
||||
case file::time::modify: return data.st_mtime;
|
||||
case file::time::access: return data.st_atime;
|
||||
}
|
||||
return filesize;
|
||||
}
|
||||
|
||||
bool open() {
|
||||
return fp;
|
||||
}
|
||||
|
||||
bool open(const char *fn, mode mode_) {
|
||||
bool open(const string &filename, mode mode_) {
|
||||
if(fp) return false;
|
||||
|
||||
switch(file_mode = mode_) {
|
||||
#if !defined(_WIN32)
|
||||
case mode::read: fp = fopen(fn, "rb"); break;
|
||||
case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
|
||||
case mode::readwrite: fp = fopen(fn, "rb+"); break;
|
||||
case mode::writeread: fp = fopen(fn, "wb+"); break;
|
||||
case mode::read: fp = fopen(filename, "rb" ); break;
|
||||
case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
|
||||
case mode::readwrite: fp = fopen(filename, "rb+"); break;
|
||||
case mode::writeread: fp = fopen(filename, "wb+"); break;
|
||||
#else
|
||||
case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break;
|
||||
case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
|
||||
case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||||
case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
|
||||
case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
|
||||
case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
|
||||
case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
|
||||
#endif
|
||||
}
|
||||
if(!fp) return false;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
#define NALL_FILEMAP_HPP
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -21,7 +21,7 @@ namespace nall {
|
||||
public:
|
||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||
|
||||
bool opened() const { return p_opened(); }
|
||||
bool open() const { return p_open(); }
|
||||
bool open(const char *filename, mode mode_) { return p_open(filename, mode_); }
|
||||
void close() { return p_close(); }
|
||||
unsigned size() const { return p_size; }
|
||||
@@ -42,11 +42,17 @@ namespace nall {
|
||||
|
||||
HANDLE p_filehandle, p_maphandle;
|
||||
|
||||
bool p_opened() const {
|
||||
bool p_open() const {
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
bool p_open(const char *filename, mode mode_) {
|
||||
if(file::exists(filename) && file::size(filename) == 0) {
|
||||
p_handle = 0;
|
||||
p_size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int desired_access, creation_disposition, flprotect, map_access;
|
||||
|
||||
switch(mode_) {
|
||||
@@ -128,11 +134,17 @@ namespace nall {
|
||||
|
||||
int p_fd;
|
||||
|
||||
bool p_opened() const {
|
||||
bool p_open() const {
|
||||
return p_handle;
|
||||
}
|
||||
|
||||
bool p_open(const char *filename, mode mode_) {
|
||||
if(file::exists(filename) && file::size(filename) == 0) {
|
||||
p_handle = 0;
|
||||
p_size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int open_flags, mmap_flags;
|
||||
|
||||
switch(mode_) {
|
||||
|
@@ -1,18 +0,0 @@
|
||||
#ifndef NALL_FOREACH_HPP
|
||||
#define NALL_FOREACH_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <nall/concept.hpp>
|
||||
|
||||
#undef foreach
|
||||
|
||||
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
|
||||
#define foreach3(iter, object, foreach_counter) \
|
||||
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
|
||||
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
|
||||
|
||||
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
|
||||
#define foreach_decl(_1, _2, _3, N, ...) N
|
||||
#define foreach(...) foreach_impl(__VA_ARGS__)
|
||||
|
||||
#endif
|
@@ -36,19 +36,19 @@ namespace nall {
|
||||
public:
|
||||
operator bool() const { return callback; }
|
||||
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
||||
void reset() { if(callback) { delete callback; callback = 0; } }
|
||||
void reset() { if(callback) { delete callback; callback = nullptr; } }
|
||||
|
||||
function& operator=(const function &source) {
|
||||
if(this != &source) {
|
||||
if(callback) { delete callback; callback = 0; }
|
||||
if(callback) { delete callback; callback = nullptr; }
|
||||
if(source.callback) callback = source.callback->copy();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
function(const function &source) : callback(0) { operator=(source); }
|
||||
function() : callback(0) {}
|
||||
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(const function &source) : callback(nullptr) { operator=(source); }
|
||||
function() : callback(nullptr) {}
|
||||
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(R (*function)(P...)) { callback = new global(function); }
|
||||
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
||||
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
||||
|
@@ -5,8 +5,8 @@ namespace nall {
|
||||
|
||||
class GameBoyCartridge {
|
||||
public:
|
||||
string xml;
|
||||
inline GameBoyCartridge(const uint8_t *data, unsigned size);
|
||||
string markup;
|
||||
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
@@ -21,8 +21,8 @@ public:
|
||||
} info;
|
||||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
||||
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
markup = "";
|
||||
if(romsize < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
@@ -34,6 +34,20 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
unsigned base = romsize - 0x8000;
|
||||
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||
) {
|
||||
//MMM01 stores header at bottom of image
|
||||
//flip this around for consistency with all other mappers
|
||||
uint8_t header[0x8000];
|
||||
memcpy(header, romdata + base, 0x8000);
|
||||
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||
memcpy(romdata, header, 0x8000);
|
||||
}
|
||||
|
||||
switch(romdata[0x0147]) {
|
||||
case 0x00: info.mapper = "none"; break;
|
||||
case 0x01: info.mapper = "MBC1"; break;
|
||||
@@ -86,18 +100,15 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
xml << "<cartridge mapper='" << info.mapper << "'";
|
||||
if(info.rtc) xml << " rtc='true'";
|
||||
if(info.rumble) xml << " rumble='true'";
|
||||
xml << ">\n";
|
||||
markup.append("cartridge mapper=", info.mapper);
|
||||
if(info.rtc) markup.append(" rtc");
|
||||
if(info.rumble) markup.append(" rumble");
|
||||
markup.append("\n");
|
||||
|
||||
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
|
||||
markup.append("\t" "rom size=", hex(romsize), "\n"); //TODO: trust/check info.romsize?
|
||||
|
||||
if(info.ramsize > 0)
|
||||
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
|
||||
|
||||
xml << "</cartridge>\n";
|
||||
xml.transform("'", "\"");
|
||||
markup.append("\t" "ram size=", hex(info.ramsize), info.battery ? " non-volatile\n" : "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
87
bsnes/nall/gzip.hpp
Executable file
87
bsnes/nall/gzip.hpp
Executable file
@@ -0,0 +1,87 @@
|
||||
#ifndef NALL_GZIP_HPP
|
||||
#define NALL_GZIP_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/inflate.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct gzip {
|
||||
string filename;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
bool decompress(const string &filename);
|
||||
bool decompress(const uint8_t *data, unsigned size);
|
||||
|
||||
gzip();
|
||||
~gzip();
|
||||
};
|
||||
|
||||
bool gzip::decompress(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
bool result = decompress(data, size);
|
||||
delete[] data;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
||||
if(size < 18) return false;
|
||||
if(data[0] != 0x1f) return false;
|
||||
if(data[1] != 0x8b) return false;
|
||||
unsigned cm = data[2];
|
||||
unsigned flg = data[3];
|
||||
unsigned mtime = data[4];
|
||||
mtime |= data[5] << 8;
|
||||
mtime |= data[6] << 16;
|
||||
mtime |= data[7] << 24;
|
||||
unsigned xfl = data[8];
|
||||
unsigned os = data[9];
|
||||
unsigned p = 10;
|
||||
unsigned isize = data[size - 4];
|
||||
isize |= data[size - 3] << 8;
|
||||
isize |= data[size - 2] << 16;
|
||||
isize |= data[size - 1] << 24;
|
||||
filename = "";
|
||||
|
||||
if(flg & 0x04) { //FEXTRA
|
||||
unsigned xlen = data[p + 0];
|
||||
xlen |= data[p + 1] << 8;
|
||||
p += 2 + xlen;
|
||||
}
|
||||
|
||||
if(flg & 0x08) { //FNAME
|
||||
char buffer[PATH_MAX];
|
||||
for(unsigned n = 0; n < PATH_MAX; n++, p++) {
|
||||
buffer[n] = data[p];
|
||||
if(data[p] == 0) break;
|
||||
}
|
||||
if(data[p++]) return false;
|
||||
filename = buffer;
|
||||
}
|
||||
|
||||
if(flg & 0x10) { //FCOMMENT
|
||||
while(data[p++]);
|
||||
}
|
||||
|
||||
if(flg & 0x02) { //FHCRC
|
||||
p += 2;
|
||||
}
|
||||
|
||||
this->size = isize;
|
||||
this->data = new uint8_t[this->size];
|
||||
return inflate(this->data, this->size, data + p, size - p - 8);
|
||||
}
|
||||
|
||||
gzip::gzip() : data(nullptr) {
|
||||
}
|
||||
|
||||
gzip::~gzip() {
|
||||
if(data) delete[] data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
176
bsnes/nall/http.hpp
Executable file
176
bsnes/nall/http.hpp
Executable file
@@ -0,0 +1,176 @@
|
||||
#ifndef NALL_HTTP_HPP
|
||||
#define NALL_HTTP_HPP
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct http {
|
||||
string hostname;
|
||||
addrinfo *serverinfo;
|
||||
int serversocket;
|
||||
string header;
|
||||
|
||||
inline void download(const string &path, uint8_t *&data, unsigned &size) {
|
||||
data = 0;
|
||||
size = 0;
|
||||
|
||||
send({
|
||||
"GET ", path, " HTTP/1.1\r\n"
|
||||
"Host: ", hostname, "\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
});
|
||||
|
||||
header = downloadHeader();
|
||||
downloadContent(data, size);
|
||||
}
|
||||
|
||||
inline bool connect(string host, unsigned port) {
|
||||
hostname = host;
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
|
||||
if(status != 0) return false;
|
||||
|
||||
serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
|
||||
if(serversocket == -1) return false;
|
||||
|
||||
int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
|
||||
if(result == -1) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool send(const string &data) {
|
||||
return send((const uint8_t*)(const char*)data, data.length());
|
||||
}
|
||||
|
||||
inline bool send(const uint8_t *data, unsigned size) {
|
||||
while(size) {
|
||||
int length = ::send(serversocket, (const char*)data, size, 0);
|
||||
if(length == -1) return false;
|
||||
data += length;
|
||||
size -= length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline string downloadHeader() {
|
||||
string output;
|
||||
do {
|
||||
char buffer[2];
|
||||
int length = recv(serversocket, buffer, 1, 0);
|
||||
if(length <= 0) return output;
|
||||
buffer[1] = 0;
|
||||
output.append(buffer);
|
||||
} while(output.endswith("\r\n\r\n") == false);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline string downloadChunkLength() {
|
||||
string output;
|
||||
do {
|
||||
char buffer[2];
|
||||
int length = recv(serversocket, buffer, 1, 0);
|
||||
if(length <= 0) return output;
|
||||
buffer[1] = 0;
|
||||
output.append(buffer);
|
||||
} while(output.endswith("\r\n") == false);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline void downloadContent(uint8_t *&data, unsigned &size) {
|
||||
unsigned capacity = 0;
|
||||
|
||||
if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
|
||||
while(true) {
|
||||
unsigned length = hex(downloadChunkLength());
|
||||
if(length == 0) break;
|
||||
capacity += length;
|
||||
data = (uint8_t*)realloc(data, capacity);
|
||||
|
||||
char buffer[length];
|
||||
while(length) {
|
||||
int packetlength = recv(serversocket, buffer, length, 0);
|
||||
if(packetlength <= 0) break;
|
||||
memcpy(data + size, buffer, packetlength);
|
||||
size += packetlength;
|
||||
length -= packetlength;
|
||||
}
|
||||
}
|
||||
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
|
||||
unsigned length = decimal((const char*)header + position() + 16);
|
||||
while(length) {
|
||||
char buffer[256];
|
||||
int packetlength = recv(serversocket, buffer, min(256, length), 0);
|
||||
if(packetlength <= 0) break;
|
||||
capacity += packetlength;
|
||||
data = (uint8_t*)realloc(data, capacity);
|
||||
memcpy(data + size, buffer, packetlength);
|
||||
size += packetlength;
|
||||
length -= packetlength;
|
||||
}
|
||||
} else {
|
||||
while(true) {
|
||||
char buffer[256];
|
||||
int packetlength = recv(serversocket, buffer, 256, 0);
|
||||
if(packetlength <= 0) break;
|
||||
capacity += packetlength;
|
||||
data = (uint8_t*)realloc(data, capacity);
|
||||
memcpy(data + size, buffer, packetlength);
|
||||
size += packetlength;
|
||||
}
|
||||
}
|
||||
|
||||
data = (uint8_t*)realloc(data, capacity + 1);
|
||||
data[capacity] = 0;
|
||||
}
|
||||
|
||||
inline void disconnect() {
|
||||
close(serversocket);
|
||||
freeaddrinfo(serverinfo);
|
||||
serverinfo = 0;
|
||||
serversocket = -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
inline int close(int sock) {
|
||||
return closesocket(sock);
|
||||
}
|
||||
|
||||
inline http() {
|
||||
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
|
||||
WSADATA wsaData;
|
||||
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
close(sock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
358
bsnes/nall/inflate.hpp
Executable file
358
bsnes/nall/inflate.hpp
Executable file
@@ -0,0 +1,358 @@
|
||||
#ifndef NALL_INFLATE_HPP
|
||||
#define NALL_INFLATE_HPP
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
namespace nall {
|
||||
|
||||
namespace puff {
|
||||
inline int puff(
|
||||
unsigned char *dest, unsigned long *destlen,
|
||||
unsigned char *source, unsigned long *sourcelen
|
||||
);
|
||||
}
|
||||
|
||||
inline bool inflate(
|
||||
uint8_t *target, unsigned targetLength,
|
||||
const uint8_t *source, unsigned sourceLength
|
||||
) {
|
||||
unsigned long tl = targetLength, sl = sourceLength;
|
||||
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
namespace puff {
|
||||
|
||||
//zlib/contrib/puff.c
|
||||
//version 2.1*
|
||||
//author: Mark Adler
|
||||
//license: zlib
|
||||
//ported by: byuu
|
||||
|
||||
//* I have corrected a bug in fixed(), where it was accessing uninitialized
|
||||
// memory: calling construct() with lencode prior to initializing lencode.count
|
||||
|
||||
enum {
|
||||
MAXBITS = 15,
|
||||
MAXLCODES = 286,
|
||||
MAXDCODES = 30,
|
||||
FIXLCODES = 288,
|
||||
MAXCODES = MAXLCODES + MAXDCODES,
|
||||
};
|
||||
|
||||
struct state {
|
||||
unsigned char *out;
|
||||
unsigned long outlen;
|
||||
unsigned long outcnt;
|
||||
|
||||
unsigned char *in;
|
||||
unsigned long inlen;
|
||||
unsigned long incnt;
|
||||
int bitbuf;
|
||||
int bitcnt;
|
||||
|
||||
jmp_buf env;
|
||||
};
|
||||
|
||||
struct huffman {
|
||||
short *count;
|
||||
short *symbol;
|
||||
};
|
||||
|
||||
inline int bits(state *s, int need) {
|
||||
long val;
|
||||
|
||||
val = s->bitbuf;
|
||||
while(s->bitcnt < need) {
|
||||
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
||||
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
|
||||
s->bitcnt += 8;
|
||||
}
|
||||
|
||||
s->bitbuf = (int)(val >> need);
|
||||
s->bitcnt -= need;
|
||||
|
||||
return (int)(val & ((1L << need) - 1));
|
||||
}
|
||||
|
||||
inline int stored(state *s) {
|
||||
unsigned len;
|
||||
|
||||
s->bitbuf = 0;
|
||||
s->bitcnt = 0;
|
||||
|
||||
if(s->incnt + 4 > s->inlen) return 2;
|
||||
len = s->in[s->incnt++];
|
||||
len |= s->in[s->incnt++] << 8;
|
||||
if(s->in[s->incnt++] != (~len & 0xff) ||
|
||||
s->in[s->incnt++] != ((~len >> 8) & 0xff)
|
||||
) return 2;
|
||||
|
||||
if(s->incnt + len > s->inlen) return 2;
|
||||
if(s->out != 0) {
|
||||
if(s->outcnt + len > s->outlen) return 1;
|
||||
while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
|
||||
} else {
|
||||
s->outcnt += len;
|
||||
s->incnt += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int decode(state *s, huffman *h) {
|
||||
int len, code, first, count, index, bitbuf, left;
|
||||
short *next;
|
||||
|
||||
bitbuf = s->bitbuf;
|
||||
left = s->bitcnt;
|
||||
code = first = index = 0;
|
||||
len = 1;
|
||||
next = h->count + 1;
|
||||
while(true) {
|
||||
while(left--) {
|
||||
code |= bitbuf & 1;
|
||||
bitbuf >>= 1;
|
||||
count = *next++;
|
||||
if(code - count < first) {
|
||||
s->bitbuf = bitbuf;
|
||||
s->bitcnt = (s->bitcnt - len) & 7;
|
||||
return h->symbol[index + (code - first)];
|
||||
}
|
||||
index += count;
|
||||
first += count;
|
||||
first <<= 1;
|
||||
code <<= 1;
|
||||
len++;
|
||||
}
|
||||
left = (MAXBITS + 1) - len;
|
||||
if(left == 0) break;
|
||||
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
||||
bitbuf = s->in[s->incnt++];
|
||||
if(left > 8) left = 8;
|
||||
}
|
||||
|
||||
return -10;
|
||||
}
|
||||
|
||||
inline int construct(huffman *h, short *length, int n) {
|
||||
int symbol, len, left;
|
||||
short offs[MAXBITS + 1];
|
||||
|
||||
for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
|
||||
for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
|
||||
if(h->count[0] == n) return 0;
|
||||
|
||||
left = 1;
|
||||
for(len = 1; len <= MAXBITS; len++) {
|
||||
left <<= 1;
|
||||
left -= h->count[len];
|
||||
if(left < 0) return left;
|
||||
}
|
||||
|
||||
offs[1] = 0;
|
||||
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
|
||||
|
||||
for(symbol = 0; symbol < n; symbol++) {
|
||||
if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
inline int codes(state *s, huffman *lencode, huffman *distcode) {
|
||||
int symbol, len;
|
||||
unsigned dist;
|
||||
static const short lens[29] = {
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
||||
};
|
||||
static const short lext[29] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
||||
};
|
||||
static const short dists[30] = {
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577
|
||||
};
|
||||
static const short dext[30] = {
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
||||
12, 12, 13, 13
|
||||
};
|
||||
|
||||
do {
|
||||
symbol = decode(s, lencode);
|
||||
if(symbol < 0) return symbol;
|
||||
if(symbol < 256) {
|
||||
if(s->out != 0) {
|
||||
if(s->outcnt == s->outlen) return 1;
|
||||
s->out[s->outcnt] = symbol;
|
||||
}
|
||||
s->outcnt++;
|
||||
} else if(symbol > 256) {
|
||||
symbol -= 257;
|
||||
if(symbol >= 29) return -10;
|
||||
len = lens[symbol] + bits(s, lext[symbol]);
|
||||
|
||||
symbol = decode(s, distcode);
|
||||
if(symbol < 0) return symbol;
|
||||
dist = dists[symbol] + bits(s, dext[symbol]);
|
||||
#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
||||
if(dist > s->outcnt) return -11;
|
||||
#endif
|
||||
|
||||
if(s->out != 0) {
|
||||
if(s->outcnt + len > s->outlen) return 1;
|
||||
while(len--) {
|
||||
s->out[s->outcnt] =
|
||||
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
||||
dist > s->outcnt ? 0 :
|
||||
#endif
|
||||
s->out[s->outcnt - dist];
|
||||
s->outcnt++;
|
||||
}
|
||||
} else {
|
||||
s->outcnt += len;
|
||||
}
|
||||
}
|
||||
} while(symbol != 256);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int fixed(state *s) {
|
||||
static int virgin = 1;
|
||||
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
|
||||
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
||||
static huffman lencode, distcode;
|
||||
|
||||
if(virgin) {
|
||||
int symbol = 0;
|
||||
short lengths[FIXLCODES];
|
||||
|
||||
lencode.count = lencnt;
|
||||
lencode.symbol = lensym;
|
||||
distcode.count = distcnt;
|
||||
distcode.symbol = distsym;
|
||||
|
||||
for(; symbol < 144; symbol++) lengths[symbol] = 8;
|
||||
for(; symbol < 256; symbol++) lengths[symbol] = 9;
|
||||
for(; symbol < 280; symbol++) lengths[symbol] = 7;
|
||||
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
|
||||
construct(&lencode, lengths, FIXLCODES);
|
||||
|
||||
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
|
||||
construct(&distcode, lengths, MAXDCODES);
|
||||
|
||||
virgin = 0;
|
||||
}
|
||||
|
||||
return codes(s, &lencode, &distcode);
|
||||
}
|
||||
|
||||
inline int dynamic(state *s) {
|
||||
int nlen, ndist, ncode, index, err;
|
||||
short lengths[MAXCODES];
|
||||
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
|
||||
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
||||
huffman lencode, distcode;
|
||||
static const short order[19] = {
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||
};
|
||||
|
||||
lencode.count = lencnt;
|
||||
lencode.symbol = lensym;
|
||||
distcode.count = distcnt;
|
||||
distcode.symbol = distsym;
|
||||
|
||||
nlen = bits(s, 5) + 257;
|
||||
ndist = bits(s, 5) + 1;
|
||||
ncode = bits(s, 4) + 4;
|
||||
if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
|
||||
|
||||
for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
|
||||
for(; index < 19; index++) lengths[order[index]] = 0;
|
||||
|
||||
err = construct(&lencode, lengths, 19);
|
||||
if(err != 0) return -4;
|
||||
|
||||
index = 0;
|
||||
while(index < nlen + ndist) {
|
||||
int symbol, len;
|
||||
|
||||
symbol = decode(s, &lencode);
|
||||
if(symbol < 16) {
|
||||
lengths[index++] = symbol;
|
||||
} else {
|
||||
len = 0;
|
||||
if(symbol == 16) {
|
||||
if(index == 0) return -5;
|
||||
len = lengths[index - 1];
|
||||
symbol = 3 + bits(s, 2);
|
||||
} else if(symbol == 17) {
|
||||
symbol = 3 + bits(s, 3);
|
||||
} else {
|
||||
symbol = 11 + bits(s, 7);
|
||||
}
|
||||
if(index + symbol > nlen + ndist) return -6;
|
||||
while(symbol--) lengths[index++] = len;
|
||||
}
|
||||
}
|
||||
|
||||
if(lengths[256] == 0) return -9;
|
||||
|
||||
err = construct(&lencode, lengths, nlen);
|
||||
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
|
||||
|
||||
err = construct(&distcode, lengths + nlen, ndist);
|
||||
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
|
||||
|
||||
return codes(s, &lencode, &distcode);
|
||||
}
|
||||
|
||||
inline int puff(
|
||||
unsigned char *dest, unsigned long *destlen,
|
||||
unsigned char *source, unsigned long *sourcelen
|
||||
) {
|
||||
state s;
|
||||
int last, type, err;
|
||||
|
||||
s.out = dest;
|
||||
s.outlen = *destlen;
|
||||
s.outcnt = 0;
|
||||
|
||||
s.in = source;
|
||||
s.inlen = *sourcelen;
|
||||
s.incnt = 0;
|
||||
s.bitbuf = 0;
|
||||
s.bitcnt = 0;
|
||||
|
||||
if(setjmp(s.env) != 0) {
|
||||
err = 2;
|
||||
} else {
|
||||
do {
|
||||
last = bits(&s, 1);
|
||||
type = bits(&s, 2);
|
||||
err = type == 0 ? stored(&s)
|
||||
: type == 1 ? fixed(&s)
|
||||
: type == 2 ? dynamic(&s)
|
||||
: -1;
|
||||
if(err != 0) break;
|
||||
} while(!last);
|
||||
}
|
||||
|
||||
if(err <= 0) {
|
||||
*destlen = s.outcnt;
|
||||
*sourcelen = s.incnt;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -110,7 +110,7 @@ struct Keyboard {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string() << "KB" << ID << "::" << KeyboardScancodeName[index];
|
||||
return { "KB", ID, "::", KeyboardScancodeName[index] };
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
@@ -207,7 +207,7 @@ struct Mouse {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string() << "MS" << ID << "::" << MouseScancodeName[index];
|
||||
return { "MS", ID, "::", MouseScancodeName[index] };
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
@@ -330,7 +330,7 @@ struct Joypad {
|
||||
index = code - (Base + Size * i);
|
||||
}
|
||||
}
|
||||
return string() << "JP" << ID << "::" << JoypadScancodeName[index];
|
||||
return { "JP", ID, "::", JoypadScancodeName[index] };
|
||||
}
|
||||
|
||||
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
|
||||
|
60
bsnes/nall/intrinsics.hpp
Executable file
60
bsnes/nall/intrinsics.hpp
Executable file
@@ -0,0 +1,60 @@
|
||||
#ifndef NALL_INTRINSICS_HPP
|
||||
#define NALL_INTRINSICS_HPP
|
||||
|
||||
struct Intrinsics {
|
||||
enum class Compiler : unsigned { GCC, VisualC, Unknown };
|
||||
enum class Platform : unsigned { X, OSX, Windows, Unknown };
|
||||
enum class Endian : unsigned { LSB, MSB, Unknown };
|
||||
|
||||
static inline Compiler compiler();
|
||||
static inline Platform platform();
|
||||
static inline Endian endian();
|
||||
};
|
||||
|
||||
/* Compiler detection */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define COMPILER_GCC
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::GCC; }
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_VISUALC
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::VisualC; }
|
||||
#else
|
||||
#define COMPILER_UNKNOWN
|
||||
Intrinsics::Compiler Intrinsics::compiler() { return Intrinsics::Compiler::Unknown; }
|
||||
#endif
|
||||
|
||||
/* Platform detection */
|
||||
|
||||
#if defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define PLATFORM_X
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::X; }
|
||||
#elif defined(__APPLE__)
|
||||
#define PLATFORM_OSX
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::OSX; }
|
||||
#elif defined(_WIN32)
|
||||
#define PLATFORM_WINDOWS
|
||||
#define PLATFORM_WIN
|
||||
Intrinsics::Platform Intrinsics::platform() { return Intrinsics::Platform::Windows; }
|
||||
#else
|
||||
#define PLATFORM_UNKNOWN
|
||||
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||
#define ENDIAN_LSB
|
||||
#define ARCH_LSB
|
||||
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::LSB; }
|
||||
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
||||
#define ENDIAN_MSB
|
||||
#define ARCH_MSB
|
||||
Intrinsics::Endian Intrinsics::endian() { return Intrinsics::Endian::MSB; }
|
||||
#else
|
||||
#define ENDIAN_UNKNOWN
|
||||
#define ARCH_UNKNOWN
|
||||
Intrinsics::Endian Intrinsics::endia() { return Intrinsics::Endian::Unknown; }
|
||||
#endif
|
||||
|
||||
#endif
|
110
bsnes/nall/ips.hpp
Executable file
110
bsnes/nall/ips.hpp
Executable file
@@ -0,0 +1,110 @@
|
||||
#ifndef NALL_IPS_HPP
|
||||
#define NALL_IPS_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct ips {
|
||||
inline bool apply();
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline void modify(const uint8_t *data, unsigned size);
|
||||
inline bool source(const string &filename);
|
||||
inline bool modify(const string &filename);
|
||||
inline ips();
|
||||
inline ~ips();
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
const uint8_t *modifyData;
|
||||
unsigned modifySize;
|
||||
};
|
||||
|
||||
bool ips::apply() {
|
||||
if(modifySize < 8) return false;
|
||||
if(modifyData[0] != 'P') return false;
|
||||
if(modifyData[1] != 'A') return false;
|
||||
if(modifyData[2] != 'T') return false;
|
||||
if(modifyData[3] != 'C') return false;
|
||||
if(modifyData[4] != 'H') return false;
|
||||
|
||||
if(data) delete[] data;
|
||||
data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
|
||||
size = sourceSize;
|
||||
memcpy(data, sourceData, sourceSize);
|
||||
unsigned offset = 5;
|
||||
|
||||
while(true) {
|
||||
unsigned address, length;
|
||||
|
||||
if(offset > modifySize - 3) break;
|
||||
address = modifyData[offset++] << 16;
|
||||
address |= modifyData[offset++] << 8;
|
||||
address |= modifyData[offset++] << 0;
|
||||
|
||||
if(address == 0x454f46) { //EOF
|
||||
if(offset == modifySize) return true;
|
||||
if(offset == modifySize - 3) {
|
||||
size = modifyData[offset++] << 16;
|
||||
size |= modifyData[offset++] << 8;
|
||||
size |= modifyData[offset++] << 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(offset > modifySize - 2) break;
|
||||
length = modifyData[offset++] << 8;
|
||||
length |= modifyData[offset++] << 0;
|
||||
|
||||
if(length) { //Copy
|
||||
if(offset > modifySize - length) break;
|
||||
while(length--) data[address++] = modifyData[offset++];
|
||||
} else { //RLE
|
||||
if(offset > modifySize - 3) break;
|
||||
length = modifyData[offset++] << 8;
|
||||
length |= modifyData[offset++] << 0;
|
||||
if(length == 0) break; //illegal
|
||||
while(length--) data[address++] = modifyData[offset];
|
||||
offset++;
|
||||
}
|
||||
|
||||
size = max(size, address);
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ips::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data, sourceSize = size;
|
||||
}
|
||||
|
||||
void ips::modify(const uint8_t *data, unsigned size) {
|
||||
modifyData = data, modifySize = size;
|
||||
}
|
||||
|
||||
bool ips::source(const string &filename) {
|
||||
return file::read(filename, sourceData, sourceSize);
|
||||
}
|
||||
|
||||
bool ips::modify(const string &filename) {
|
||||
return file::read(filename, modifyData, modifySize);
|
||||
}
|
||||
|
||||
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
|
||||
}
|
||||
|
||||
ips::~ips() {
|
||||
if(data) delete[] data;
|
||||
if(sourceData) delete[] sourceData;
|
||||
if(modifyData) delete[] modifyData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,81 +1,165 @@
|
||||
#ifndef NALL_LZSS_HPP
|
||||
#define NALL_LZSS_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/filemap.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
class lzss {
|
||||
public:
|
||||
static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) {
|
||||
output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9];
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(i < inlength) {
|
||||
unsigned flagoffset = o++;
|
||||
uint8_t flag = 0x00;
|
||||
//19:5 pulldown
|
||||
//8:1 marker: d7-d0
|
||||
//length: { 4 - 35 }, offset: { 1 - 0x80000 }
|
||||
//4-byte file size header
|
||||
//little-endian encoding
|
||||
struct lzss {
|
||||
inline void source(const uint8_t *data, unsigned size);
|
||||
inline bool source(const string &filename);
|
||||
inline unsigned size() const;
|
||||
inline bool compress(const string &filename);
|
||||
inline bool decompress(uint8_t *targetData, unsigned targetSize);
|
||||
inline bool decompress(const string &filename);
|
||||
|
||||
for(unsigned b = 0; b < 8 && i < inlength; b++) {
|
||||
unsigned longest = 0, pointer;
|
||||
for(unsigned index = 1; index < 4096; index++) {
|
||||
unsigned count = 0;
|
||||
while(true) {
|
||||
if(count >= 15 + 3) break; //verify pattern match is not longer than max length
|
||||
if(i + count >= inlength) break; //verify pattern match does not read past end of input
|
||||
if(i + count < index) break; //verify read is not before start of input
|
||||
if(input[i + count] != input[i + count - index]) break; //verify pattern still matches
|
||||
count++;
|
||||
}
|
||||
protected:
|
||||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
} *tree[65536];
|
||||
|
||||
if(count > longest) {
|
||||
longest = count;
|
||||
pointer = index;
|
||||
}
|
||||
}
|
||||
filemap sourceFile;
|
||||
const uint8_t *sourceData;
|
||||
unsigned sourceSize;
|
||||
|
||||
if(longest < 3) output[o++] = input[i++];
|
||||
else {
|
||||
flag |= 1 << b;
|
||||
uint16_t x = ((longest - 3) << 12) + pointer;
|
||||
output[o++] = x;
|
||||
output[o++] = x >> 8;
|
||||
i += longest;
|
||||
}
|
||||
public:
|
||||
inline lzss() : sourceData(nullptr), sourceSize(0) {}
|
||||
};
|
||||
|
||||
void lzss::source(const uint8_t *data, unsigned size) {
|
||||
sourceData = data;
|
||||
sourceSize = size;
|
||||
}
|
||||
|
||||
bool lzss::source(const string &filename) {
|
||||
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||
sourceData = sourceFile.data();
|
||||
sourceSize = sourceFile.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned lzss::size() const {
|
||||
unsigned size = 0;
|
||||
if(sourceSize < 4) return size;
|
||||
for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
|
||||
return size;
|
||||
}
|
||||
|
||||
bool lzss::compress(const string &filename) {
|
||||
file targetFile;
|
||||
if(targetFile.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
|
||||
for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
|
||||
|
||||
uint8_t buffer[25];
|
||||
unsigned sourceOffset = 0;
|
||||
|
||||
while(sourceOffset < sourceSize) {
|
||||
uint8_t mask = 0x00;
|
||||
unsigned bufferOffset = 1;
|
||||
|
||||
for(unsigned iteration = 0; iteration < 8; iteration++) {
|
||||
if(sourceOffset >= sourceSize) break;
|
||||
|
||||
uint16_t symbol = sourceData[sourceOffset + 0];
|
||||
if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
|
||||
Node *node = tree[symbol];
|
||||
unsigned maxLength = 0, maxOffset = 0;
|
||||
|
||||
while(node) {
|
||||
if(node->offset < sourceOffset - 0x80000) {
|
||||
//out-of-range: all subsequent nodes will also be, so free up their memory
|
||||
if(node->next) { delete node->next; node->next = 0; }
|
||||
break;
|
||||
}
|
||||
|
||||
output[flagoffset] = flag;
|
||||
unsigned length = 0, x = sourceOffset, y = node->offset;
|
||||
while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
|
||||
if(length > maxLength) maxLength = length, maxOffset = node->offset;
|
||||
if(length == 35) break;
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
outlength = o;
|
||||
return true;
|
||||
}
|
||||
//attach current symbol to top of tree for subsequent searches
|
||||
node = new Node;
|
||||
node->offset = sourceOffset;
|
||||
node->next = tree[symbol];
|
||||
tree[symbol] = node;
|
||||
|
||||
static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) {
|
||||
output = new(zeromemory) uint8_t[length];
|
||||
|
||||
unsigned i = 0, o = 0;
|
||||
while(o < length) {
|
||||
uint8_t flag = input[i++];
|
||||
|
||||
for(unsigned b = 0; b < 8 && o < length; b++) {
|
||||
if(!(flag & (1 << b))) output[o++] = input[i++];
|
||||
else {
|
||||
uint16_t offset = input[i++];
|
||||
offset += input[i++] << 8;
|
||||
uint16_t lookuplength = (offset >> 12) + 3;
|
||||
offset &= 4095;
|
||||
for(unsigned index = 0; index < lookuplength && o + index < length; index++) {
|
||||
output[o + index] = output[o + index - offset];
|
||||
}
|
||||
o += lookuplength;
|
||||
}
|
||||
}
|
||||
if(maxLength < 4) {
|
||||
buffer[bufferOffset++] = sourceData[sourceOffset++];
|
||||
} else {
|
||||
unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
|
||||
for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
|
||||
mask |= 0x80 >> iteration;
|
||||
sourceOffset += maxLength;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
buffer[0] = mask;
|
||||
targetFile.write(buffer, bufferOffset);
|
||||
}
|
||||
|
||||
sourceFile.close();
|
||||
targetFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
|
||||
if(targetSize < size()) return false;
|
||||
|
||||
unsigned sourceOffset = 4, targetOffset = 0;
|
||||
while(sourceOffset < sourceSize) {
|
||||
uint8_t mask = sourceData[sourceOffset++];
|
||||
|
||||
for(unsigned iteration = 0; iteration < 8; iteration++) {
|
||||
if(sourceOffset >= sourceSize) break;
|
||||
|
||||
if((mask & (0x80 >> iteration)) == 0) {
|
||||
targetData[targetOffset++] = sourceData[sourceOffset++];
|
||||
} else {
|
||||
unsigned code = 0;
|
||||
for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
|
||||
unsigned length = (code >> 19) + 4;
|
||||
unsigned offset = targetOffset - 1 - (code & 0x7ffff);
|
||||
while(length--) targetData[targetOffset++] = targetData[offset++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool lzss::decompress(const string &filename) {
|
||||
if(sourceSize < 4) return false;
|
||||
unsigned targetSize = size();
|
||||
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
fp.truncate(targetSize);
|
||||
fp.close();
|
||||
|
||||
filemap targetFile;
|
||||
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
|
||||
uint8_t *targetData = targetFile.data();
|
||||
|
||||
bool result = decompress(targetData, targetSize);
|
||||
sourceFile.close();
|
||||
targetFile.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,7 +1,12 @@
|
||||
#ifndef NALL_PLATFORM_HPP
|
||||
#define NALL_PLATFORM_HPP
|
||||
|
||||
#include <nall/utf8.hpp>
|
||||
#if defined(_WIN32)
|
||||
//minimum version needed for _wstat64, etc
|
||||
#undef __MSVCRT_VERSION__
|
||||
#define __MSVCRT_VERSION__ 0x0601
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
||||
//=========================
|
||||
//standard platform headers
|
||||
@@ -18,16 +23,19 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <shlobj.h>
|
||||
#include <wchar.h>
|
||||
#undef interface
|
||||
#define dllexport __declspec(dllexport)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#define dllexport
|
||||
#endif
|
||||
|
||||
@@ -53,11 +61,11 @@
|
||||
#if defined(_WIN32)
|
||||
#define getcwd _getcwd
|
||||
#define ftruncate _chsize
|
||||
#define putenv _putenv
|
||||
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
|
||||
#define putenv _putenv
|
||||
#define rmdir _rmdir
|
||||
#define vsnprintf _vsnprintf
|
||||
#define usleep(n) Sleep(n / 1000)
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
||||
//================
|
||||
@@ -87,6 +95,7 @@
|
||||
wchar_t fn[_MAX_PATH] = L"";
|
||||
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
|
||||
strcpy(resolvedname, nall::utf8_t(fn));
|
||||
for(unsigned n = 0; resolvedname[n]; n++) if(resolvedname[n] == '\\') resolvedname[n] = '/';
|
||||
return resolvedname;
|
||||
}
|
||||
|
||||
@@ -94,6 +103,9 @@
|
||||
wchar_t fp[_MAX_PATH] = L"";
|
||||
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -101,6 +113,9 @@
|
||||
wchar_t fp[_MAX_PATH] = L"";
|
||||
_wgetcwd(fp, _MAX_PATH);
|
||||
strcpy(path, nall::utf8_t(fp));
|
||||
for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
@@ -110,11 +125,16 @@
|
||||
*path = 0;
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) strcpy(path, userinfo->pw_dir);
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
|
||||
inline char *getcwd(char *path) {
|
||||
return getcwd(path, PATH_MAX);
|
||||
auto unused = getcwd(path, PATH_MAX);
|
||||
unsigned length = strlen(path);
|
||||
if(path[length] != '/') strcpy(path + length, "/");
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
426
bsnes/nall/png.hpp
Executable file
426
bsnes/nall/png.hpp
Executable file
@@ -0,0 +1,426 @@
|
||||
#ifndef NALL_PNG_HPP
|
||||
#define NALL_PNG_HPP
|
||||
|
||||
//PNG image decoder
|
||||
//author: byuu
|
||||
|
||||
#include <nall/inflate.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct png {
|
||||
uint32_t *data;
|
||||
unsigned size;
|
||||
|
||||
struct Info {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned bitDepth;
|
||||
unsigned colorType;
|
||||
unsigned compressionMethod;
|
||||
unsigned filterType;
|
||||
unsigned interlaceMethod;
|
||||
|
||||
unsigned bytesPerPixel;
|
||||
unsigned pitch;
|
||||
|
||||
uint8_t palette[256][3];
|
||||
} info;
|
||||
|
||||
uint8_t *rawData;
|
||||
unsigned rawSize;
|
||||
|
||||
inline bool decode(const string &filename);
|
||||
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
|
||||
inline void transform();
|
||||
inline void alphaTransform(uint32_t rgb = 0xffffff);
|
||||
inline png();
|
||||
inline ~png();
|
||||
|
||||
protected:
|
||||
enum class FourCC : unsigned {
|
||||
IHDR = 0x49484452,
|
||||
PLTE = 0x504c5445,
|
||||
IDAT = 0x49444154,
|
||||
IEND = 0x49454e44,
|
||||
};
|
||||
|
||||
unsigned bitpos;
|
||||
|
||||
inline unsigned interlace(unsigned pass, unsigned index);
|
||||
inline unsigned inflateSize();
|
||||
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
|
||||
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
|
||||
inline unsigned read(const uint8_t *data, unsigned length);
|
||||
inline unsigned decode(const uint8_t *&data);
|
||||
inline unsigned readbits(const uint8_t *&data);
|
||||
inline unsigned scale(unsigned n);
|
||||
};
|
||||
|
||||
bool png::decode(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) return false;
|
||||
bool result = decode(data, size);
|
||||
delete[] data;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
||||
if(sourceSize < 8) return false;
|
||||
if(read(sourceData + 0, 4) != 0x89504e47) return false;
|
||||
if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
|
||||
|
||||
uint8_t *compressedData = 0;
|
||||
unsigned compressedSize = 0;
|
||||
|
||||
unsigned offset = 8;
|
||||
while(offset < sourceSize) {
|
||||
unsigned length = read(sourceData + offset + 0, 4);
|
||||
unsigned fourCC = read(sourceData + offset + 4, 4);
|
||||
unsigned checksum = read(sourceData + offset + 8 + length, 4);
|
||||
|
||||
if(fourCC == (unsigned)FourCC::IHDR) {
|
||||
info.width = read(sourceData + offset + 8, 4);
|
||||
info.height = read(sourceData + offset + 12, 4);
|
||||
info.bitDepth = read(sourceData + offset + 16, 1);
|
||||
info.colorType = read(sourceData + offset + 17, 1);
|
||||
info.compressionMethod = read(sourceData + offset + 18, 1);
|
||||
info.filterType = read(sourceData + offset + 19, 1);
|
||||
info.interlaceMethod = read(sourceData + offset + 20, 1);
|
||||
|
||||
if(info.bitDepth == 0 || info.bitDepth > 16) return false;
|
||||
if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
|
||||
if(info.compressionMethod != 0) return false;
|
||||
if(info.filterType != 0) return false;
|
||||
if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
|
||||
|
||||
switch(info.colorType) {
|
||||
case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
|
||||
case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
|
||||
case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
|
||||
case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
|
||||
case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
|
||||
default: return false;
|
||||
}
|
||||
|
||||
if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
|
||||
if(info.bitDepth != 8 && info.bitDepth != 16) return false;
|
||||
if(info.colorType == 3 && info.bitDepth == 16) return false;
|
||||
|
||||
info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
|
||||
info.pitch = (int)info.width * info.bytesPerPixel;
|
||||
}
|
||||
|
||||
if(fourCC == (unsigned)FourCC::PLTE) {
|
||||
if(length % 3) return false;
|
||||
for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
|
||||
info.palette[n][0] = sourceData[p++];
|
||||
info.palette[n][1] = sourceData[p++];
|
||||
info.palette[n][2] = sourceData[p++];
|
||||
}
|
||||
}
|
||||
|
||||
if(fourCC == (unsigned)FourCC::IDAT) {
|
||||
compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
|
||||
memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
|
||||
compressedSize += length;
|
||||
}
|
||||
|
||||
if(fourCC == (unsigned)FourCC::IEND) {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += 4 + 4 + length + 4;
|
||||
}
|
||||
|
||||
unsigned interlacedSize = inflateSize();
|
||||
uint8_t *interlacedData = new uint8_t[interlacedSize];
|
||||
|
||||
bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
|
||||
delete[] compressedData;
|
||||
|
||||
if(result == false) {
|
||||
delete[] interlacedData;
|
||||
return false;
|
||||
}
|
||||
|
||||
rawSize = info.width * info.height * info.bytesPerPixel;
|
||||
rawData = new uint8_t[rawSize];
|
||||
|
||||
if(info.interlaceMethod == 0) {
|
||||
if(filter(rawData, interlacedData, info.width, info.height) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const uint8_t *passData = interlacedData;
|
||||
for(unsigned pass = 0; pass < 7; pass++) {
|
||||
if(deinterlace(passData, pass) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] interlacedData;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned png::interlace(unsigned pass, unsigned index) {
|
||||
static const unsigned data[7][4] = {
|
||||
//x-distance, y-distance, x-origin, y-origin
|
||||
{ 8, 8, 0, 0 },
|
||||
{ 8, 8, 4, 0 },
|
||||
{ 4, 8, 0, 4 },
|
||||
{ 4, 4, 2, 0 },
|
||||
{ 2, 4, 0, 2 },
|
||||
{ 2, 2, 1, 0 },
|
||||
{ 1, 2, 0, 1 },
|
||||
};
|
||||
return data[pass][index];
|
||||
}
|
||||
|
||||
unsigned png::inflateSize() {
|
||||
if(info.interlaceMethod == 0) {
|
||||
return info.width * info.height * info.bytesPerPixel + info.height;
|
||||
}
|
||||
|
||||
unsigned size = 0;
|
||||
for(unsigned pass = 0; pass < 7; pass++) {
|
||||
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||
if(width == 0 || height == 0) continue;
|
||||
size += width * height * info.bytesPerPixel + height;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
|
||||
unsigned xd = interlace(pass, 0), yd = interlace(pass, 1);
|
||||
unsigned xo = interlace(pass, 2), yo = interlace(pass, 3);
|
||||
unsigned width = (info.width + (xd - xo - 1)) / xd;
|
||||
unsigned height = (info.height + (yd - yo - 1)) / yd;
|
||||
if(width == 0 || height == 0) return true;
|
||||
|
||||
unsigned outputSize = width * height * info.bytesPerPixel;
|
||||
uint8_t *outputData = new uint8_t[outputSize];
|
||||
bool result = filter(outputData, inputData, width, height);
|
||||
|
||||
const uint8_t *rd = outputData;
|
||||
for(unsigned y = yo; y < info.height; y += yd) {
|
||||
uint8_t *wr = rawData + y * info.pitch;
|
||||
for(unsigned x = xo; x < info.width; x += xd) {
|
||||
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
|
||||
wr[x * info.bytesPerPixel + b] = *rd++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputData += outputSize + height;
|
||||
delete[] outputData;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
|
||||
uint8_t *wr = outputData;
|
||||
const uint8_t *rd = inputData;
|
||||
int bpp = info.bytesPerPixel, pitch = width * bpp;
|
||||
for(int y = 0; y < height; y++) {
|
||||
uint8_t filter = *rd++;
|
||||
|
||||
switch(filter) {
|
||||
case 0x00: //None
|
||||
for(int x = 0; x < pitch; x++) {
|
||||
wr[x] = rd[x];
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01: //Subtract
|
||||
for(int x = 0; x < pitch; x++) {
|
||||
wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02: //Above
|
||||
for(int x = 0; x < pitch; x++) {
|
||||
wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03: //Average
|
||||
for(int x = 0; x < pitch; x++) {
|
||||
short a = x - bpp < 0 ? 0 : wr[x - bpp];
|
||||
short b = y - 1 < 0 ? 0 : wr[x - pitch];
|
||||
|
||||
wr[x] = rd[x] + (uint8_t)((a + b) / 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: //Paeth
|
||||
for(int x = 0; x < pitch; x++) {
|
||||
short a = x - bpp < 0 ? 0 : wr[x - bpp];
|
||||
short b = y - 1 < 0 ? 0 : wr[x - pitch];
|
||||
short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
|
||||
|
||||
short p = a + b - c;
|
||||
short pa = p > a ? p - a : a - p;
|
||||
short pb = p > b ? p - b : b - p;
|
||||
short pc = p > c ? p - c : c - p;
|
||||
|
||||
uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
|
||||
|
||||
wr[x] = rd[x] + paeth;
|
||||
}
|
||||
break;
|
||||
|
||||
default: //Invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
rd += pitch;
|
||||
wr += pitch;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned png::read(const uint8_t *data, unsigned length) {
|
||||
unsigned result = 0;
|
||||
while(length--) result = (result << 8) | (*data++);
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned png::decode(const uint8_t *&data) {
|
||||
unsigned p, r, g, b, a;
|
||||
|
||||
switch(info.colorType) {
|
||||
case 0: //L
|
||||
r = g = b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 2: //R,G,B
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 3: //P
|
||||
p = readbits(data);
|
||||
r = info.palette[p][0];
|
||||
g = info.palette[p][1];
|
||||
b = info.palette[p][2];
|
||||
a = 0xff;
|
||||
break;
|
||||
case 4: //L,A
|
||||
r = g = b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
case 6: //R,G,B,A
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
}
|
||||
|
||||
return (a << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
|
||||
unsigned png::readbits(const uint8_t *&data) {
|
||||
unsigned result = 0;
|
||||
switch(info.bitDepth) {
|
||||
case 1:
|
||||
result = (*data >> bitpos) & 1;
|
||||
bitpos++;
|
||||
if(bitpos == 8) { data++; bitpos = 0; }
|
||||
break;
|
||||
case 2:
|
||||
result = (*data >> bitpos) & 3;
|
||||
bitpos += 2;
|
||||
if(bitpos == 8) { data++; bitpos = 0; }
|
||||
break;
|
||||
case 4:
|
||||
result = (*data >> bitpos) & 15;
|
||||
bitpos += 4;
|
||||
if(bitpos == 8) { data++; bitpos = 0; }
|
||||
break;
|
||||
case 8:
|
||||
result = *data++;
|
||||
break;
|
||||
case 16:
|
||||
result = (data[0] << 8) | (data[1] << 0);
|
||||
data += 2;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned png::scale(unsigned n) {
|
||||
switch(info.bitDepth) {
|
||||
case 1: return n ? 0xff : 0x00;
|
||||
case 2: return n * 0x55;
|
||||
case 4: return n * 0x11;
|
||||
case 8: return n;
|
||||
case 16: return n >> 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void png::transform() {
|
||||
if(data) delete[] data;
|
||||
data = new uint32_t[info.width * info.height];
|
||||
|
||||
bitpos = 0;
|
||||
const uint8_t *rd = rawData;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
uint32_t *wr = data + y * info.width;
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
wr[x] = decode(rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void png::alphaTransform(uint32_t rgb) {
|
||||
transform();
|
||||
|
||||
uint8_t ir = rgb >> 16;
|
||||
uint8_t ig = rgb >> 8;
|
||||
uint8_t ib = rgb >> 0;
|
||||
|
||||
uint32_t *p = data;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
uint32_t pixel = *p;
|
||||
uint8_t a = pixel >> 24;
|
||||
uint8_t r = pixel >> 16;
|
||||
uint8_t g = pixel >> 8;
|
||||
uint8_t b = pixel >> 0;
|
||||
|
||||
r = (r * a) + (ir * (255 - a)) >> 8;
|
||||
g = (g * a) + (ig * (255 - a)) >> 8;
|
||||
b = (b * a) + (ib * (255 - a)) >> 8;
|
||||
|
||||
*p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
png::png() : data(nullptr), rawData(nullptr) {
|
||||
}
|
||||
|
||||
png::~png() {
|
||||
if(data) delete[] data;
|
||||
if(rawData) delete[] rawData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -12,7 +12,7 @@ namespace nall {
|
||||
//priority queue implementation using binary min-heap array;
|
||||
//does not require normalize() function.
|
||||
//O(1) find (tick)
|
||||
//O(log n) insert (enqueue)
|
||||
//O(log n) append (enqueue)
|
||||
//O(log n) remove (dequeue)
|
||||
template<typename type_t> class priority_queue {
|
||||
public:
|
||||
|
@@ -22,7 +22,7 @@
|
||||
// readwrite<int> y;
|
||||
//};
|
||||
|
||||
//return types are const T& (byref) instead fo T (byval) to avoid major speed
|
||||
//return types are const T& (byref) instead of T (byval) to avoid major speed
|
||||
//penalties for objects with expensive copy constructors
|
||||
|
||||
//operator-> provides access to underlying object type:
|
||||
|
@@ -8,12 +8,20 @@ namespace nall {
|
||||
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
struct random_cyclic {
|
||||
unsigned seed;
|
||||
inline unsigned operator()() {
|
||||
return seed = (seed >> 1) ^ (((seed & 1) - 1) & 0xedb88320);
|
||||
struct random_lfsr {
|
||||
inline void seed(unsigned seed__) {
|
||||
seed_ = seed__;
|
||||
}
|
||||
random_cyclic() : seed(0) {}
|
||||
|
||||
inline unsigned operator()() {
|
||||
return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
random_lfsr() : seed_(0) {
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned seed_;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
#ifndef NALL_REFERENCE_ARRAY_HPP
|
||||
#define NALL_REFERENCE_ARRAY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/concept.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename T> struct reference_array {
|
||||
@@ -18,7 +18,7 @@ namespace nall {
|
||||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = 0;
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
@@ -36,13 +36,29 @@ namespace nall {
|
||||
buffersize = newsize;
|
||||
}
|
||||
|
||||
void append(const T data) {
|
||||
bool append(const T data) {
|
||||
for(unsigned index = 0; index < buffersize; index++) {
|
||||
if(pool[index] == &data) return false;
|
||||
}
|
||||
|
||||
unsigned index = buffersize++;
|
||||
if(index >= poolsize) resize(index + 1);
|
||||
pool[index] = &data;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename... Args> reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) {
|
||||
bool remove(const T data) {
|
||||
for(unsigned index = 0; index < buffersize; index++) {
|
||||
if(pool[index] == &data) {
|
||||
for(unsigned i = index; i < buffersize - 1; i++) pool[i] = pool[i + 1];
|
||||
resize(buffersize - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args> reference_array(Args&... args) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
construct(args...);
|
||||
}
|
||||
|
||||
@@ -64,7 +80,7 @@ namespace nall {
|
||||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
buffersize = source.buffersize;
|
||||
source.pool = 0;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
@@ -79,6 +95,22 @@ namespace nall {
|
||||
return *pool[index];
|
||||
}
|
||||
|
||||
//iteration
|
||||
struct iterator {
|
||||
bool operator!=(const iterator &source) const { return index != source.index; }
|
||||
T& operator*() { return array.operator[](index); }
|
||||
iterator& operator++() { index++; return *this; }
|
||||
iterator(const reference_array &array, unsigned index) : array(array), index(index) {}
|
||||
private:
|
||||
const reference_array &array;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(*this, 0); }
|
||||
iterator end() { return iterator(*this, buffersize); }
|
||||
const iterator begin() const { return iterator(*this, 0); }
|
||||
const iterator end() const { return iterator(*this, buffersize); }
|
||||
|
||||
private:
|
||||
void construct() {
|
||||
}
|
||||
@@ -96,8 +128,6 @@ namespace nall {
|
||||
construct(args...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct has_size<reference_array<T>> { enum { value = true }; };
|
||||
}
|
||||
|
||||
#endif
|
||||
|
61
bsnes/nall/resource.hpp
Executable file
61
bsnes/nall/resource.hpp
Executable file
@@ -0,0 +1,61 @@
|
||||
#ifndef NALL_RESOURCE_HPP
|
||||
#define NALL_RESOURCE_HPP
|
||||
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/zip.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct resource {
|
||||
//create resource with "zip -9 resource.zip resource"
|
||||
static bool encode(const char *outputFilename, const char *inputFilename) {
|
||||
file fp;
|
||||
if(fp.open(inputFilename, file::mode::read) == false) return false;
|
||||
unsigned size = fp.size();
|
||||
uint8_t *data = new uint8_t[size];
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
|
||||
fp.open(outputFilename, file::mode::write);
|
||||
fp.print("static const uint8_t data[", size, "] = {\n");
|
||||
uint8_t *p = data;
|
||||
while(size) {
|
||||
fp.print(" ");
|
||||
for(unsigned n = 0; n < 32 && size; n++, size--) {
|
||||
fp.print((unsigned)*p++, ",");
|
||||
}
|
||||
fp.print("\n");
|
||||
}
|
||||
fp.print("};\n");
|
||||
fp.close();
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
//extract first file from ZIP archive
|
||||
bool decode(const uint8_t *cdata, unsigned csize) {
|
||||
if(data) delete[] data;
|
||||
|
||||
zip archive;
|
||||
if(archive.open(cdata, csize) == false) return false;
|
||||
if(archive.file.size() == 0) return false;
|
||||
bool result = archive.extract(archive.file[0], data, size);
|
||||
archive.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
resource() : data(0), size(0) {
|
||||
}
|
||||
|
||||
~resource() {
|
||||
if(data) delete[] data;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -3,6 +3,8 @@
|
||||
|
||||
//author: vladitx
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
#define PTR(t, a) ((t*)(a))
|
||||
|
||||
@@ -49,7 +51,7 @@ namespace nall {
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
void sha256_init(sha256_ctx *p) {
|
||||
inline void sha256_init(sha256_ctx *p) {
|
||||
memset(p, 0, sizeof(sha256_ctx));
|
||||
memcpy(p->h, T_H, sizeof(T_H));
|
||||
}
|
||||
@@ -90,7 +92,7 @@ namespace nall {
|
||||
p->inlen = 0;
|
||||
}
|
||||
|
||||
void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
|
||||
inline void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
|
||||
unsigned l;
|
||||
p->len += len;
|
||||
|
||||
@@ -107,7 +109,7 @@ namespace nall {
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(sha256_ctx *p) {
|
||||
inline void sha256_final(sha256_ctx *p) {
|
||||
uint64_t len;
|
||||
p->in[p->inlen++] = 0x80;
|
||||
|
||||
@@ -124,7 +126,7 @@ namespace nall {
|
||||
sha256_block(p);
|
||||
}
|
||||
|
||||
void sha256_hash(sha256_ctx *p, uint8_t *s) {
|
||||
inline void sha256_hash(sha256_ctx *p, uint8_t *s) {
|
||||
uint32_t *t = (uint32_t*)s;
|
||||
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
|
||||
}
|
||||
|
@@ -3,10 +3,10 @@
|
||||
|
||||
namespace nall {
|
||||
|
||||
class SNESCartridge {
|
||||
class SnesCartridge {
|
||||
public:
|
||||
string xmlMemoryMap;
|
||||
inline SNESCartridge(const uint8_t *data, unsigned size);
|
||||
string markup;
|
||||
inline SnesCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
@@ -105,432 +105,346 @@ public:
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
#define T "\t"
|
||||
|
||||
SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
|
||||
read_header(data, size);
|
||||
|
||||
string xml = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
string xml;
|
||||
markup = "";
|
||||
|
||||
if(type == TypeBsx) {
|
||||
xml << "<cartridge/>";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
markup.append("cartridge");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeSufamiTurbo) {
|
||||
xml << "<cartridge/>";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
markup.append("cartridge");
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == TypeGameBoy) {
|
||||
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
|
||||
markup.append("cartridge rtc=", gameboy_has_rtc(data, size), "\n");
|
||||
if(gameboy_ram_size(data, size) > 0) {
|
||||
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
|
||||
markup.append(T "ram size=0x", hex(gameboy_ram_size(data, size)), "\n");
|
||||
}
|
||||
xml << "</cartridge>\n";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
xml << "<cartridge";
|
||||
if(region == NTSC) {
|
||||
xml << " region='NTSC'";
|
||||
} else {
|
||||
xml << " region='PAL'";
|
||||
}
|
||||
xml << ">\n";
|
||||
markup.append("cartridge region=", region == NTSC ? "NTSC\n" : "PAL\n");
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <icd2 revision='1'>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </icd2>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
|
||||
markup.append(T "icd2 revision=1\n");
|
||||
markup.append(T T "map address=00-3f:6000-7fff\n");
|
||||
markup.append(T T "map address=80-bf:6000-7fff\n");
|
||||
} else if(type == TypeSuperGameBoy2Bios) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <icd2 revision='2'>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </icd2>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
|
||||
markup.append(T "icd2 revision=1\n");
|
||||
markup.append(T T "map address=00-3f:6000-7fff\n");
|
||||
markup.append(T T "map address=80-bf:6000-7fff\n");
|
||||
} else if(has_cx4) {
|
||||
markup.append(T "hitachidsp model=HG51B169 frequency=20000000 firmware=cx4.bin sha256=ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18\n");
|
||||
markup.append(T T "rom\n");
|
||||
markup.append(T T T "map mode=linear address=00-7f:8000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=80-ff:8000-ffff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:6000-7fff\n");
|
||||
markup.append(T T T "map address=80-bf:6000-7fff\n");
|
||||
} else if(has_spc7110) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-cf:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=shadow address=00-0f:8000-ffff\n");
|
||||
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=c0-cf:0000-ffff\n");
|
||||
|
||||
xml << " <spc7110>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << hex(size - 0x100000) << "'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:4800-483f'/>\n";
|
||||
xml << " <map address='80-bf:4800-483f'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
markup.append(T "spc7110\n");
|
||||
markup.append(T T "mcu\n");
|
||||
markup.append(T T T "map address=d0-ff:0000-ffff offset=0x100000 size=0x", hex(size - 0x100000), "\n");
|
||||
markup.append(T T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T T "map mode=linear address=00:6000-7fff\n");
|
||||
markup.append(T T T "map mode=linear address=30:6000-7fff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:4800-483f\n");
|
||||
markup.append(T T T "map address=80-bf:4800-483f\n");
|
||||
if(has_spc7110rtc) {
|
||||
xml << " <rtc>\n";
|
||||
xml << " <map address='00-3f:4840-4842'/>\n";
|
||||
xml << " <map address='80-bf:4840-4842'/>\n";
|
||||
xml << " </rtc>\n";
|
||||
markup.append(T T "rtc\n");
|
||||
markup.append(T T T "map address=00-3f:4840-4842\n");
|
||||
markup.append(T T T "map address=80-bf:4840-4842\n");
|
||||
}
|
||||
xml << " <dcu>\n";
|
||||
xml << " <map address='50:0000-ffff'/>\n";
|
||||
xml << " </dcu>\n";
|
||||
xml << " </spc7110>\n";
|
||||
markup.append(T T "dcu\n");
|
||||
markup.append(T T T "map address=50:0000-ffff\n");
|
||||
} else if(mapper == LoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-7f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=80-ff:8000-ffff\n");
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-ffff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=f0-ff:0000-ffff\n");
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == HiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=shadow address=00-3f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
|
||||
markup.append(T T "map mode=shadow address=80-bf:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=c0-ff:0000-ffff\n");
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == ExLoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-3f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=40-7f:0000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=80-bf:8000-ffff\n");
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
|
||||
}
|
||||
} else if(mapper == ExHiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=shadow address=00-3f:8000-ffff offset=0x400000\n");
|
||||
markup.append(T T "map mode=linear address=40-7f:0000-ffff offset=0x400000\n");
|
||||
markup.append(T T "map mode=shadow address=80-bf:8000-ffff offset=0x000000\n");
|
||||
markup.append(T T "map mode=linear address=c0-ff:0000-ffff offset=0x000000\n");
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
|
||||
} else {
|
||||
xml << " <map mode='linear' address='70-7f:0000-ffff'/>\n";
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-ffff\n");
|
||||
}
|
||||
xml << " </ram>\n";
|
||||
}
|
||||
} else if(mapper == SuperFXROM) {
|
||||
xml << " <superfx revision='2'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
|
||||
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
|
||||
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:3000-32ff'/>\n";
|
||||
xml << " <map address='80-bf:3000-32ff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </superfx>\n";
|
||||
markup.append(T "superfx revision=2\n");
|
||||
markup.append(T T "rom\n");
|
||||
markup.append(T T T "map mode=linear address=00-3f:8000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=40-5f:0000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=80-bf:8000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=c0-df:0000-ffff\n");
|
||||
markup.append(T T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T T "map mode=linear address=00-3f:6000-7fff size=0x2000\n");
|
||||
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=80-bf:6000-7fff size=0x2000\n");
|
||||
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:3000-32ff\n");
|
||||
markup.append(T T T "map address=80-bf:3000-32ff\n");
|
||||
} else if(mapper == SA1ROM) {
|
||||
xml << " <sa1>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='direct' address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='direct' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='direct' address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram>\n";
|
||||
xml << " <map mode='direct' address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <iram size='800'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
|
||||
xml << " </iram>\n";
|
||||
xml << " <bwram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
|
||||
xml << " </bwram>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:2200-23ff'/>\n";
|
||||
xml << " <map address='80-bf:2200-23ff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </sa1>\n";
|
||||
markup.append(T "sa1\n");
|
||||
markup.append(T T "mcu\n");
|
||||
markup.append(T T T "rom\n");
|
||||
markup.append(T T T T "map mode=direct address=00-3f:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=direct address=80-bf:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=direct address=c0-ff:0000-ffff\n");
|
||||
markup.append(T T T "ram\n");
|
||||
markup.append(T T T T "map mode=direct address=00-3f:6000-7fff\n");
|
||||
markup.append(T T T T "map mode=direct address=80-bf:6000-7fff\n");
|
||||
markup.append(T T "iram size=0x800\n");
|
||||
markup.append(T T T "map mode=linear address=00-3f:3000-37ff\n");
|
||||
markup.append(T T T "map mode=linear address=80-bf:3000-37ff\n");
|
||||
markup.append(T T "bwram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T T "map mode=linear address=40-4f:0000-ffff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:2200-23ff\n");
|
||||
markup.append(T T T "map address=80-bf:2200-23ff\n");
|
||||
} else if(mapper == BSCLoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <bsx>\n";
|
||||
xml << " <slot>\n";
|
||||
xml << " <map mode='linear' address='c0-ef:0000-ffff'/>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </bsx>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-1f:8000-ffff offset=0x000000\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:8000-ffff offset=0x100000\n");
|
||||
markup.append(T T "map mode=linear address=80-9f:8000-ffff offset=0x200000\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:8000-ffff offset=0x100000\n");
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=70-7f:0000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=f0-ff:0000-7fff\n");
|
||||
markup.append(T "bsx\n");
|
||||
markup.append(T T "slot\n");
|
||||
markup.append(T T T "map mode=linear address=c0-ef:0000-ffff\n");
|
||||
} else if(mapper == BSCHiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-1f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='40-5f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <bsx>\n";
|
||||
xml << " <slot>\n";
|
||||
xml << " <map mode='shadow' address='20-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='shadow' address='a0-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </bsx>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=shadow address=00-1f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=40-5f:0000-ffff\n");
|
||||
markup.append(T T "map mode=shadow address=80-9f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=c0-df:0000-ffff\n");
|
||||
markup.append(T "ram size=0x", hex(ram_size), "\n");
|
||||
markup.append(T T "map mode=linear address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "map mode=linear address=a0-bf:6000-7fff\n");
|
||||
markup.append(T "bsx\n");
|
||||
markup.append(T T "slot\n");
|
||||
markup.append(T T T "map mode=shadow address=20-3f:8000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=60-7f:0000-ffff\n");
|
||||
markup.append(T T T "map mode=shadow address=a0-bf:8000-ffff\n");
|
||||
markup.append(T T T "map mode=linear address=e0-ff:0000-ffff\n");
|
||||
} else if(mapper == BSXROM) {
|
||||
xml << " <bsx>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='00-3f:8000-ffff'/>\n";
|
||||
xml << " <map address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map address='40-7f:0000-ffff'/>\n";
|
||||
xml << " <map address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " <map address='20-3f:6000-7fff'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:5000-5fff'/>\n";
|
||||
xml << " <map address='80-bf:5000-5fff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </bsx>\n";
|
||||
markup.append(T "bsx\n");
|
||||
markup.append(T T "mcu\n");
|
||||
markup.append(T T T "map address=00-3f:8000-ffff\n");
|
||||
markup.append(T T T "map address=80-bf:8000-ffff\n");
|
||||
markup.append(T T T "map address=40-7f:0000-ffff\n");
|
||||
markup.append(T T T "map address=c0-ff:0000-ffff\n");
|
||||
markup.append(T T T "map address=20-3f:6000-7fff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:5000-5fff\n");
|
||||
markup.append(T T T "map address=80-bf:5000-5fff\n");
|
||||
} else if(mapper == STROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <sufamiturbo>\n";
|
||||
xml << " <slot id='A'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='20000'>\n";
|
||||
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " <slot id='B'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='20000'>\n";
|
||||
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </slot>\n";
|
||||
xml << " </sufamiturbo>\n";
|
||||
markup.append(T "rom\n");
|
||||
markup.append(T T "map mode=linear address=00-1f:8000-ffff\n");
|
||||
markup.append(T T "map mode=linear address=80-9f:8000-ffff\n");
|
||||
markup.append(T "sufamiturbo\n");
|
||||
markup.append(T T "slot id=A\n");
|
||||
markup.append(T T T "rom\n");
|
||||
markup.append(T T T T "map mode=linear address=20-3f:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=linear address=a0-bf:8000-ffff\n");
|
||||
markup.append(T T T "ram size=0x20000\n");
|
||||
markup.append(T T T T "map mode=linear address=60-63:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=linear address=e0-e3:8000-ffff\n");
|
||||
markup.append(T T "slot id=B\n");
|
||||
markup.append(T T T "rom\n");
|
||||
markup.append(T T T T "map mode=linear address=40-5f:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=linear address=c0-df:8000-ffff\n");
|
||||
markup.append(T T T "ram size=0x20000\n");
|
||||
markup.append(T T T T "map mode=linear address=70-73:8000-ffff\n");
|
||||
markup.append(T T T T "map mode=linear address=f0-f3:8000-ffff\n");
|
||||
}
|
||||
|
||||
if(has_srtc) {
|
||||
xml << " <srtc>\n";
|
||||
xml << " <map address='00-3f:2800-2801'/>\n";
|
||||
xml << " <map address='80-bf:2800-2801'/>\n";
|
||||
xml << " </srtc>\n";
|
||||
markup.append(T "srtc\n");
|
||||
markup.append(T T "map address=00-3f:2800-2801\n");
|
||||
markup.append(T T "map address=80-bf:2800-2801\n");
|
||||
}
|
||||
|
||||
if(has_sdd1) {
|
||||
xml << " <sdd1>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='c0-ff:0000-ffff'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:4800-4807'/>\n";
|
||||
xml << " <map address='80-bf:4800-4807'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </sdd1>\n";
|
||||
}
|
||||
|
||||
if(has_cx4) {
|
||||
xml << " <cx4>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </cx4>\n";
|
||||
markup.append(T "sdd1\n");
|
||||
markup.append(T T "mcu\n");
|
||||
markup.append(T T T "map address=c0-ff:0000-ffff\n");
|
||||
markup.append(T T "mmio\n");
|
||||
markup.append(T T T "map address=00-3f:4800-4807\n");
|
||||
markup.append(T T T "map address=80-bf:4800-4807\n");
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
|
||||
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp1b.bin sha256=4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c\n");
|
||||
if(dsp1_mapper == DSP1LoROM1MB) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=20-3f:8000-bfff\n");
|
||||
markup.append(T T T "map address=a0-bf:8000-bfff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=20-3f:c000-ffff\n");
|
||||
markup.append(T T T "map address=a0-bf:c000-ffff\n");
|
||||
} else if(dsp1_mapper == DSP1LoROM2MB) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60-6f:0000-3fff'/>\n";
|
||||
xml << " <map address='e0-ef:0000-3fff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60-6f:4000-7fff'/>\n";
|
||||
xml << " <map address='e0-ef:4000-7fff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=60-6f:0000-3fff\n");
|
||||
markup.append(T T T "map address=e0-ef:0000-3fff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=60-6f:4000-7fff\n");
|
||||
markup.append(T T T "map address=e0-ef:4000-7fff\n");
|
||||
} else if(dsp1_mapper == DSP1HiROM) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='00-1f:6000-6fff'/>\n";
|
||||
xml << " <map address='80-9f:6000-6fff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='00-1f:7000-7fff'/>\n";
|
||||
xml << " <map address='80-9f:7000-7fff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=00-1f:6000-6fff\n");
|
||||
markup.append(T T T "map address=80-9f:6000-6fff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=00-1f:7000-7fff\n");
|
||||
markup.append(T T T "map address=80-9f:7000-7fff\n");
|
||||
}
|
||||
xml << " </necdsp>\n";
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp2.bin sha256=5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1\n");
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=20-3f:8000-bfff\n");
|
||||
markup.append(T T T "map address=a0-bf:8000-bfff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=20-3f:c000-ffff\n");
|
||||
markup.append(T T T "map address=a0-bf:c000-ffff\n");
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='20-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='a0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp3.bin sha256=2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90\n");
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=20-3f:8000-bfff\n");
|
||||
markup.append(T T T "map address=a0-bf:8000-bfff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=20-3f:c000-ffff\n");
|
||||
markup.append(T T T "map address=a0-bf:c000-ffff\n");
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='30-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='b0-bf:8000-bfff'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='30-3f:c000-ffff'/>\n";
|
||||
xml << " <map address='b0-bf:c000-ffff'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " </necdsp>\n";
|
||||
markup.append(T "necdsp model=uPD7725 frequency=8000000 firmware=dsp4.bin sha256=63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a\n");
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=30-3f:8000-bfff\n");
|
||||
markup.append(T T T "map address=b0-bf:8000-bfff\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=30-3f:c000-ffff\n");
|
||||
markup.append(T T T "map address=b0-bf:c000-ffff\n");
|
||||
}
|
||||
|
||||
if(has_obc1) {
|
||||
xml << " <obc1>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </obc1>\n";
|
||||
markup.append(T "obc1\n");
|
||||
markup.append(T T "map address=00-3f:6000-7fff\n");
|
||||
markup.append(T T "map address=80-bf:6000-7fff\n");
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60:0001'/>\n";
|
||||
xml << " <map address='e0:0001'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " <dp>\n";
|
||||
xml << " <map address='68-6f:0000-0fff'/>\n";
|
||||
xml << " <map address='e8-ef:0000-0fff'/>\n";
|
||||
xml << " </dp>\n";
|
||||
xml << " </necdsp>\n";
|
||||
markup.append(T "necdsp model=uPD96050 frequency=10000000 firmware=st0010.bin sha256=55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e\n");
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=60:0000\n");
|
||||
markup.append(T T T "map address=e0:0000\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=60:0001\n");
|
||||
markup.append(T T T "map address=e0:0001\n");
|
||||
markup.append(T T "dp\n");
|
||||
markup.append(T T T "map address=68-6f:0000-0fff\n");
|
||||
markup.append(T T T "map address=e8-ef:0000-0fff\n");
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
xml << " </dr>\n";
|
||||
xml << " <sr>\n";
|
||||
xml << " <map address='60:0001'/>\n";
|
||||
xml << " <map address='e0:0001'/>\n";
|
||||
xml << " </sr>\n";
|
||||
xml << " <dp>\n";
|
||||
xml << " <map address='68-6f:0000-0fff'/>\n";
|
||||
xml << " <map address='e8-ef:0000-0fff'/>\n";
|
||||
xml << " </dp>\n";
|
||||
xml << " </necdsp>\n";
|
||||
markup.append(T "necdsp model=uPD96050 frequency=15000000 firmware=st0011.bin sha256=651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e\n");
|
||||
markup.append(T T "dr\n");
|
||||
markup.append(T T T "map address=60:0000\n");
|
||||
markup.append(T T T "map address=e0:0000\n");
|
||||
markup.append(T T "sr\n");
|
||||
markup.append(T T T "map address=60:0001\n");
|
||||
markup.append(T T T "map address=e0:0001\n");
|
||||
markup.append(T T "dp\n");
|
||||
markup.append(T T T "map address=68-6f:0000-0fff\n");
|
||||
markup.append(T T T "map address=e8-ef:0000-0fff\n");
|
||||
}
|
||||
|
||||
if(has_st018) {
|
||||
xml << " <setarisc program='ST-0018'>\n";
|
||||
xml << " <map address='00-3f:3800-38ff'/>\n";
|
||||
xml << " <map address='80-bf:3800-38ff'/>\n";
|
||||
xml << " </setarisc>\n";
|
||||
markup.append(T "setarisc firmware=ST-0018\n");
|
||||
markup.append(T T "map address=00-3f:3800-38ff\n");
|
||||
markup.append(T T "map address=80-bf:3800-38ff\n");
|
||||
}
|
||||
|
||||
xml << "</cartridge>\n";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
}
|
||||
|
||||
void SNESCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
#undef T
|
||||
|
||||
void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
@@ -758,7 +672,7 @@ void SNESCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned SnesCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
@@ -773,7 +687,7 @@ unsigned SNESCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
unsigned SnesCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
@@ -854,7 +768,7 @@ unsigned SNESCartridge::score_header(const uint8_t *data, unsigned size, unsigne
|
||||
return score;
|
||||
}
|
||||
|
||||
unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
|
||||
unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return 0;
|
||||
switch(data[0x0149]) {
|
||||
case 0x00: return 0 * 1024;
|
||||
@@ -867,7 +781,7 @@ unsigned SNESCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SNESCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
|
||||
bool SnesCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
|
||||
if(size < 512) return false;
|
||||
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
|
||||
return false;
|
||||
|
26
bsnes/nall/stack.hpp
Executable file
26
bsnes/nall/stack.hpp
Executable file
@@ -0,0 +1,26 @@
|
||||
#ifndef NALL_STACK_HPP
|
||||
#define NALL_STACK_HPP
|
||||
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<typename T> struct stack : public linear_vector<T> {
|
||||
void push(const T &value) {
|
||||
linear_vector<T>::append(value);
|
||||
}
|
||||
|
||||
T pull() {
|
||||
if(linear_vector<T>::size() == 0) throw;
|
||||
T value = linear_vector<T>::operator[](linear_vector<T>::size() - 1);
|
||||
linear_vector<T>::remove(linear_vector<T>::size() - 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
T& operator()() {
|
||||
if(linear_vector<T>::size() == 0) throw;
|
||||
return linear_vector<T>::operator[](linear_vector<T>::size() - 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,16 +1,34 @@
|
||||
#ifndef NALL_STRING_HPP
|
||||
#define NALL_STRING_HPP
|
||||
|
||||
#include <initializer_list>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/atoi.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
#include <nall/windows/utf8.hpp>
|
||||
|
||||
#define NALL_STRING_INTERNAL_HPP
|
||||
#include <nall/string/base.hpp>
|
||||
#include <nall/string/bml.hpp>
|
||||
#include <nall/string/bsv.hpp>
|
||||
#include <nall/string/core.hpp>
|
||||
#include <nall/string/cast.hpp>
|
||||
#include <nall/string/compare.hpp>
|
||||
#include <nall/string/convert.hpp>
|
||||
#include <nall/string/cstring.hpp>
|
||||
#include <nall/string/filename.hpp>
|
||||
#include <nall/string/math.hpp>
|
||||
#include <nall/string/platform.hpp>
|
||||
@@ -23,10 +41,6 @@
|
||||
#include <nall/string/variadic.hpp>
|
||||
#include <nall/string/wrapper.hpp>
|
||||
#include <nall/string/xml.hpp>
|
||||
|
||||
namespace nall {
|
||||
template<> struct has_length<string> { enum { value = true }; };
|
||||
template<> struct has_size<lstring> { enum { value = true }; };
|
||||
}
|
||||
#undef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
#endif
|
||||
|
@@ -1,32 +1,38 @@
|
||||
#ifndef NALL_STRING_BASE_HPP
|
||||
#define NALL_STRING_BASE_HPP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/utf8.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
class string;
|
||||
struct cstring;
|
||||
struct string;
|
||||
struct lstring;
|
||||
template<typename T> inline const char* to_string(T);
|
||||
|
||||
class string {
|
||||
public:
|
||||
struct cstring {
|
||||
inline operator const char*() const;
|
||||
inline unsigned length() const;
|
||||
inline bool operator==(const char*) const;
|
||||
inline bool operator!=(const char*) const;
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> iposition(const char *key) const;
|
||||
inline cstring& operator=(const char *data);
|
||||
inline cstring(const char *data);
|
||||
inline cstring();
|
||||
|
||||
protected:
|
||||
const char *data;
|
||||
};
|
||||
|
||||
struct string {
|
||||
inline void reserve(unsigned);
|
||||
|
||||
template<typename... Args> inline string& assign(Args&&... args);
|
||||
template<typename... Args> inline string& append(Args&&... args);
|
||||
inline string& assign_(const char*);
|
||||
inline string& append_(const char*);
|
||||
|
||||
inline bool readfile(const string&);
|
||||
|
||||
inline string& replace (const char*, const char*);
|
||||
inline string& qreplace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& replace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& ireplace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& qreplace(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline string& iqreplace(const char*, const char*);
|
||||
|
||||
inline unsigned length() const;
|
||||
|
||||
@@ -43,17 +49,18 @@ namespace nall {
|
||||
|
||||
inline string& lower();
|
||||
inline string& upper();
|
||||
inline string& qlower();
|
||||
inline string& qupper();
|
||||
inline string& transform(const char *before, const char *after);
|
||||
|
||||
template<unsigned limit = 0> inline string& ltrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& rtrim(const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& trim (const char *key = " ");
|
||||
template<unsigned limit = 0> inline string& trim(const char *key = " ", const char *rkey = 0);
|
||||
|
||||
inline optional<unsigned> position(const char *key) const;
|
||||
inline optional<unsigned> iposition(const char *key) const;
|
||||
inline optional<unsigned> qposition(const char *key) const;
|
||||
|
||||
template<typename T> inline string& operator= (T value);
|
||||
template<typename T> inline string& operator<<(T value);
|
||||
inline optional<unsigned> iqposition(const char *key) const;
|
||||
|
||||
inline operator const char*() const;
|
||||
inline char* operator()();
|
||||
@@ -74,51 +81,66 @@ namespace nall {
|
||||
inline string(string&&);
|
||||
inline ~string();
|
||||
|
||||
inline char* begin() { return &data[0]; }
|
||||
inline char* end() { return &data[length()]; }
|
||||
inline const char* begin() const { return &data[0]; }
|
||||
inline const char* end() const { return &data[length()]; }
|
||||
|
||||
//internal functions
|
||||
inline string& assign_(const char*);
|
||||
inline string& append_(const char*);
|
||||
|
||||
protected:
|
||||
char *data;
|
||||
unsigned size;
|
||||
|
||||
template<unsigned Limit, bool Insensitive, bool Quoted> inline string& ureplace(const char*, const char*);
|
||||
|
||||
#if defined(QSTRING_H)
|
||||
public:
|
||||
inline operator QString() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class lstring : public linear_vector<string> {
|
||||
public:
|
||||
struct lstring : public linear_vector<string> {
|
||||
template<typename T> inline lstring& operator<<(T value);
|
||||
|
||||
inline optional<unsigned> find(const char*) const;
|
||||
template<unsigned limit = 0> inline void split (const char*, const char*);
|
||||
template<unsigned limit = 0> inline void qsplit(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& split(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& isplit(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& qsplit(const char*, const char*);
|
||||
template<unsigned Limit = 0> inline lstring& iqsplit(const char*, const char*);
|
||||
|
||||
lstring();
|
||||
lstring(std::initializer_list<string>);
|
||||
inline bool operator==(const lstring&) const;
|
||||
inline bool operator!=(const lstring&) const;
|
||||
|
||||
inline lstring();
|
||||
inline lstring(std::initializer_list<string>);
|
||||
|
||||
protected:
|
||||
template<unsigned Limit, bool Insensitive, bool Quoted> inline lstring& usplit(const char*, const char*);
|
||||
};
|
||||
|
||||
//compare.hpp
|
||||
inline char chrlower(char c);
|
||||
inline char chrupper(char c);
|
||||
inline int stricmp(const char *str1, const char *str2);
|
||||
inline int istrcmp(const char *str1, const char *str2);
|
||||
inline bool wildcard(const char *str, const char *pattern);
|
||||
inline bool iwildcard(const char *str, const char *pattern);
|
||||
inline bool strbegin (const char *str, const char *key);
|
||||
inline bool stribegin(const char *str, const char *key);
|
||||
inline bool strend (const char *str, const char *key);
|
||||
inline bool striend(const char *str, const char *key);
|
||||
inline bool strbegin(const char *str, const char *key);
|
||||
inline bool istrbegin(const char *str, const char *key);
|
||||
inline bool strend(const char *str, const char *key);
|
||||
inline bool istrend(const char *str, const char *key);
|
||||
|
||||
//convert.hpp
|
||||
inline char* strlower(char *str);
|
||||
inline char* strupper(char *str);
|
||||
inline char* qstrlower(char *str);
|
||||
inline char* qstrupper(char *str);
|
||||
inline char* strtr(char *dest, const char *before, const char *after);
|
||||
inline uintmax_t hex (const char *str);
|
||||
inline intmax_t integer(const char *str);
|
||||
inline uintmax_t decimal(const char *str);
|
||||
inline uintmax_t binary (const char *str);
|
||||
inline double fp (const char *str);
|
||||
|
||||
//math.hpp
|
||||
inline bool strint (const char *str, int &result);
|
||||
inline bool strint(const char *str, int &result);
|
||||
inline bool strmath(const char *str, int &result);
|
||||
|
||||
//platform.hpp
|
||||
@@ -132,26 +154,31 @@ namespace nall {
|
||||
|
||||
//strpos.hpp
|
||||
inline optional<unsigned> strpos(const char *str, const char *key);
|
||||
inline optional<unsigned> istrpos(const char *str, const char *key);
|
||||
inline optional<unsigned> qstrpos(const char *str, const char *key);
|
||||
inline optional<unsigned> iqstrpos(const char *str, const char *key);
|
||||
template<bool Insensitive = false, bool Quoted = false> inline optional<unsigned> ustrpos(const char *str, const char *key);
|
||||
|
||||
//trim.hpp
|
||||
template<unsigned limit = 0> inline char* ltrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* rtrim(char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* trim (char *str, const char *key = " ");
|
||||
template<unsigned limit = 0> inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
|
||||
|
||||
//utility.hpp
|
||||
template<bool Insensitive> alwaysinline bool chrequal(char x, char y);
|
||||
template<bool Quoted, typename T> alwaysinline bool quoteskip(T *&p);
|
||||
template<bool Quoted, typename T> alwaysinline bool quotecopy(char *&t, T *&p);
|
||||
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
|
||||
inline unsigned strlcat(string &dest, const char *src, unsigned length);
|
||||
inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
|
||||
inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
|
||||
inline string sha256(const uint8_t *data, unsigned size);
|
||||
|
||||
inline string integer(intmax_t value);
|
||||
template<unsigned length = 0> inline string linteger(intmax_t value);
|
||||
template<unsigned length = 0> inline string rinteger(intmax_t value);
|
||||
inline string decimal(uintmax_t value);
|
||||
template<unsigned length = 0> inline string ldecimal(uintmax_t value);
|
||||
template<unsigned length = 0> inline string rdecimal(uintmax_t value);
|
||||
template<unsigned length = 0> inline string hex(uintmax_t value);
|
||||
template<unsigned length = 0> inline string binary(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string integer(intmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string linteger(intmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string decimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
|
||||
inline unsigned fp(char *str, double value);
|
||||
inline string fp(double value);
|
||||
|
||||
|
151
bsnes/nall/string/bml.hpp
Executable file
151
bsnes/nall/string/bml.hpp
Executable file
@@ -0,0 +1,151 @@
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//BML v1.0 parser
|
||||
//revision 0.05
|
||||
|
||||
namespace nall {
|
||||
namespace BML {
|
||||
|
||||
inline static string indent(const char *s, unsigned depth) {
|
||||
array<char> output;
|
||||
do {
|
||||
for(unsigned n = 0; n < depth; n++) output.append('\t');
|
||||
do output.append(*s); while(*s && *s++ != '\n');
|
||||
} while(*s);
|
||||
return output.get();
|
||||
}
|
||||
|
||||
struct Node {
|
||||
cstring name;
|
||||
cstring value;
|
||||
|
||||
private:
|
||||
linear_vector<Node> children;
|
||||
|
||||
inline bool valid(char p) const { //A-Za-z0-9-.
|
||||
return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u;
|
||||
}
|
||||
|
||||
inline unsigned parseDepth(char *&p) {
|
||||
while(*p == '\n' || *p == '#') {
|
||||
while(*p != '\n') *p++ = 0;
|
||||
*p++ = 0; //'\n'
|
||||
}
|
||||
unsigned depth = 0;
|
||||
while(p[depth] == '\t') depth++;
|
||||
return depth;
|
||||
}
|
||||
|
||||
inline void parseName(char *&p) {
|
||||
if(valid(*p) == false) throw "Missing node name";
|
||||
name = p;
|
||||
while(valid(*p)) p++;
|
||||
}
|
||||
|
||||
inline void parseValue(char *&p) {
|
||||
char terminal = *p == ':' ? '\n' : ' '; //':' or '='
|
||||
*p++ = 0;
|
||||
value = p;
|
||||
while(*p && *p != terminal && *p != '\n') p++;
|
||||
}
|
||||
|
||||
inline void parseBlock(char *&p, unsigned depth) {
|
||||
value = p;
|
||||
char *w = p;
|
||||
while(parseDepth(p) > depth) {
|
||||
p += depth + 1;
|
||||
while(*p && *p != '\n') *w++ = *p++;
|
||||
if(*p && *p != '\n') throw "Multi-line value missing line feed";
|
||||
*w++ = *p;
|
||||
}
|
||||
*(w - 1) = 0; //'\n'
|
||||
}
|
||||
|
||||
inline void parseLine(char *&p) {
|
||||
unsigned depth = parseDepth(p);
|
||||
while(*p == '\t') p++;
|
||||
|
||||
parseName(p);
|
||||
bool multiLine = *p == '~';
|
||||
if(multiLine) *p++ = 0;
|
||||
else if(*p == ':' || *p == '=') parseValue(p);
|
||||
if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered";
|
||||
|
||||
while(*p == ' ') {
|
||||
*p++ = 0;
|
||||
Node node;
|
||||
node.parseName(p);
|
||||
if(*p == ':' || *p == '=') node.parseValue(p);
|
||||
if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node";
|
||||
if(*p == '\n') *p++ = 0;
|
||||
children.append(node);
|
||||
}
|
||||
|
||||
if(multiLine) return parseBlock(p, depth);
|
||||
|
||||
while(parseDepth(p) > depth) {
|
||||
Node node;
|
||||
node.parseLine(p);
|
||||
children.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
inline void parse(char *&p) {
|
||||
while(*p) {
|
||||
Node node;
|
||||
node.parseLine(p);
|
||||
children.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline Node& operator[](const char *name) {
|
||||
for(auto &node : children) {
|
||||
if(node.name == name) return node;
|
||||
}
|
||||
static Node node;
|
||||
node.name = nullptr;
|
||||
return node;
|
||||
}
|
||||
|
||||
inline bool exists() const { return name; }
|
||||
unsigned size() const { return children.size(); }
|
||||
Node* begin() { return children.begin(); }
|
||||
Node* end() { return children.end(); }
|
||||
const Node* begin() const { return children.begin(); }
|
||||
const Node* end() const { return children.end(); }
|
||||
inline Node() : name(""), value("") {}
|
||||
friend class Document;
|
||||
};
|
||||
|
||||
struct Document : Node {
|
||||
cstring error;
|
||||
|
||||
inline bool load(const char *document) {
|
||||
if(document == nullptr) return false;
|
||||
this->document = strdup(document);
|
||||
char *p = this->document;
|
||||
try {
|
||||
this->error = nullptr;
|
||||
parse(p);
|
||||
} catch(const char *error) {
|
||||
this->error = error;
|
||||
free(this->document);
|
||||
this->document = nullptr;
|
||||
children.reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Document(const char *document = "") : document(nullptr), error(nullptr) { if(*document) load(document); }
|
||||
inline ~Document() { if(document) free(document); }
|
||||
|
||||
private:
|
||||
char *document;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,74 +1,75 @@
|
||||
#ifndef NALL_STRING_BSV_HPP
|
||||
#define NALL_STRING_BSV_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//BSV parser
|
||||
//version 0.01
|
||||
//BSV v1.0 parser
|
||||
//revision 0.02
|
||||
|
||||
namespace nall {
|
||||
|
||||
inline string bsv_decode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '}' ) return "";
|
||||
if(*input == '\r') return "";
|
||||
if(*input == '\n') return "";
|
||||
struct BSV {
|
||||
static inline string decode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '}' ) return "";
|
||||
if(*input == '\r') return "";
|
||||
if(*input == '\n') return "";
|
||||
|
||||
//normal characters
|
||||
if(*input != '{') { output[offset++] = *input++; continue; }
|
||||
//normal characters
|
||||
if(*input != '{') { output[offset++] = *input++; continue; }
|
||||
|
||||
//entities
|
||||
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
|
||||
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
|
||||
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
|
||||
//entities
|
||||
if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; }
|
||||
if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; }
|
||||
if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; }
|
||||
|
||||
//illegal entities
|
||||
return "";
|
||||
//illegal entities
|
||||
return "";
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
inline string bsv_encode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '\r') return "";
|
||||
static inline string encode(const char *input) {
|
||||
string output;
|
||||
unsigned offset = 0;
|
||||
while(*input) {
|
||||
//illegal characters
|
||||
if(*input == '\r') return "";
|
||||
|
||||
if(*input == '\n') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'f';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
if(*input == '\n') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'f';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '{') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '}') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'r';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[offset++] = *input++;
|
||||
}
|
||||
|
||||
if(*input == '{') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'l';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(*input == '}') {
|
||||
output[offset++] = '{';
|
||||
output[offset++] = 'r';
|
||||
output[offset++] = 'b';
|
||||
output[offset++] = '}';
|
||||
input++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[offset++] = *input++;
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
output[offset] = 0;
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,20 +1,20 @@
|
||||
#ifndef NALL_STRING_CAST_HPP
|
||||
#define NALL_STRING_CAST_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//this is needed, as C++0x does not support explicit template specialization inside classes
|
||||
template<> inline const char* to_string<bool> (bool v) { return v ? "true" : "false"; }
|
||||
template<> inline const char* to_string<signed int> (signed int v) { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
|
||||
template<> inline const char* to_string<unsigned int> (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
|
||||
template<> inline const char* to_string<double> (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
|
||||
template<> inline const char* to_string<char*> (char *v) { return v; }
|
||||
template<> inline const char* to_string<const char*> (const char *v) { return v; }
|
||||
template<> inline const char* to_string<string> (string v) { return v; }
|
||||
template<> inline const char* to_string<const string&>(const string &v) { return v; }
|
||||
|
||||
template<typename T> string& string::operator= (T value) { return assign(to_string<T>(value)); }
|
||||
template<typename T> string& string::operator<<(T value) { return append(to_string<T>(value)); }
|
||||
template<> inline const char* to_string<bool> (bool v) { return v ? "true" : "false"; }
|
||||
template<> inline const char* to_string<signed int> (signed int v) { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
|
||||
template<> inline const char* to_string<unsigned int> (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
|
||||
template<> inline const char* to_string<intmax_t> (intmax_t v) { static char temp[256]; snprintf(temp, 255, "%+lld", (long long)v); return temp; }
|
||||
template<> inline const char* to_string<uintmax_t> (uintmax_t v) { static char temp[256]; snprintf(temp, 255, "%llu", (unsigned long long)v); return temp; }
|
||||
template<> inline const char* to_string<double> (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
|
||||
template<> inline const char* to_string<char*> (char *v) { return v; }
|
||||
template<> inline const char* to_string<const char*> (const char *v) { return v; }
|
||||
template<> inline const char* to_string<string> (string v) { return v; }
|
||||
template<> inline const char* to_string<const string&> (const string &v) { return v; }
|
||||
template<> inline const char* to_string<cstring> (cstring v) { return v; }
|
||||
template<> inline const char* to_string<const cstring&>(const cstring &v) { return v; }
|
||||
|
||||
template<typename T> lstring& lstring::operator<<(T value) {
|
||||
operator[](size()).assign(to_string<T>(value));
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#ifndef NALL_STRING_COMPARE_HPP
|
||||
#define NALL_STRING_COMPARE_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
@@ -11,7 +10,7 @@ char chrupper(char c) {
|
||||
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
int stricmp(const char *str1, const char *str2) {
|
||||
int istrcmp(const char *str1, const char *str2) {
|
||||
while(*str1) {
|
||||
if(chrlower(*str1) != chrlower(*str2)) break;
|
||||
str1++, str2++;
|
||||
@@ -66,7 +65,7 @@ bool strbegin(const char *str, const char *key) {
|
||||
return (!memcmp(str, key, ksl));
|
||||
}
|
||||
|
||||
bool stribegin(const char *str, const char *key) {
|
||||
bool istrbegin(const char *str, const char *key) {
|
||||
int ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
@@ -89,7 +88,7 @@ bool strend(const char *str, const char *key) {
|
||||
return (!memcmp(str + ssl - ksl, key, ksl));
|
||||
}
|
||||
|
||||
bool striend(const char *str, const char *key) {
|
||||
bool istrend(const char *str, const char *key) {
|
||||
int ssl = strlen(str), ksl = strlen(key);
|
||||
|
||||
if(ksl > ssl) return false;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#ifndef NALL_STRING_CONVERT_HPP
|
||||
#define NALL_STRING_CONVERT_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
@@ -23,6 +22,26 @@ char* strupper(char *str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
char* qstrlower(char *s) {
|
||||
if(!s) return 0;
|
||||
bool quoted = false;
|
||||
while(*s) {
|
||||
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||
if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char* qstrupper(char *s) {
|
||||
if(!s) return 0;
|
||||
bool quoted = false;
|
||||
while(*s) {
|
||||
if(*s == '\"' || *s == '\'') quoted ^= 1;
|
||||
if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char* strtr(char *dest, const char *before, const char *after) {
|
||||
if(!dest || !before || !after) return dest;
|
||||
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
|
||||
@@ -40,86 +59,6 @@ char* strtr(char *dest, const char *before, const char *after) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
uintmax_t hex(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
//skip hex identifiers 0x and $, if present
|
||||
if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2;
|
||||
else if(*str == '$') str++;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else if(x >= 'A' && x <= 'F') x -= 'A' - 10;
|
||||
else if(x >= 'a' && x <= 'f') x -= 'a' - 10;
|
||||
else break; //stop at first invalid character
|
||||
result = result * 16 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
intmax_t integer(const char *str) {
|
||||
if(!str) return 0;
|
||||
intmax_t result = 0;
|
||||
bool negate = false;
|
||||
|
||||
//check for sign
|
||||
if(*str == '+') {
|
||||
negate = false;
|
||||
str++;
|
||||
} else if(*str == '-') {
|
||||
negate = true;
|
||||
str++;
|
||||
}
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 10 + x;
|
||||
}
|
||||
|
||||
return !negate ? result : -result;
|
||||
}
|
||||
|
||||
uintmax_t decimal(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x >= '0' && x <= '9') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 10 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uintmax_t binary(const char *str) {
|
||||
if(!str) return 0;
|
||||
uintmax_t result = 0;
|
||||
|
||||
//skip bin identifiers 0b and %, if present
|
||||
if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2;
|
||||
else if(*str == '%') str++;
|
||||
|
||||
while(*str) {
|
||||
uint8_t x = *str++;
|
||||
if(x == '0' || x == '1') x -= '0';
|
||||
else break; //stop at first invalid character
|
||||
result = result * 2 + x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double fp(const char *str) {
|
||||
return atof(str);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#ifndef NALL_STRING_CORE_HPP
|
||||
#define NALL_STRING_CORE_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
@@ -66,15 +65,17 @@ bool string::operator> (const char *str) const { return strcmp(data, str) > 0;
|
||||
bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; }
|
||||
|
||||
string& string::operator=(const string &value) {
|
||||
if(&value == this) return *this;
|
||||
assign(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::operator=(string &&source) {
|
||||
if(&source == this) return *this;
|
||||
if(data) free(data);
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = 0;
|
||||
source.data = nullptr;
|
||||
source.size = 0;
|
||||
return *this;
|
||||
}
|
||||
@@ -87,14 +88,16 @@ template<typename... Args> string::string(Args&&... args) {
|
||||
}
|
||||
|
||||
string::string(const string &value) {
|
||||
if(&value == this) return;
|
||||
size = strlen(value);
|
||||
data = strdup(value);
|
||||
}
|
||||
|
||||
string::string(string &&source) {
|
||||
if(&source == this) return;
|
||||
size = source.size;
|
||||
data = source.data;
|
||||
source.data = 0;
|
||||
source.data = nullptr;
|
||||
}
|
||||
|
||||
string::~string() {
|
||||
@@ -131,6 +134,19 @@ optional<unsigned> lstring::find(const char *key) const {
|
||||
return { false, 0 };
|
||||
}
|
||||
|
||||
bool lstring::operator==(const lstring &source) const {
|
||||
if(this == &source) return true;
|
||||
if(size() != source.size()) return false;
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
if(operator[](n) != source[n]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lstring::operator!=(const lstring &source) const {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
inline lstring::lstring() {
|
||||
}
|
||||
|
||||
|
21
bsnes/nall/string/cstring.hpp
Executable file
21
bsnes/nall/string/cstring.hpp
Executable file
@@ -0,0 +1,21 @@
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
//const string:
|
||||
//bind a const char* pointer to an object that has various testing functionality;
|
||||
//yet lacks the memory allocation and modification functionality of the string class
|
||||
|
||||
namespace nall {
|
||||
|
||||
cstring::operator const char*() const { return data; }
|
||||
unsigned cstring::length() const { return strlen(data); }
|
||||
bool cstring::operator==(const char *s) const { return !strcmp(data, s); }
|
||||
bool cstring::operator!=(const char *s) const { return strcmp(data, s); }
|
||||
optional<unsigned> cstring::position (const char *key) const { return strpos(data, key); }
|
||||
optional<unsigned> cstring::iposition(const char *key) const { return istrpos(data, key); }
|
||||
cstring& cstring::operator=(const char *data) { this->data = data; return *this; }
|
||||
cstring::cstring(const char *data) : data(data) {}
|
||||
cstring::cstring() : data("") {}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,5 +1,4 @@
|
||||
#ifndef NALL_FILENAME_HPP
|
||||
#define NALL_FILENAME_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
#ifndef NALL_STRING_MATH_HPP
|
||||
#define NALL_STRING_MATH_HPP
|
||||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
static function<int64_t (const char *&)> eval_fallback;
|
||||
|
||||
static int eval_integer(const char *&s) {
|
||||
if(!*s) throw "unrecognized_integer";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
@@ -58,7 +59,7 @@ static int eval_integer(const char *&s) {
|
||||
}
|
||||
|
||||
static int eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized_token";
|
||||
int value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
@@ -74,10 +75,12 @@ static int eval(const char *&s, int depth = 0) {
|
||||
|
||||
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||
|
||||
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||
|
||||
else throw "unrecognized_token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) break;
|
||||
x = *s, y = *(s + 1);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user