Update to v082r20 release.

byuu says:

NES now has save state support.
NES A/B buttons were indeed swapped, so that's fixed now.
nall/dsp now puts resamplers into separate classes, so that each can
have their own state information.
opengl.hpp uses GL_RGBA internal format and doesn't regenerate textures
on resize. No speedup, no fix to junk on resize, so I will be very
unhappy if this breaks things for anyone.
GLSL shaders use <fragment filter="nearest/point"> as you guys wanted.
ui-snes was removed.
This commit is contained in:
Tim Allen 2011-09-23 21:13:57 +10:00
parent 98ec338285
commit 979aa640af
115 changed files with 785 additions and 5209 deletions

View File

@ -75,6 +75,6 @@ clean:
-@$(call delete,*.manifest)
archive-all:
tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes ui-snes 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:;

View File

@ -4,7 +4,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static unsigned SerializerVersion = 2;
static const unsigned SerializerVersion = 2;
}
}

View File

@ -5,9 +5,24 @@
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 Resampler : unsigned {
Point,
enum class ResampleEngine : unsigned {
Nearest,
Linear,
Cosine,
Cubic,
@ -17,12 +32,12 @@ struct DSP {
inline void setChannels(unsigned channels);
inline void setPrecision(unsigned precision);
inline void setFrequency(double frequency); //inputFrequency
inline void setVolume(double volume);
inline void setBalance(double balance);
inline void setFrequency(real frequency); //inputFrequency
inline void setVolume(real volume);
inline void setBalance(real balance);
inline void setResampler(Resampler resampler);
inline void setResamplerFrequency(double frequency); //outputFrequency
inline void setResampler(ResampleEngine resamplingEngine);
inline void setResamplerFrequency(real frequency); //outputFrequency
inline void sample(signed channel[]);
inline bool pending();
@ -33,33 +48,27 @@ struct DSP {
inline ~DSP();
protected:
friend class ResampleNearest;
friend class ResampleLinear;
friend class ResampleCosine;
friend class ResampleCubic;
friend class ResampleAverage;
friend class ResampleHermite;
struct Settings {
unsigned channels;
unsigned precision;
double frequency;
double volume;
double balance;
real frequency;
real volume;
real balance;
//internal
double intensity;
real intensity;
real intensityInverse;
} settings;
struct ResamplerSettings {
Resampler engine;
double frequency;
//internal
double fraction;
double step;
} resampler;
inline void resamplerRun();
inline void resamplerWrite(double channel[]);
inline void resamplePoint();
inline void resampleLinear();
inline void resampleCosine();
inline void resampleCubic();
inline void resampleHermite();
inline void resampleAverage();
Resampler *resampler;
inline void write(real channel[]);
#include "buffer.hpp"
Buffer buffer;
@ -70,14 +79,20 @@ protected:
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 "settings.hpp"
void DSP::sample(signed channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
buffer.write(c) = (double)channel[c] / settings.intensity;
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
}
buffer.wroffset++;
resamplerRun();
resampler->sample();
}
bool DSP::pending() {
@ -94,31 +109,13 @@ void DSP::read(signed channel[]) {
output.rdoffset++;
}
void DSP::resamplerRun() {
switch(resampler.engine) {
case Resampler::Point: return resamplePoint();
case Resampler::Linear: return resampleLinear();
case Resampler::Cosine: return resampleCosine();
case Resampler::Cubic: return resampleCubic();
case Resampler::Hermite: return resampleHermite();
case Resampler::Average: return resampleAverage();
}
}
void DSP::resamplerWrite(double channel[]) {
void DSP::write(real channel[]) {
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) = channel[c];
}
output.wroffset++;
}
#include "resample/point.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
void DSP::adjustVolume() {
for(unsigned c = 0; c < settings.channels; c++) {
output.read(c) *= settings.volume;
@ -138,25 +135,30 @@ signed DSP::clamp(const unsigned bits, const signed x) {
}
void DSP::clear() {
resampler.fraction = 0.0;
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);
setResampler(Resampler::Hermite);
setResamplerFrequency(44100.0);
clear();
}
DSP::~DSP() {
if(resampler) delete resampler;
}
#undef real
}
#endif

View File

@ -1,31 +1,72 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleAverage() {
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(resampler.step < 1.0) return resampleHermite();
if(step < 1.0) return sampleLinear();
resampler.fraction += 1.0;
fraction += 1.0;
double scalar = 1.0;
if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
real scalar = 1.0;
if(fraction > step) scalar = 1.0 - (fraction - step);
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) += buffer.read(c) * scalar;
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
}
if(resampler.fraction >= resampler.step) {
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) /= resampler.step;
if(fraction >= step) {
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) /= step;
}
output.wroffset++;
dsp.output.wroffset++;
resampler.fraction -= resampler.step;
for(unsigned c = 0; c < settings.channels; c++) {
output.write(c) = buffer.read(c) * resampler.fraction;
fraction -= step;
for(unsigned c = 0; c < dsp.settings.channels; c++) {
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
}
}
buffer.rdoffset++;
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

View File

@ -1,25 +1,44 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleCosine() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
struct ResampleCosine : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
real fraction;
real step;
};
double mu = resampler.fraction;
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;
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
dsp.write(channel);
fraction += step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@ -1,31 +1,50 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleCubic() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
struct ResampleCubic : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -3);
double b = buffer.read(n, -2);
double c = buffer.read(n, -1);
double d = buffer.read(n, -0);
real fraction;
real step;
};
double mu = resampler.fraction;
void ResampleCubic::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
double A = d - c - a + b;
double B = a - b - A;
double C = c - a;
double D = b;
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;
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
dsp.write(channel);
fraction += step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@ -1,21 +1,40 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleHermite() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
struct ResampleHermite : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -3);
double b = buffer.read(n, -2);
double c = buffer.read(n, -1);
double d = buffer.read(n, -0);
real fraction;
real step;
};
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
void ResampleHermite::setFrequency() {
fraction = 0.0;
step = dsp.settings.frequency / frequency;
}
double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
void ResampleHermite::clear() {
fraction = 0.0;
}
mu1 = resampler.fraction;
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;
@ -32,12 +51,12 @@ void DSP::resampleHermite() {
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
dsp.write(channel);
fraction += step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View File

@ -1,24 +1,43 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resampleLinear() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
struct ResampleLinear : Resampler {
inline void setFrequency();
inline void clear();
inline void sample();
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
real fraction;
real step;
};
double mu = resampler.fraction;
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;
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
dsp.write(channel);
fraction += step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
dsp.buffer.rdoffset++;
fraction -= 1.0;
}
#endif

View 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

View File

@ -1,24 +0,0 @@
#ifdef NALL_DSP_INTERNAL_HPP
void DSP::resamplePoint() {
while(resampler.fraction <= 1.0) {
double channel[settings.channels];
for(unsigned n = 0; n < settings.channels; n++) {
double a = buffer.read(n, -1);
double b = buffer.read(n, -0);
double mu = resampler.fraction;
channel[n] = mu < 0.5 ? a : b;
}
resamplerWrite(channel);
resampler.fraction += resampler.step;
}
buffer.rdoffset++;
resampler.fraction -= 1.0;
}
#endif

View File

@ -10,30 +10,40 @@ void DSP::setChannels(unsigned channels) {
void DSP::setPrecision(unsigned precision) {
settings.precision = precision;
settings.intensity = 1 << (settings.precision - 1);
settings.intensityInverse = 1.0 / settings.intensity;
}
void DSP::setFrequency(double frequency) {
void DSP::setFrequency(real frequency) {
settings.frequency = frequency;
resampler.fraction = 0;
resampler.step = settings.frequency / resampler.frequency;
resampler->setFrequency();
}
void DSP::setVolume(double volume) {
void DSP::setVolume(real volume) {
settings.volume = volume;
}
void DSP::setBalance(double balance) {
void DSP::setBalance(real balance) {
settings.balance = balance;
}
void DSP::setResampler(Resampler engine) {
resampler.engine = engine;
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;
}
void DSP::setResamplerFrequency(double frequency) {
resampler.frequency = frequency;
resampler.fraction = 0;
resampler.step = settings.frequency / resampler.frequency;
throw;
}
void DSP::setResamplerFrequency(real frequency) {
resampler->frequency = frequency;
resampler->setFrequency();
}
#endif

View File

@ -21,6 +21,7 @@ namespace nall {
};
template<typename T> class optional {
public:
bool valid;
T value;
public:

View File

@ -2,6 +2,7 @@
namespace NES {
#include "serialization.cpp"
APU apu;
const uint8 APU::length_counter_table[32] = {
@ -31,6 +32,10 @@ void APU::Main() {
void APU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
unsigned rectangle_output, triangle_output, noise_output, dmc_output;
rectangle_output = rectangle[0].clock();
@ -50,7 +55,7 @@ void APU::main() {
void APU::tick() {
clock += 12;
if(clock >= 0) co_switch(cpu.thread);
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
}
void APU::set_irq_line() {

View File

@ -10,6 +10,7 @@ struct APU : Processor {
uint8 read(uint16 addr);
void write(uint16 addr, uint8 data);
void serialize(serializer&);
APU();
struct Envelope {
@ -23,6 +24,7 @@ struct APU : Processor {
unsigned volume() const;
void clock();
void serialize(serializer&);
};
struct Sweep {
@ -36,6 +38,7 @@ struct APU : Processor {
bool check_period();
void clock(unsigned channel);
void serialize(serializer&);
};
struct Rectangle {
@ -53,6 +56,7 @@ struct APU : Processor {
void clock_length();
bool check_period();
uint8 clock();
void serialize(serializer&);
} rectangle[2];
struct Triangle {
@ -71,6 +75,7 @@ struct APU : Processor {
void clock_length();
void clock_linear_length();
uint8 clock();
void serialize(serializer&);
} triangle;
struct Noise {
@ -86,6 +91,7 @@ struct APU : Processor {
void clock_length();
uint8 clock();
void serialize(serializer&);
} noise;
struct DMC {
@ -114,6 +120,7 @@ struct APU : Processor {
void start();
uint8 clock();
void serialize(serializer&);
} dmc;
struct FrameCounter {
@ -124,6 +131,8 @@ struct APU : Processor {
uint2 mode;
uint2 counter;
signed divider;
void serialize(serializer&);
} frame;
void clock_frame_counter();

103
bsnes/nes/apu/serialization.cpp Executable file
View File

@ -0,0 +1,103 @@
void APU::serialize(serializer &s) {
Processor::serialize(s);
rectangle[0].serialize(s);
rectangle[1].serialize(s);
triangle.serialize(s);
dmc.serialize(s);
frame.serialize(s);
s.integer(enabled_channels);
}
void APU::Envelope::serialize(serializer &s) {
s.integer(speed);
s.integer(use_speed_as_volume);
s.integer(loop_mode);
s.integer(reload_decay);
s.integer(decay_counter);
s.integer(decay_volume);
}
void APU::Sweep::serialize(serializer &s) {
s.integer(shift);
s.integer(decrement);
s.integer(period);
s.integer(counter);
s.integer(enable);
s.integer(reload);
s.integer(rectangle_period);
}
void APU::Rectangle::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
sweep.serialize(s);
s.integer(duty);
s.integer(duty_counter);
s.integer(period);
s.integer(period_counter);
}
void APU::Triangle::serialize(serializer &s) {
s.integer(length_counter);
s.integer(linear_length);
s.integer(halt_length_counter);
s.integer(period);
s.integer(period_counter);
s.integer(step_counter);
s.integer(linear_length_counter);
s.integer(reload_linear);
}
void APU::Noise::serialize(serializer &s) {
s.integer(length_counter);
envelope.serialize(s);
s.integer(period);
s.integer(period_counter);
s.integer(short_mode);
s.integer(lfsr);
}
void APU::DMC::serialize(serializer &s) {
s.integer(length_counter);
s.integer(irq_pending);
s.integer(period);
s.integer(period_counter);
s.integer(irq_enable);
s.integer(loop_mode);
s.integer(dac_latch);
s.integer(addr_latch);
s.integer(length_latch);
s.integer(read_addr);
s.integer(dma_delay_counter);
s.integer(bit_counter);
s.integer(have_dma_buffer);
s.integer(dma_buffer);
s.integer(have_sample);
s.integer(sample);
}
void APU::FrameCounter::serialize(serializer &s) {
s.integer(irq_pending);
s.integer(mode);
s.integer(counter);
s.integer(divider);
}

View File

@ -36,6 +36,7 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
case 16: mapper = &Mapper::bandaiFCG; break;
}
system.load();
loaded = true;
sha256 = nall::sha256(rom_data, rom_size);
}
@ -94,4 +95,10 @@ void Cartridge::ciram_write(uint13 addr, uint8 data) {
return mapper->ciram_write(addr, data);
}
void Cartridge::serialize(serializer &s) {
if(chr_ram) s.array(chr_data, chr_size);
return mapper->serialize(s);
}
}

View File

@ -11,6 +11,7 @@ struct Cartridge : property<Cartridge> {
readonly<bool> loaded;
readonly<string> sha256;
void serialize(serializer&);
Cartridge();
//privileged:

View File

@ -190,8 +190,7 @@ void CPU::op_exec() {
regs.pc--;
print("Unimplemented opcode: ", hex<4>(regs.pc), " = ", hex<2>(bus.read(regs.pc)), "\n");
print("Counter = ", opcodeCounter, "\n");
while(true) scheduler.exit();
while(true) scheduler.exit(Scheduler::ExitReason::UnknownEvent);
}
#undef I

View File

@ -2,10 +2,9 @@
namespace NES {
static unsigned opcodeCounter = 0;
#include "core/core.cpp"
#include "memory/memory.cpp"
#include "serialization.cpp"
CPU cpu;
void CPU::Main() {
@ -17,6 +16,10 @@ void CPU::main() {
unsigned lpc = 0xffff;
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(status.interrupt_pending) {
interrupt();
continue;
@ -28,16 +31,15 @@ void CPU::main() {
}
op_exec();
opcodeCounter++;
}
}
void CPU::add_clocks(unsigned clocks) {
apu.clock -= clocks;
if(apu.clock < 0) co_switch(apu.thread);
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread);
if(ppu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(ppu.thread);
}
void CPU::power() {

View File

@ -40,6 +40,9 @@ struct CPU : Processor {
void oam_dma();
void serialize(serializer&);
//internal:
bool trace;
};

41
bsnes/nes/cpu/serialization.cpp Executable file
View File

@ -0,0 +1,41 @@
void CPU::serialize(serializer &s) {
Processor::serialize(s);
s.array(ram);
s.integer(regs.mdr);
s.integer(regs.pc);
s.integer(regs.a);
s.integer(regs.x);
s.integer(regs.y);
s.integer(regs.s);
s.integer(regs.p.n);
s.integer(regs.p.v);
s.integer(regs.p.d);
s.integer(regs.p.i);
s.integer(regs.p.z);
s.integer(regs.p.c);
s.integer(abs.w);
s.integer(iabs.w);
s.integer(rd);
s.integer(zp);
s.integer(aa);
s.integer(status.interrupt_pending);
s.integer(status.nmi_pending);
s.integer(status.nmi_line);
s.integer(status.irq_line);
s.integer(status.irq_apu_line);
s.integer(status.rdy_line);
s.integer(status.rdy_addr.valid);
s.integer(status.rdy_addr.value);
s.integer(status.oam_dma_pending);
s.integer(status.oam_dma_page);
s.integer(status.controller_latch);
s.integer(status.controller_port0);
s.integer(status.controller_port1);
}

View File

@ -2,6 +2,7 @@
namespace NES {
#include "serialization.cpp"
Input input;
void Input::latch(bool data) {

View File

@ -11,6 +11,8 @@ struct Input {
void power();
void reset();
void serialize(serializer &s);
private:
Device port1;
Device port2;

View File

@ -0,0 +1,8 @@
void Input::serialize(serializer &s) {
s.integer((unsigned&)port1);
s.integer((unsigned&)port2);
s.integer(latchdata);
s.integer(counter1);
s.integer(counter2);
}

View File

@ -58,6 +58,15 @@ void Interface::run() {
system.run();
}
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();
foreach(code, list) {

View File

@ -22,6 +22,9 @@ struct Interface {
virtual void reset();
virtual void run();
virtual serializer serialize();
virtual bool unserialize(serializer&);
virtual void setCheats(const lstring &list = lstring{});
virtual void message(const string &text);

View File

@ -41,3 +41,8 @@ void AOROM::reset() {
prg_bank = 0x0f;
mirror_select = 0;
}
void AOROM::serialize(serializer &s) {
s.integer(prg_bank);
s.integer(mirror_select);
}

View File

@ -11,6 +11,8 @@ struct AOROM : Mapper {
void power();
void reset();
void serialize(serializer&);
private:
uint4 prg_bank;
bool mirror_select;

View File

@ -103,3 +103,14 @@ void BandaiFCG::clock() {
}
}
}
//
void BandaiFCG::serialize(serializer &s) {
s.array(chr_bank);
s.integer(prg_bank);
s.integer(mirror_select);
s.integer(irq_counter_enable);
s.integer(irq_counter);
s.integer(irq_latch);
}

View File

@ -11,6 +11,8 @@ struct BandaiFCG : Mapper {
void power();
void reset();
void serialize(serializer&);
private:
unsigned ciram_addr(unsigned addr) const;
void clock();

View File

@ -40,3 +40,7 @@ void CNROM::power() {
void CNROM::reset() {
chr_bank = 0;
}
void CNROM::serialize(serializer &s) {
s.integer(chr_bank);
}

View File

@ -11,6 +11,8 @@ struct CNROM : Mapper {
void power();
void reset();
void serialize(serializer&);
private:
uint2 chr_bank;
};

View File

@ -16,6 +16,8 @@ namespace Mapper {
virtual void power() = 0;
virtual void reset() = 0;
virtual void serialize(serializer&) = 0;
};
#include "none/none.hpp"

View File

@ -194,3 +194,13 @@ void MMC1::reset() {
r[2] = 0x01;
r[3] = 0x00;
}
//
void MMC1::serialize(serializer &s) {
s.array(prg_ram);
s.array(r);
s.integer(prg_ex_select);
s.integer(shiftaddr);
s.integer(shiftdata);
}

View File

@ -14,6 +14,8 @@ struct MMC1 : Mapper {
void power();
void reset();
void serialize(serializer&);
private:
uint8 prg_ram[8192];
uint8 r[4];

View File

@ -37,3 +37,6 @@ void None::power() {
void None::reset() {
}
void None::serialize(serializer &s) {
}

View File

@ -10,6 +10,8 @@ struct None : Mapper {
void power();
void reset();
void serialize(serializer&);
};
extern None none;

View File

@ -4,6 +4,7 @@
namespace NES {
namespace Info {
static const char Name[] = "bnes";
static const unsigned SerializerVersion = 1;
}
}

View File

@ -2,6 +2,7 @@
namespace NES {
#include "serialization.cpp"
PPU ppu;
void PPU::Main() {
@ -10,6 +11,10 @@ void PPU::Main() {
void PPU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
raster_scanline();
}
}
@ -34,7 +39,7 @@ void PPU::scanline_edge() {
void PPU::frame_edge() {
status.field ^= 1;
interface->videoRefresh(buffer);
scheduler.exit();
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void PPU::power() {

View File

@ -35,6 +35,8 @@ struct PPU : Processor {
void raster_sprite();
void raster_scanline();
void serialize(serializer&);
struct Status {
uint8 mdr;

72
bsnes/nes/ppu/serialization.cpp Executable file
View File

@ -0,0 +1,72 @@
void PPU::serialize(serializer &s) {
Processor::serialize(s);
s.integer(status.mdr);
s.integer(status.field);
s.integer(status.lx);
s.integer(status.ly);
s.integer(status.bus_data);
s.integer(status.address_latch);
s.integer(status.vaddr);
s.integer(status.taddr);
s.integer(status.xaddr);
s.integer(status.nmi_enable);
s.integer(status.master_select);
s.integer(status.sprite_size);
s.integer(status.bg_addr);
s.integer(status.sprite_addr);
s.integer(status.vram_increment);
s.integer(status.emphasis);
s.integer(status.sprite_enable);
s.integer(status.bg_enable);
s.integer(status.sprite_edge_enable);
s.integer(status.bg_edge_enable);
s.integer(status.grayscale);
s.integer(status.nmi);
s.integer(status.sprite_zero_hit);
s.integer(status.sprite_overflow);
s.integer(status.oam_addr);
s.integer(raster.nametable);
s.integer(raster.attribute);
s.integer(raster.tiledatalo);
s.integer(raster.tiledatahi);
s.integer(raster.oam_iterator);
s.integer(raster.oam_counter);
for(unsigned n = 0; n < 8; n++) {
s.integer(raster.oam[n].id);
s.integer(raster.oam[n].y);
s.integer(raster.oam[n].tile);
s.integer(raster.oam[n].attr);
s.integer(raster.oam[n].x);
s.integer(raster.oam[n].tiledatalo);
s.integer(raster.oam[n].tiledatahi);
}
for(unsigned n = 0; n < 8; n++) {
s.integer(raster.soam[n].id);
s.integer(raster.soam[n].y);
s.integer(raster.soam[n].tile);
s.integer(raster.soam[n].attr);
s.integer(raster.soam[n].x);
s.integer(raster.soam[n].tiledatalo);
s.integer(raster.soam[n].tiledatahi);
}
s.array(buffer);
s.array(ciram);
s.array(cgram);
s.array(oam);
}

View File

@ -9,7 +9,8 @@ void Scheduler::enter() {
co_switch(thread);
}
void Scheduler::exit() {
void Scheduler::exit(ExitReason reason) {
exit_reason = reason;
thread = co_active();
co_switch(host_thread);
}
@ -21,6 +22,8 @@ void Scheduler::power() {
void Scheduler::reset() {
host_thread = co_active();
thread = cpu.thread;
sync = SynchronizeMode::None;
exit_reason = ExitReason::UnknownEvent;
}
}

View File

@ -1,9 +1,13 @@
struct Scheduler {
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : unsigned { None, PPU, All } sync;
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason;
cothread_t host_thread; //program thread (used to exit emulation)
cothread_t thread; //active emulation thread (used to enter emulation)
void enter();
void exit();
void exit(ExitReason);
void power();
void reset();

View File

@ -0,0 +1,60 @@
serializer System::serialize() {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0;
char description[512];
memset(&description, 0, sizeof description);
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
return s;
}
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
//if(crc32 != 0) return false;
reset();
serialize_all(s);
return true;
}
void System::serialize(serializer &s) {
}
void System::serialize_all(serializer &s) {
system.serialize(s);
input.serialize(s);
cartridge.serialize(s);
cpu.serialize(s);
apu.serialize(s);
ppu.serialize(s);
}
void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
serialize_size = s.size();
}

View File

@ -2,12 +2,40 @@
namespace NES {
#include "serialization.cpp"
System system;
void System::run() {
scheduler.enter();
}
void System::runtosave() {
scheduler.sync = Scheduler::SynchronizeMode::PPU;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = cpu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = apu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent);
}
}
void System::load() {
serialize_init();
}
void System::power() {
cartridge.power();
cpu.power();

View File

@ -1,10 +1,22 @@
struct System {
void run();
void runtosave();
void runthreadtosave();
void load();
void power();
void reset();
void init();
void term();
serializer serialize();
bool unserialize(serializer&);
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();
unsigned serialize_size;
};
extern System system;

View File

@ -96,7 +96,7 @@ public:
if(name == Video::Shader) {
OpenGL::set_shader(any_cast<const char*>(value));
settings.filter = any_cast<unsigned>(OpenGL::shaderfilter);
settings.filter = OpenGL::fragmentfilter;
return true;
}

View File

@ -28,8 +28,8 @@ class OpenGL {
public:
GLuint gltexture;
GLuint glprogram;
unsigned shaderfilter;
GLuint fragmentshader;
unsigned fragmentfilter;
GLuint vertexshader;
bool shader_support;
@ -39,17 +39,16 @@ public:
void resize(unsigned width, unsigned height) {
if(iwidth >= width && iheight >= height) return;
if(gltexture) glDeleteTextures(1, &gltexture);
if(gltexture == 0) glGenTextures(1, &gltexture);
iwidth = max(width, iwidth );
iheight = max(height, iheight);
if(buffer) delete[] buffer;
buffer = new uint32_t[iwidth * iheight];
buffer = new uint32_t[iwidth * iheight]();
glGenTextures(1, &gltexture);
glBindTexture(GL_TEXTURE_2D, gltexture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth);
glTexImage2D(GL_TEXTURE_2D,
/* mip-map level = */ 0, /* internal format = */ GL_RGB,
/* mip-map level = */ 0, /* internal format = */ GL_RGBA,
iwidth, iheight, /* border = */ 0, /* format = */ GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
}
@ -140,7 +139,7 @@ public:
if(source) {
bool is_glsl = false;
shaderfilter = 0;
fragmentfilter = 0;
string fragment_source;
string vertex_source;
@ -149,10 +148,12 @@ public:
if(head.name == "shader") {
foreach(attribute, head.attribute) {
if(attribute.name == "language" && attribute.content == "GLSL") is_glsl = true;
if(attribute.name == "filter") shaderfilter = attribute.content == "linear" ? 1 : 0;
}
foreach(element, head.element) {
if(element.name == "fragment") {
foreach(attribute, element.attribute) {
if(attribute.name == "filter") fragmentfilter = attribute.content == "linear" ? 1 : 0;
}
fragment_source = element.parse();
} else if(element.name == "vertex") {
vertex_source = element.parse();
@ -239,8 +240,8 @@ public:
OpenGL() {
gltexture = 0;
glprogram = 0;
shaderfilter = 0;
fragmentshader = 0;
fragmentfilter = 0;
vertexshader = 0;
buffer = 0;

View File

@ -65,7 +65,7 @@ public:
if(name == Video::Shader) {
settings.shader = any_cast<const char*>(value);
OpenGL::set_shader(settings.shader);
settings.filter = any_cast<unsigned>(OpenGL::shaderfilter);
settings.filter = OpenGL::fragmentfilter;
return true;
}

View File

@ -13,7 +13,7 @@ void Audio::coprocessor_enable(bool state) {
void Audio::coprocessor_frequency(double input_frequency) {
dspaudio.setFrequency(input_frequency);
dspaudio.setResampler(nall::DSP::Resampler::Average);
dspaudio.setResampler(nall::DSP::ResampleEngine::Average);
dspaudio.setResamplerFrequency(system.apu_frequency() / 768.0);
}

View File

@ -1,110 +0,0 @@
include $(snes)/Makefile
include $(gameboy)/Makefile
ui_objects := ui-main ui-general ui-settings ui-tools ui-input ui-utility ui-path ui-cartridge ui-debugger
ui_objects += ruby phoenix
ui_objects += $(if $(call streq,$(platform),win),resource)
# platform
ifeq ($(platform),x)
ifeq ($(phoenix),gtk)
phoenix_compile = $(call compile,-DPHOENIX_GTK `pkg-config --cflags gtk+-2.0`)
link += `pkg-config --libs gtk+-2.0`
else
phoenix_compile = $(call compile,-DPHOENIX_QT `pkg-config --cflags QtCore QtGui`)
link += `pkg-config --libs QtCore QtGui`
endif
ruby := video.glx video.xv video.sdl
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.x
link += $(if $(findstring audio.openal,$(ruby)),-lopenal)
else ifeq ($(platform),osx)
phoenix_compile = $(call compile,-DPHOENIX_QT)
link +=
ruby :=
ruby += audio.openal
ruby += input.carbon
link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
else ifeq ($(platform),win)
phoenix_compile = $(call compile,-DPHOENIX_WINDOWS)
link +=
ruby := video.direct3d video.wgl video.directdraw video.gdi
ruby += audio.directsound audio.xaudio2
ruby += input.rawinput input.directinput
link += $(if $(findstring audio.openal,$(ruby)),-lopenal32)
endif
# ruby
rubyflags := $(if $(finstring .sdl,$(ruby)),`sdl-config --cflags`)
link += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
link += $(if $(findstring video.direct3d,$(ruby)),-ld3d9)
link += $(if $(findstring video.directdraw,$(ruby)),-lddraw)
link += $(if $(findstring video.glx,$(ruby)),-lGL)
link += $(if $(findstring video.wgl,$(ruby)),-lopengl32)
link += $(if $(findstring video.xv,$(ruby)),-lXv)
link += $(if $(findstring audio.alsa,$(ruby)),-lasound)
link += $(if $(findstring audio.ao,$(ruby)),-lao)
link += $(if $(findstring audio.directsound,$(ruby)),-ldsound)
link += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse)
link += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
link += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
link += $(if $(findstring input.directinput,$(ruby)),-ldinput8 -ldxguid)
link += $(if $(findstring input.rawinput,$(ruby)),-ldinput8 -ldxguid)
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
# rules
objects := $(ui_objects) $(objects)
objects := $(patsubst %,obj/%.o,$(objects))
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/*)
obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/general/*)
obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/tools/*)
obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/settings/*)
obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/input/*)
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/utility/*)
obj/ui-path.o: $(ui)/path/path.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/path/*)
obj/ui-cartridge.o: $(ui)/cartridge/cartridge.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/cartridge/*)
obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/debugger/*)
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
$(call compile,$(rubydef) $(rubyflags))
obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*)
$(phoenix_compile)
obj/resource.o: $(ui)/resource.rc
windres $(ui)/resource.rc obj/resource.o
# targets
build: $(objects)
ifeq ($(platform),osx)
test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS
$(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link))
else
$(strip $(cpp) -o out/bsnes $(objects) $(link))
endif
install:
ifeq ($(platform),x)
install -D -m 755 out/bsnes $(DESTDIR)$(prefix)/bin/bsnes
endif
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop
mkdir -p ~/.config/bsnes
cp data/cheats.xml ~/.config/bsnes/cheats.xml
chmod 777 ~/.config/bsnes ~/.config/bsnes/cheats.xml
uninstall:
ifeq ($(platform),x)
rm $(DESTDIR)$(prefix)/bin/bsnes
endif
rm $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png
rm $(DESTDIR)$(prefix)/share/applications/bsnes.desktop

View File

@ -1,65 +0,0 @@
#include <snes/snes.hpp>
#include <nall/base64.hpp>
#include <nall/bmp.hpp>
#include <nall/compositor.hpp>
#include <nall/config.hpp>
#include <nall/crc32.hpp>
#include <nall/directory.hpp>
#include <nall/dsp.hpp>
#include <nall/filemap.hpp>
#include <nall/input.hpp>
#include <nall/resource.hpp>
#include <nall/bps/patch.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gameboy/cartridge.hpp>
using namespace nall;
#include <ruby/ruby.hpp>
using namespace ruby;
#include <phoenix/phoenix.hpp>
using namespace phoenix;
struct TopLevelWindow : Window {
string name;
string position;
};
#include "interface.hpp"
#include "config.hpp"
#include "general/general.hpp"
#include "settings/settings.hpp"
#include "tools/tools.hpp"
#include "input/input.hpp"
#include "utility/utility.hpp"
#include "path/path.hpp"
#include "cartridge/cartridge.hpp"
#if defined(DEBUGGER)
#include "debugger/debugger.hpp"
#endif
struct Application {
string proportionalFont;
string proportionalFontBold;
string monospaceFont;
string titleFont;
bool compositorActive;
bool pause;
bool quit;
void main(int argc, char **argv);
void addWindow(TopLevelWindow *window, const string &name, const string &position);
Application();
private:
array<TopLevelWindow*> windows;
configuration geometryConfig;
void loadGeometry();
void saveGeometry();
};
extern nall::DSP dspaudio;
extern Application application;

View File

@ -1,175 +0,0 @@
#include "../base.hpp"
Cartridge cartridge;
bool Cartridge::loadNormal(const char *basename) {
unload();
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
baseName = nall::basename(basename);
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, { baseXML });
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
utility.cartridgeLoaded();
return true;
}
bool Cartridge::loadBsxSlotted(const char *basename, const char *slotname) {
unload();
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
loadCartridge(SNES::bsxflash.memory, bsxXML, slotname);
baseName = nall::basename(basename);
bsxName = nall::basename(slotname);
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, { baseXML, bsxXML });
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
utility.cartridgeLoaded();
return true;
}
bool Cartridge::loadBsx(const char *basename, const char *slotname) {
unload();
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
loadCartridge(SNES::bsxflash.memory, bsxXML, slotname);
baseName = nall::basename(basename);
bsxName = nall::basename(slotname);
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, { baseXML, bsxXML });
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
utility.cartridgeLoaded();
return true;
}
bool Cartridge::loadSufamiTurbo(const char *basename, const char *slotAname, const char *slotBname) {
unload();
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
loadCartridge(SNES::sufamiturbo.slotA.rom, sufamiTurboAXML, slotAname);
loadCartridge(SNES::sufamiturbo.slotB.rom, sufamiTurboBXML, slotBname);
baseName = nall::basename(basename);
sufamiTurboAName = nall::basename(slotAname);
sufamiTurboBName = nall::basename(slotBname);
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, { baseXML, sufamiTurboAXML, sufamiTurboBXML });
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
utility.cartridgeLoaded();
return true;
}
bool Cartridge::loadSuperGameBoy(const char *basename, const char *slotname) {
unload();
if(loadCartridge(SNES::cartridge.rom, baseXML, basename) == false) return false;
unsigned size = 0;
uint8_t *data = 0;
file fp;
if(fp.open(slotname, file::mode::read)) {
data = new uint8_t[size = fp.size()];
fp.read(data, size);
fp.close();
}
//note: it is safe to pass below two functions null pointers
GameBoyCartridge info(data, size);
GameBoy::cartridge.load(info.xml, data, size);
if(data) delete[] data;
baseName = nall::basename(basename);
gameBoyName = nall::basename(slotname);
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { baseXML, "" });
foreach(memory, SNES::cartridge.nvram) loadMemory(memory);
if(GameBoy::cartridge.info.battery && fp.open(path.load(SNES::Cartridge::Slot::GameBoy, ".sav"), file::mode::read)) {
fp.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, fp.size()));
fp.close();
}
utility.cartridgeLoaded();
return true;
}
void Cartridge::unload() {
patch.applied = false;
patch.information = "";
if(SNES::cartridge.loaded() == false) return;
foreach(memory, SNES::cartridge.nvram) saveMemory(memory);
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
file fp;
if(GameBoy::cartridge.info.battery && fp.open(path.load(SNES::Cartridge::Slot::GameBoy, ".sav"), file::mode::write)) {
fp.write(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
fp.close();
}
}
utility.cartridgeUnloaded();
baseName = bsxName = sufamiTurboAName = sufamiTurboBName = gameBoyName = "";
}
bool Cartridge::loadCartridge(SNES::MappedRAM &memory, string &XML, const char *filename) {
if(file::exists(filename) == false) return false;
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
if(XML.readfile({ nall::basename(filename), ".xml" }) == false) XML = "";
unsigned size = fp.size();
uint8_t *data = new uint8_t[size];
fp.read(data, size);
fp.close();
string patchName = { nall::basename(filename), ".bps" };
if(file::exists(patchName)) {
bpspatch bps;
bps.modify(patchName);
unsigned targetSize = bps.size();
uint8_t *targetData = new uint8_t[targetSize];
bps.source(data, size);
bps.target(targetData, targetSize);
if(bps.apply() == bpspatch::result::success) {
delete[] data;
data = targetData;
size = targetSize;
patch.applied = true;
xml_element document = xml_parse(bps.metadata());
foreach(root, document.element) {
if(root.name == "metadata") {
if(auto x = root.content.position("<information>")) {
if(auto y = root.content.position("</information>")) {
patch.information = substr(root.content, x(), y() - x() + 14);
}
}
if(auto x = root.content.position("<cartridge ")) {
if(auto y = root.content.position("</cartridge>")) {
XML = substr(root.content, x(), y() - x() + 12);
}
}
}
}
} else {
delete[] targetData;
}
}
if(XML == "") XML = SNESCartridge(data, size).xmlMemoryMap;
memory.copy(data, size);
delete[] data;
return true;
}
bool Cartridge::loadMemory(SNES::Cartridge::NonVolatileRAM &memory) {
if(memory.size == 0) return true;
string filename = path.load(memory.slot, memory.id);
file fp;
if(fp.open(filename, file::mode::read) == false) return false;
fp.read(memory.data, min(memory.size, fp.size()));
fp.close();
return true;
}
bool Cartridge::saveMemory(SNES::Cartridge::NonVolatileRAM &memory) {
if(memory.size == 0) return true;
string filename = path.load(memory.slot, memory.id);
file fp;
if(fp.open(filename, file::mode::write) == false) return false;
fp.write(memory.data, memory.size);
fp.close();
return true;
}

View File

@ -1,23 +0,0 @@
struct Cartridge {
bool loadNormal(const char *basename);
bool loadBsxSlotted(const char *basename, const char *slotname);
bool loadBsx(const char *basename, const char *slotname);
bool loadSufamiTurbo(const char *basename, const char *slotAname, const char *slotBname);
bool loadSuperGameBoy(const char *basename, const char *slotname);
void unload();
string baseName, bsxName, sufamiTurboAName, sufamiTurboBName, gameBoyName;
string baseXML, bsxXML, sufamiTurboAXML, sufamiTurboBXML, gameBoyXML;
struct Patch {
bool applied;
string information;
} patch;
private:
bool loadCartridge(SNES::MappedRAM &memory, string &XML, const char *filename);
bool loadMemory(SNES::Cartridge::NonVolatileRAM &memory);
bool saveMemory(SNES::Cartridge::NonVolatileRAM &memory);
};
extern Cartridge cartridge;

View File

@ -1,53 +0,0 @@
Configuration config;
void Configuration::load() {
configuration::load(::path.home("bsnes.cfg"));
}
void Configuration::save() {
configuration::save(::path.home("bsnes.cfg"));
}
void Configuration::create() {
attach(SNES::config.random, "snes.random");
attach(SNES::config.cpu.version, "snes.cpu.version");
attach(SNES::config.cpu.ntsc_frequency, "snes.cpu.ntscFrequency");
attach(SNES::config.cpu.pal_frequency, "snes.cpu.palFrequency");
attach(SNES::config.smp.ntsc_frequency, "snes.smp.ntscFrequency");
attach(SNES::config.smp.pal_frequency, "snes.smp.palFrequency");
attach(SNES::config.ppu1.version, "snes.ppu1.version");
attach(SNES::config.ppu2.version, "snes.ppu2.version");
attach(SNES::config.superfx.speed, "snes.superfx.speed");
attach(video.driver = "", "video.driver");
attach(video.synchronize = false, "video.synchronize");
attach(video.smooth = true, "video.smooth");
attach(video.filter = "", "video.filter");
attach(video.shader = "", "video.shader");
attach(video.region = 0, "video.region");
attach(video.scale = 2, "video.scale");
attach(video.fullscreenScale = 0, "video.fullscreenScale");
attach(video.aspectRatioCorrection = true, "video.aspectRatioCorrection");
attach(video.brightness = 100, "video.brightness");
attach(video.contrast = 100, "video.contrast");
attach(video.gamma = 100, "video.gamma");
attach(video.useGammaRamp = true, "video.useGammaRamp");
attach(audio.driver = "", "audio.driver");
attach(audio.synchronize = true, "audio.synchronize");
attach(audio.mute = false, "audio.mute");
attach(audio.volume = 100, "audio.volume");
attach(audio.balance = 100, "audio.balance");
attach(audio.latency = 60, "audio.latency");
attach(audio.inputFrequency = 32000, "audio.inputFrequency");
attach(audio.outputFrequency = 44100, "audio.outputFrequency");
attach(input.driver = "", "input.driver");
attach(settings.startFullScreen = false, "settings.startFullScreen", "Start in full screen mode for front-end use");
attach(settings.focusPolicy = 0, "settings.focusPolicy");
attach(settings.compositorPolicy = 1, "settings.compositorPolicy");
attach(controller.port1 = 1, "controller.port1");
attach(controller.port2 = 1, "controller.port2");
}

View File

@ -1,49 +0,0 @@
struct Configuration : public configuration {
struct Video {
string driver;
bool synchronize;
bool smooth;
string filter;
string shader;
bool region;
unsigned scale;
unsigned fullscreenScale;
bool aspectRatioCorrection;
unsigned brightness;
unsigned contrast;
unsigned gamma;
bool useGammaRamp;
} video;
struct Audio {
string driver;
bool synchronize;
bool mute;
unsigned volume;
unsigned balance;
unsigned latency;
unsigned inputFrequency;
unsigned outputFrequency;
} audio;
struct Input {
string driver;
} input;
struct Settings {
bool startFullScreen;
unsigned focusPolicy;
unsigned compositorPolicy;
} settings;
struct Controller {
unsigned port1;
unsigned port2;
} controller;
void load();
void save();
void create();
};
extern Configuration config;

View File

@ -1,91 +0,0 @@
Console console;
void Console::create() {
setTitle("Console");
application.addWindow(this, "Debugger.Console", "192,192");
output.setFont(application.monospaceFont);
output.setEditable(false);
traceToConsole.setText("Trace to console");
traceToFile.setText("Trace to file");
traceCPU.setText("Trace CPU");
traceSMP.setText("Trace SMP");
traceToConsole.setChecked(true);
traceCPU.setChecked(true);
clearConsole.setText("Clear console");
layout.setMargin(5);
layout.append(output, ~0, ~0, 5);
controlLayout.append(traceToConsole, 120, 0 );
controlLayout.append(traceToFile, 120, 0 );
controlLayout.append(traceCPU, 120, 0 );
controlLayout.append(traceSMP, 120, 0 );
controlLayout.append(spacer, 120, ~0 );
controlLayout.append(clearConsole, 120, 0 );
layout.append(controlLayout, 0, ~0 );
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width + 585, 350 });
onClose = []() {
debugger.showConsole.setChecked(false);
};
traceToFile.onTick = []() { console.tracerEnable(console.traceToFile.checked()); };
clearConsole.onTick = []() {
console.buffer = "";
console.output.setText(console.buffer);
};
}
void Console::write(const string &text, bool echo) {
if(traceToConsole.checked() || echo) {
if(buffer != "") buffer.append("\n");
buffer.append(text);
output.setText(buffer);
output.setCursorPosition(~0);
OS::processEvents();
}
if(traceToFile.checked() && logfile.open()) {
logfile.print(text, "\n");
}
}
void Console::tracerEnable(bool state) {
if(state == true) {
logfile.open(path.load(SNES::Cartridge::Slot::Base, ".log"), file::mode::write);
} else {
logfile.close();
}
}
void Console::eventBreakpoint() {
unsigned n = SNES::debugger.breakpoint_hit;
write({ "Breakpoint ", n + 1, " hit." }, true);
if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::CPUBus) {
eventTraceCPU();
cpuDebugger.refreshDisassembly();
} else if(SNES::debugger.breakpoint[n].source == SNES::Debugger::Breakpoint::Source::APURAM) {
eventTraceSMP();
smpDebugger.refreshDisassembly();
}
}
void Console::eventTraceCPU() {
if(traceCPU.checked() == false) return;
if(traceToConsole.checked() == false && traceToFile.checked() == false) return;
char text[256];
SNES::cpu.disassemble_opcode(text, SNES::cpu.regs.pc);
write(text);
}
void Console::eventTraceSMP() {
if(traceSMP.checked() == false) return;
if(traceToConsole.checked() == false && traceToFile.checked() == false) return;
char text[256];
SNES::smp.disassemble_opcode(text, SNES::smp.regs.pc);
write(text);
}

View File

@ -1,24 +0,0 @@
struct Console : TopLevelWindow {
HorizontalLayout layout;
TextEdit output;
VerticalLayout controlLayout;
CheckBox traceToConsole;
CheckBox traceToFile;
CheckBox traceCPU;
CheckBox traceSMP;
Label spacer;
Button clearConsole;
string buffer;
file logfile;
void create();
void write(const string &text, bool echo = false);
void tracerEnable(bool state);
void eventBreakpoint();
void eventTraceCPU();
void eventTraceSMP();
};
extern Console console;

View File

@ -1,111 +0,0 @@
CPUDebugger cpuDebugger;
void CPUDebugger::create() {
setTitle("CPU Debugger");
application.addWindow(this, "Debugger.CPUdebugger", "192,192");
output.setFont(application.monospaceFont);
output.setEditable(false);
stepInto.setText("Step Into");
stepOver.setText("Step Over");
proceed.setText("Proceed");
proceed.setEnabled(false);
layout.setMargin(5);
layout.append(output, ~0, ~0, 5);
controlLayout.append(stepInto, 80, 0 );
controlLayout.append(stepOver, 80, 0 );
controlLayout.append(proceed, 80, 0 );
controlLayout.append(spacer, 80, ~0 );
layout.append(controlLayout, 0, ~0 );
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width + 300, 220 });
onClose = []() {
debugger.showCPUDebugger.setChecked(false);
};
stepInto.onTick = []() {
debugger.debugMode = Debugger::DebugMode::StepIntoCPU;
};
stepOver.onTick = { &CPUDebugger::eventStepOver, this };
}
void CPUDebugger::synchronize() {
stepInto.setEnabled(SNES::cartridge.loaded() && debugger.enableDebugger.checked() && breakpointEditor.runToBreakpoint.checked() == false);
stepOver.setEnabled(stepInto.enabled() && SNES::cpu.opcode_edge);
}
void CPUDebugger::refreshDisassembly() {
unsigned addr = SNES::cpu.regs.pc;
uint8_t *usage = SNES::cpu.usage;
signed offset[15];
foreach(n, offset) n = -1;
offset[7] = addr;
//reverse disassembly
for(signed n = 6; n >= 0; n--) {
signed base = offset[n + 1];
if(base == -1) break;
for(unsigned r = 1; r <= 4; r++) {
if(usage[(base - r) & 0xffffff] & 0x20) {
offset[n] = base - r;
break;
}
}
}
//forward disassembly
for(signed n = 8; n <= 14; n++) {
signed base = offset[n - 1];
if(base == -1) break;
for(unsigned r = 1; r <= 4; r++) {
if(usage[(base + r) & 0xffffff] & 0x20) {
offset[n] = base + r;
break;
}
}
}
string buffer;
for(unsigned n = 0; n < 15; n++) {
buffer.append(n == 7 ? "> " : " ");
if(offset[n] == -1) {
buffer.append("...\n");
} else {
unsigned addr = offset[n];
buffer.append(hex<6>(addr));
buffer.append(" ");
string text = SNESCPU::disassemble(
addr, usage[addr] & 2, usage[addr] & 1,
read(addr + 0), read(addr + 1), read(addr + 2), read(addr + 3)
);
buffer.append(text);
buffer.append("\n");
}
}
buffer.rtrim<1>("\n");
output.setText(buffer);
}
void CPUDebugger::eventStepInto() {
refreshDisassembly();
}
void CPUDebugger::eventStepOver() {
uint8_t opcode = read(SNES::cpu.regs.pc);
unsigned length = SNESCPU::getOpcodeLength(SNES::cpu.regs.p.m, SNES::cpu.regs.p.x, opcode);
SNES::cpu.regs.pc += length;
refreshDisassembly();
console.eventTraceCPU();
}
uint8_t CPUDebugger::read(unsigned addr) {
return SNES::debugger.read(SNES::Debugger::MemorySource::CPUBus, addr);
}

View File

@ -1,19 +0,0 @@
struct CPUDebugger : TopLevelWindow {
HorizontalLayout layout;
TextEdit output;
VerticalLayout controlLayout;
Button stepInto;
Button stepOver;
Button proceed;
Widget spacer;
void create();
void synchronize();
void refreshDisassembly();
void eventStepInto();
void eventStepOver();
uint8_t read(unsigned addr);
};
extern CPUDebugger cpuDebugger;

View File

@ -1,152 +0,0 @@
#include "../base.hpp"
#if defined(DEBUGGER)
#include <nall/snes/cpu.hpp>
#include <nall/snes/smp.hpp>
#include "console.cpp"
#include "cpu/debugger.cpp"
#include "smp/debugger.cpp"
#include "tools/breakpoint-editor.cpp"
#include "tools/memory-editor.cpp"
Debugger debugger;
void Debugger::create() {
console.create();
cpuDebugger.create();
smpDebugger.create();
breakpointEditor.create();
memoryEditor.create();
setTitle("Debugger");
application.addWindow(this, "Debugger", "160,160");
enableDebugger.setText("Enable debugger");
showConsole.setText("Console");
showCPUDebugger.setText("CPU debugger");
showSMPDebugger.setText("SMP debugger");
showBreakpointEditor.setText("Breakpoint editor");
showMemoryEditor.setText("Memory editor");
layout.setMargin(5);
layout.append(enableDebugger, ~0, 0);
layout.append(showConsole, ~0, 0);
layout.append(showCPUDebugger, ~0, 0);
layout.append(showSMPDebugger, ~0, 0);
layout.append(showBreakpointEditor, ~0, 0);
layout.append(showMemoryEditor, ~0, 0);
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width, layout.minimumGeometry().height });
//windows shown by default
showConsole.setChecked();
showCPUDebugger.setChecked();
showSMPDebugger.setChecked();
showBreakpointEditor.setChecked();
enableDebugger.onTick = []() {
debugger.enable(debugger.enableDebugger.checked());
};
showConsole.onTick = []() {
console.setVisible(debugger.showConsole.checked());
};
showCPUDebugger.onTick = []() {
cpuDebugger.setVisible(debugger.showCPUDebugger.checked());
};
showSMPDebugger.onTick = []() {
smpDebugger.setVisible(debugger.showSMPDebugger.checked());
};
showBreakpointEditor.onTick = []() {
breakpointEditor.setVisible(debugger.showBreakpointEditor.checked());
};
showMemoryEditor.onTick = []() {
memoryEditor.setVisible(debugger.showMemoryEditor.checked());
};
onClose = []() {
debugger.enable(false);
};
synchronize();
}
void Debugger::synchronize() {
cpuDebugger.synchronize();
smpDebugger.synchronize();
}
void Debugger::setVisible(bool visible) {
Window::setVisible(visible);
console.setVisible(showConsole.checked() & visible);
cpuDebugger.setVisible(showCPUDebugger.checked() & visible);
smpDebugger.setVisible(showSMPDebugger.checked() & visible);
breakpointEditor.setVisible(showBreakpointEditor.checked() & visible);
memoryEditor.setVisible(showMemoryEditor.checked() & visible);
}
void Debugger::enable(bool state) {
enableDebugger.setChecked(state);
synchronize();
}
void Debugger::run() {
if(enableDebugger.checked() == false) {
SNES::system.run();
return;
}
if(debugMode == DebugMode::None) {
usleep(20 * 1000);
return;
}
SNES::system.run();
if(debugMode == DebugMode::WaitForBreakpoint) {
if(SNES::debugger.break_event == SNES::Debugger::BreakEvent::BreakpointHit) {
debugMode = DebugMode::None;
console.eventBreakpoint();
breakpointEditor.eventBreakpoint();
synchronize();
}
}
SNES::debugger.break_event = SNES::Debugger::BreakEvent::None;
}
bool Debugger::step_cpu() {
if(enableDebugger.checked() == false) return false;
console.eventTraceCPU();
if(debugMode == DebugMode::StepIntoCPU) {
debugMode = DebugMode::None;
cpuDebugger.eventStepInto();
synchronize();
return true;
}
return false;
}
bool Debugger::step_smp() {
if(enableDebugger.checked() == false) return false;
console.eventTraceSMP();
if(debugMode == DebugMode::StepIntoSMP) {
debugMode = DebugMode::None;
smpDebugger.eventStepInto();
synchronize();
return true;
}
return false;
}
Debugger::Debugger() {
debugMode = DebugMode::None;
SNES::cpu.step_event = { &Debugger::step_cpu, this };
SNES::smp.step_event = { &Debugger::step_smp, this };
}
#endif

View File

@ -1,34 +0,0 @@
#include "console.hpp"
#include "cpu/debugger.hpp"
#include "smp/debugger.hpp"
#include "tools/breakpoint-editor.hpp"
#include "tools/memory-editor.hpp"
struct Debugger : TopLevelWindow {
enum class DebugMode : unsigned {
None,
WaitForBreakpoint,
StepIntoCPU,
StepIntoSMP,
} debugMode;
VerticalLayout layout;
CheckBox enableDebugger;
CheckBox showConsole;
CheckBox showCPUDebugger;
CheckBox showSMPDebugger;
CheckBox showBreakpointEditor;
CheckBox showMemoryEditor;
void create();
void synchronize();
void setVisible(bool visible = true);
void enable(bool state);
void run();
bool step_cpu();
bool step_smp();
Debugger();
};
extern Debugger debugger;

View File

@ -1,110 +0,0 @@
SMPDebugger smpDebugger;
void SMPDebugger::create() {
setTitle("SMP Debugger");
application.addWindow(this, "Debugger.SMPDebugger", "192,192");
output.setFont(application.monospaceFont);
output.setEditable(false);
stepInto.setText("Step Into");
stepOver.setText("Step Over");
proceed.setText("Proceed");
proceed.setEnabled(false);
layout.setMargin(5);
layout.append(output, ~0, ~0, 5);
controlLayout.append(stepInto, 80, 0 );
controlLayout.append(stepOver, 80, 0 );
controlLayout.append(proceed, 80, 0 );
controlLayout.append(spacer, 80, ~0 );
layout.append(controlLayout, 0, ~0 );
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width + 300, 220 });
onClose = []() {
debugger.showSMPDebugger.setChecked(false);
};
stepInto.onTick = []() {
debugger.debugMode = Debugger::DebugMode::StepIntoSMP;
};
stepOver.onTick = { &SMPDebugger::eventStepOver, this };
}
void SMPDebugger::synchronize() {
stepInto.setEnabled(SNES::cartridge.loaded() && debugger.enableDebugger.checked() && breakpointEditor.runToBreakpoint.checked() == false);
stepOver.setEnabled(stepInto.enabled() && SNES::smp.opcode_edge);
}
void SMPDebugger::refreshDisassembly() {
uint16_t addr = SNES::smp.regs.pc;
uint8_t *usage = SNES::smp.usage;
signed offset[15];
foreach(n, offset) n = -1;
offset[7] = addr;
//reverse disassembly
for(signed n = 6; n >= 0; n--) {
signed base = offset[n + 1];
if(base == -1) break;
for(unsigned r = 1; r <= 3; r++) {
if(usage[(base - r) & 0xffff] & 0x20) {
offset[n] = base - r;
break;
}
}
}
//forward disassembly
for(signed n = 8; n <= 14; n++) {
signed base = offset[n - 1];
if(base == -1) break;
for(unsigned r = 1; r <= 3; r++) {
if(usage[(base + r) & 0xffff] & 0x20) {
offset[n] = base + r;
break;
}
}
}
string buffer;
for(unsigned n = 0; n < 15; n++) {
buffer.append(n == 7 ? "> " : " ");
if(offset[n] == -1) {
buffer.append("...\n");
} else {
uint16_t addr = offset[n];
buffer.append(hex<4>(addr));
buffer.append(" ");
string text = SNESSMP::disassemble(
addr, read(addr + 0), read(addr + 1), read(addr + 2)
);
buffer.append(text);
buffer.append("\n");
}
}
buffer.rtrim<1>("\n");
output.setText(buffer);
}
void SMPDebugger::eventStepInto() {
refreshDisassembly();
}
void SMPDebugger::eventStepOver() {
uint8_t opcode = read(SNES::smp.regs.pc);
unsigned length = SNESSMP::getOpcodeLength(opcode);
SNES::smp.regs.pc += length;
refreshDisassembly();
console.eventTraceSMP();
}
uint8_t SMPDebugger::read(uint16_t addr) {
return SNES::debugger.read(SNES::Debugger::MemorySource::APUBus, addr);
}

View File

@ -1,19 +0,0 @@
struct SMPDebugger : TopLevelWindow {
HorizontalLayout layout;
TextEdit output;
VerticalLayout controlLayout;
Button stepInto;
Button stepOver;
Button proceed;
Widget spacer;
void create();
void synchronize();
void refreshDisassembly();
void eventStepInto();
void eventStepOver();
uint8_t read(uint16_t addr);
};
extern SMPDebugger smpDebugger;

View File

@ -1,72 +0,0 @@
BreakpointEditor breakpointEditor;
void BreakpointEditor::create() {
setTitle("Breakpoint Editor");
application.addWindow(this, "Debugger.BreakpointEditor", "192,192");
runToBreakpoint.setText("Run to breakpoint");
for(unsigned n = 0; n < Breakpoints; n++) {
enableBox[n].setText({ n + 1 });
typeBox[n].append("Exec");
typeBox[n].append("Read");
typeBox[n].append("Write");
sourceBox[n].append("CPU");
sourceBox[n].append("APU");
sourceBox[n].append("VRAM");
sourceBox[n].append("OAM");
sourceBox[n].append("CGRAM");
enableBox[n].onTick = [n]() { breakpointEditor.toggleBreakpoint(n); };
}
layout.setMargin(5);
layout.append(runToBreakpoint, ~0, 0, 5);
for(unsigned n = 0; n < Breakpoints; n++) {
breakpointLayout[n].append(enableBox[n], 35, 0 );
breakpointLayout[n].append(addressBox[n], 60, 0, 5);
breakpointLayout[n].append(valueBox[n], 30, 0, 5);
breakpointLayout[n].append(typeBox[n], 0, 0, 5);
breakpointLayout[n].append(sourceBox[n], 0, 0 );
layout.append(breakpointLayout[n], ~0, 0, n < Breakpoints - 1 ? 5 : 0);
}
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width, layout.minimumGeometry().height });
onClose = []() {
debugger.showBreakpointEditor.setChecked(false);
};
runToBreakpoint.onTick = []() {
if(breakpointEditor.runToBreakpoint.checked()) {
debugger.debugMode = Debugger::DebugMode::WaitForBreakpoint;
} else {
debugger.debugMode = Debugger::DebugMode::None;
}
debugger.synchronize();
};
}
void BreakpointEditor::toggleBreakpoint(unsigned n) {
bool enabled = enableBox[n].checked();
if(enabled == false) {
SNES::debugger.breakpoint[n].enabled = false;
} else {
SNES::debugger.breakpoint[n].enabled = true;
SNES::debugger.breakpoint[n].addr = hex(addressBox[n].text());
SNES::debugger.breakpoint[n].data = hex(valueBox[n].text());
if(valueBox[n].text() == "") SNES::debugger.breakpoint[n].data = -1; //break on any value
SNES::debugger.breakpoint[n].mode = (SNES::Debugger::Breakpoint::Mode)typeBox[n].selection();
SNES::debugger.breakpoint[n].source = (SNES::Debugger::Breakpoint::Source)sourceBox[n].selection();
SNES::debugger.breakpoint[n].counter = 0;
}
//do not allow values to be edited while breakpoint is enabled
addressBox[n].setEnabled(!enabled);
valueBox[n].setEnabled(!enabled);
typeBox[n].setEnabled(!enabled);
sourceBox[n].setEnabled(!enabled);
}
void BreakpointEditor::eventBreakpoint() {
runToBreakpoint.setChecked(false);
}

View File

@ -1,17 +0,0 @@
struct BreakpointEditor : TopLevelWindow {
enum : unsigned { Breakpoints = SNES::Debugger::Breakpoints };
VerticalLayout layout;
CheckBox runToBreakpoint;
HorizontalLayout breakpointLayout[Breakpoints];
CheckBox enableBox[Breakpoints];
LineEdit addressBox[Breakpoints];
LineEdit valueBox[Breakpoints];
ComboBox typeBox[Breakpoints];
ComboBox sourceBox[Breakpoints];
void create();
void toggleBreakpoint(unsigned breakpoint);
void eventBreakpoint();
};
extern BreakpointEditor breakpointEditor;

View File

@ -1,78 +0,0 @@
MemoryEditor memoryEditor;
void MemoryEditor::create() {
setTitle("Memory Editor");
application.addWindow(this, "Debugger.MemoryEditor", "192,192");
editor.setFont(application.monospaceFont);
editor.setColumns(16);
editor.setRows(16);
sourceBox.append("CPU");
sourceBox.append("APU");
sourceBox.append("VRAM");
sourceBox.append("OAM");
sourceBox.append("CGRAM");
refreshButton.setText("Refresh");
layout.setMargin(5);
layout.append(editor, ~0, ~0, 5);
controlLayout.append(sourceBox, 80, 0 );
controlLayout.append(gotoBox, 80, 0 );
controlLayout.append(refreshButton, 80, 0 );
controlLayout.append(spacer, 80, ~0 );
layout.append(controlLayout, 0, ~0 );
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width + 475, 230 });
onClose = []() {
debugger.showMemoryEditor.setChecked(false);
};
editor.onRead = { &MemoryEditor::read, this };
editor.onWrite = { &MemoryEditor::write, this };
sourceBox.onChange = []() {
switch(memoryEditor.sourceBox.selection()) {
case 0: memoryEditor.setSource(SNES::Debugger::MemorySource::CPUBus); break;
case 1: memoryEditor.setSource(SNES::Debugger::MemorySource::APURAM); break;
case 2: memoryEditor.setSource(SNES::Debugger::MemorySource::VRAM); break;
case 3: memoryEditor.setSource(SNES::Debugger::MemorySource::OAM); break;
case 4: memoryEditor.setSource(SNES::Debugger::MemorySource::CGRAM); break;
}
};
gotoBox.onChange = []() {
unsigned addr = hex(memoryEditor.gotoBox.text());
memoryEditor.editor.setOffset(addr % memoryEditor.size);
memoryEditor.editor.update();
};
refreshButton.onTick = []() {
memoryEditor.editor.update();
};
setSource(SNES::Debugger::MemorySource::CPUBus);
}
void MemoryEditor::setSource(SNES::Debugger::MemorySource source_) {
switch(source = source_) {
case SNES::Debugger::MemorySource::CPUBus: size = 1 << 24; break;
case SNES::Debugger::MemorySource::APURAM: size = 1 << 16; break;
case SNES::Debugger::MemorySource::VRAM: size = 1 << 16; break;
case SNES::Debugger::MemorySource::OAM: size = 544; break;
case SNES::Debugger::MemorySource::CGRAM: size = 512; break;
}
editor.setLength(size);
editor.setOffset(0);
editor.update();
}
uint8_t MemoryEditor::read(unsigned addr) {
if(SNES::cartridge.loaded() == false) return 0x00;
return SNES::debugger.read(source, addr % size);
}
void MemoryEditor::write(unsigned addr, uint8_t data) {
if(SNES::cartridge.loaded() == false) return;
SNES::debugger.write(source, addr % size, data);
}

View File

@ -1,19 +0,0 @@
struct MemoryEditor : TopLevelWindow {
HorizontalLayout layout;
HexEdit editor;
VerticalLayout controlLayout;
ComboBox sourceBox;
LineEdit gotoBox;
Button refreshButton;
Widget spacer;
SNES::Debugger::MemorySource source;
unsigned size;
void create();
void setSource(SNES::Debugger::MemorySource source);
uint8_t read(unsigned addr);
void write(unsigned addr, uint8_t data);
};
extern MemoryEditor memoryEditor;

View File

@ -1,37 +0,0 @@
#include <data/bsnes-logo.hpp>
AboutWindow aboutWindow;
void AboutWindow::create() {
application.addWindow(this, "AboutWindow", "160,160");
setTitle("About bsnes ...");
setResizable(false);
setBackgroundColor({ 255, 255, 255 });
information.setText({
"bsnes v", SNES::Info::Version, " ~ Profile: ", SNES::Info::Profile,
" ~ Author: byuu ~ Website: http://byuu.org/"
});
layout.setMargin(5);
layout.append(canvas, 720, 180);
informationLayout.append(spacer, ~0, 0);
informationLayout.append(information, 0, 0);
layout.append(informationLayout, ~0, 0);
append(layout);
setGeometry({ 0, 0, layout.minimumGeometry().width, layout.minimumGeometry().height });
}
void AboutWindow::show() {
logo.decode(bsnesLogoData, sizeof bsnesLogoData);
setVisible();
uint32_t *buffer = canvas.buffer();
for(unsigned y = 0; y < 180; y++) {
uint8_t *p = logo.data + 0x36 + (180 - 1 - y) * 720 * 3;
for(unsigned x = 0; x < 720; x++) {
*buffer++ = (p[0] << 0) | (p[1] << 8) | (p[2] << 16);
p += 3;
}
}
canvas.update();
}

View File

@ -1,15 +0,0 @@
struct AboutWindow : TopLevelWindow {
VerticalLayout layout;
Canvas canvas;
HorizontalLayout informationLayout;
Widget spacer;
Label information;
void create();
void show();
private:
resource logo;
};
extern AboutWindow aboutWindow;

View File

@ -1,156 +0,0 @@
FileBrowser fileBrowser;
void FileBrowser::create() {
application.addWindow(this, "FileBrowser", "160,160");
browseButton.setText("...");
upButton.setText("..");
const unsigned sq = browseButton.minimumGeometry().height;
layout.setMargin(5);
pathLayout.append(pathBox, ~0, 0, 5);
pathLayout.append(browseButton, sq, sq, 5);
pathLayout.append(upButton, sq, sq );
layout.append(pathLayout, ~0, 0, 5);
layout.append(contentsBox, ~0, ~0 );
append(layout);
setGeometry({ 0, 0, 640, layout.minimumGeometry().height + 400 });
pathBox.onActivate = []() {
string path = fileBrowser.pathBox.text();
path.transform("\\", "/");
if(strend(path, "/") == false) path.append("/");
fileBrowser.setFolder(path);
};
browseButton.onTick = { &FileBrowser::folderBrowse, this };
upButton.onTick = { &FileBrowser::folderUp, this };
contentsBox.onActivate = { &FileBrowser::fileActivate, this };
}
void FileBrowser::fileOpen(FileBrowser::Mode requestedMode, function<void (string)> requestedCallback) {
callback = requestedCallback;
switch(requestedMode) {
case Mode::Cartridge: folderPath = "sfc"; break;
case Mode::Satellaview: folderPath = "bs"; break;
case Mode::SufamiTurbo: folderPath = "st"; break;
case Mode::GameBoy: folderPath = "gb"; break;
}
string activePath = path.load(folderPath);
//if path has not changed, do not reload list; this will preserve previously selected file
if(mode == requestedMode && folder == activePath) {
setVisible();
contentsBox.setFocused();
return;
}
setVisible(false);
filters.reset();
switch(mode = requestedMode) {
case Mode::Cartridge: {
setTitle("Load Cartridge");
filters.append(".sfc");
break;
}
case Mode::Satellaview: {
setTitle("Load Satellaview Cartridge");
filters.append(".bs");
break;
}
case Mode::SufamiTurbo: {
setTitle("Load Sufami Turbo Cartridge");
filters.append(".st");
break;
}
case Mode::GameBoy: {
setTitle("Load Game Boy Cartridge");
filters.append(".gb");
filters.append(".gbc");
filters.append(".sgb");
break;
}
}
setFolder(activePath);
setVisible(true);
contentsBox.setFocused();
}
void FileBrowser::setFolder(const string &pathname) {
contentsBox.reset();
contents.reset();
folder = pathname;
folder.transform("\\", "/");
pathBox.setText(folder);
lstring contentsList = directory::contents(folder);
foreach(item, contentsList) {
if(strend(item, "/")) {
contents.append(item);
} else foreach(filter, filters) {
if(strend(item, filter)) {
contents.append(item);
break;
}
}
}
foreach(item, contents) contentsBox.append(item);
contentsBox.setSelection(0);
contentsBox.setFocused();
}
void FileBrowser::folderBrowse() {
string pathname = OS::folderSelect(*this, folder);
if(pathname != "") setFolder(pathname);
}
void FileBrowser::folderUp() {
string path = folder;
path.rtrim<1>("/");
if(path != "") setFolder(dir(path));
}
void FileBrowser::fileActivate() {
if(contentsBox.selected() == false) return;
string filename = contents[contentsBox.selection()];
if(strend(filename, "/")) {
string cartridgeName = cartridgeFolder(filename);
if(cartridgeName == "") {
setFolder({ folder, filename });
} else {
loadFile({ folder, cartridgeName });
}
} else {
loadFile({ folder, filename });
}
}
string FileBrowser::cartridgeFolder(const string &pathname) {
if(strend(pathname, ".sfc/") == false) return "";
lstring list = directory::files({ folder, "/", pathname });
string filename;
foreach(item, list) {
if(strend(item, ".sfc")) {
if(filename != "") return ""; //more than one cartridge in this folder
filename = item;
}
}
return { pathname, filename };
}
void FileBrowser::loadFile(const string &filename) {
setVisible(false);
path.save(folderPath, folder);
if(callback) callback(filename);
}

View File

@ -1,28 +0,0 @@
struct FileBrowser : TopLevelWindow {
VerticalLayout layout;
HorizontalLayout pathLayout;
LineEdit pathBox;
Button browseButton;
Button upButton;
ListView contentsBox;
enum class Mode : unsigned { Cartridge, Satellaview, SufamiTurbo, GameBoy } mode;
void fileOpen(Mode mode, function<void (string)> callback);
void create();
private:
function<void (string)> callback;
string folder;
lstring filters;
lstring contents;
string folderPath;
void folderBrowse();
void folderUp();
void fileActivate();
void setFolder(const string &pathname);
string cartridgeFolder(const string &pathname);
void loadFile(const string &filename);
};
extern FileBrowser fileBrowser;

View File

@ -1,6 +0,0 @@
#include "../base.hpp"
#include "main-window.cpp"
#include "file-browser.cpp"
#include "slot-loader.cpp"
#include "nss-dip-window.cpp"
#include "about-window.cpp"

View File

@ -1,5 +0,0 @@
#include "main-window.hpp"
#include "file-browser.hpp"
#include "slot-loader.hpp"
#include "nss-dip-window.hpp"
#include "about-window.hpp"

View File

@ -1,476 +0,0 @@
MainWindow mainWindow;
void MainWindow::create() {
setTitle({ SNES::Info::Name, " v", SNES::Info::Version });
setResizable(false);
setGeometry({ 0, 0, 595, 448 });
application.addWindow(this, "MainWindow", "128,128");
setMenuFont(application.proportionalFont);
setStatusFont(application.proportionalFontBold);
setBackgroundColor({ 0, 0, 0 });
system.setText("System");
systemLoadCartridge.setText("Load Cartridge ...");
system.append(systemLoadCartridge);
systemLoadCartridgeSpecial.setText("Load Special");
system.append(systemLoadCartridgeSpecial);
systemLoadCartridgeBsxSlotted.setText("Load BS-X Slotted Cartridge ...");
systemLoadCartridgeSpecial.append(systemLoadCartridgeBsxSlotted);
systemLoadCartridgeBsx.setText("Load BS-X Cartridge ...");
systemLoadCartridgeSpecial.append(systemLoadCartridgeBsx);
systemLoadCartridgeSufamiTurbo.setText("Load Sufami Turbo Cartridge ...");
systemLoadCartridgeSpecial.append(systemLoadCartridgeSufamiTurbo);
systemLoadCartridgeSuperGameBoy.setText("Load Super Game Boy Cartridge ...");
systemLoadCartridgeSpecial.append(systemLoadCartridgeSuperGameBoy);
system.append(systemSeparator1);
systemPower.setText("Power Cycle");
system.append(systemPower);
systemReset.setText("Reset");
system.append(systemReset);
system.append(systemSeparator2);
systemPort1.setText("Controller Port 1");
system.append(systemPort1);
systemPort1None.setText("None");
systemPort1.append(systemPort1None);
systemPort1Gamepad.setText("Gamepad");
systemPort1.append(systemPort1Gamepad);
systemPort1Multitap.setText("Multitap");
systemPort1.append(systemPort1Multitap);
systemPort1Mouse.setText("Mouse");
systemPort1.append(systemPort1Mouse);
RadioItem::group(
systemPort1None, systemPort1Gamepad, systemPort1Multitap, systemPort1Mouse
);
systemPort2.setText("Controller Port 2");
system.append(systemPort2);
systemPort2None.setText("None");
systemPort2.append(systemPort2None);
systemPort2Gamepad.setText("Gamepad");
systemPort2.append(systemPort2Gamepad);
systemPort2Multitap.setText("Multitap");
systemPort2.append(systemPort2Multitap);
systemPort2Mouse.setText("Mouse");
systemPort2.append(systemPort2Mouse);
systemPort2SuperScope.setText("Super Scope");
systemPort2.append(systemPort2SuperScope);
systemPort2Justifier.setText("Justifier");
systemPort2.append(systemPort2Justifier);
systemPort2Justifiers.setText("Justifiers");
systemPort2.append(systemPort2Justifiers);
systemPort2Serial.setText("Serial Cable");
systemPort2.append(systemPort2Serial);
RadioItem::group(
systemPort2None, systemPort2Gamepad, systemPort2Multitap, systemPort2Mouse,
systemPort2SuperScope, systemPort2Justifier, systemPort2Justifiers,
systemPort2Serial
);
append(system);
settings.setText("Settings");
settingsVideoMode.setText("Video Mode");
settings.append(settingsVideoMode);
settingsVideoMode1x.setText("Scale 1x");
settingsVideoMode.append(settingsVideoMode1x);
settingsVideoMode2x.setText("Scale 2x");
settingsVideoMode.append(settingsVideoMode2x);
settingsVideoMode3x.setText("Scale 3x");
settingsVideoMode.append(settingsVideoMode3x);
settingsVideoMode4x.setText("Scale 4x");
settingsVideoMode.append(settingsVideoMode4x);
settingsVideoMode5x.setText("Scale 5x");
settingsVideoMode.append(settingsVideoMode5x);
RadioItem::group(
settingsVideoMode1x, settingsVideoMode2x, settingsVideoMode3x, settingsVideoMode4x, settingsVideoMode5x
);
settingsVideoMode.append(settingsVideoModeSeparator1);
settingsVideoModeAspectRatioCorrection.setText("Correct Aspect Ratio");
settingsVideoMode.append(settingsVideoModeAspectRatioCorrection);
settingsVideoModeSmoothVideo.setText("Smooth Video");
settingsVideoMode.append(settingsVideoModeSmoothVideo);
settingsVideoMode.append(settingsVideoModeSeparator2);
settingsVideoModeNTSC.setText("NTSC");
settingsVideoMode.append(settingsVideoModeNTSC);
settingsVideoModePAL.setText("PAL");
settingsVideoMode.append(settingsVideoModePAL);
setupFiltersAndShaders();
RadioItem::group(
settingsVideoModeNTSC, settingsVideoModePAL
);
settings.append(settingsSeparator1);
settingsSynchronizeVideo.setText("Synchronize Video");
settings.append(settingsSynchronizeVideo);
settingsSynchronizeAudio.setText("Synchronize Audio");
settings.append(settingsSynchronizeAudio);
settingsMuteAudio.setText("Mute Audio");
settings.append(settingsMuteAudio);
settings.append(settingsSeparator2);
settingsConfiguration.setText("Configuration Settings ...");
settings.append(settingsConfiguration);
append(settings);
tools.setText("Tools");
toolsStateSave.setText("Save State");
tools.append(toolsStateSave);
toolsStateSave1.setText("Slot 1");
toolsStateSave.append(toolsStateSave1);
toolsStateSave2.setText("Slot 2");
toolsStateSave.append(toolsStateSave2);
toolsStateSave3.setText("Slot 3");
toolsStateSave.append(toolsStateSave3);
toolsStateSave4.setText("Slot 4");
toolsStateSave.append(toolsStateSave4);
toolsStateSave5.setText("Slot 5");
toolsStateSave.append(toolsStateSave5);
toolsStateLoad.setText("Load State");
tools.append(toolsStateLoad);
toolsStateLoad1.setText("Slot 1");
toolsStateLoad.append(toolsStateLoad1);
toolsStateLoad2.setText("Slot 2");
toolsStateLoad.append(toolsStateLoad2);
toolsStateLoad3.setText("Slot 3");
toolsStateLoad.append(toolsStateLoad3);
toolsStateLoad4.setText("Slot 4");
toolsStateLoad.append(toolsStateLoad4);
toolsStateLoad5.setText("Slot 5");
toolsStateLoad.append(toolsStateLoad5);
tools.append(toolsSeparator1);
toolsCaptureScreenshot.setText("Capture Screenshot");
tools.append(toolsCaptureScreenshot);
toolsCheatEditor.setText("Cheat Editor ...");
tools.append(toolsCheatEditor);
toolsStateManager.setText("State Manager ...");
tools.append(toolsStateManager);
#if defined(DEBUGGER)
tools.append(toolsSeparator2);
toolsDebugger.setText("Debugger ...");
tools.append(toolsDebugger);
#endif
append(tools);
help.setText("Help");
helpAbout.setText("About ...");
help.append(helpAbout);
append(help);
if(config.controller.port1 == 0) systemPort1None.setChecked();
if(config.controller.port1 == 1) systemPort1Gamepad.setChecked();
if(config.controller.port1 == 2) systemPort1Multitap.setChecked();
if(config.controller.port1 == 3) systemPort1Mouse.setChecked();
if(config.controller.port2 == 0) systemPort2None.setChecked();
if(config.controller.port2 == 1) systemPort2Gamepad.setChecked();
if(config.controller.port2 == 2) systemPort2Multitap.setChecked();
if(config.controller.port2 == 3) systemPort2Mouse.setChecked();
if(config.controller.port2 == 4) systemPort2SuperScope.setChecked();
if(config.controller.port2 == 5) systemPort2Justifier.setChecked();
if(config.controller.port2 == 6) systemPort2Justifiers.setChecked();
if(config.controller.port2 == 7) systemPort2Serial.setChecked();
if(config.video.scale == 1) settingsVideoMode1x.setChecked();
if(config.video.scale == 2) settingsVideoMode2x.setChecked();
if(config.video.scale == 3) settingsVideoMode3x.setChecked();
if(config.video.scale == 4) settingsVideoMode4x.setChecked();
if(config.video.scale == 5) settingsVideoMode5x.setChecked();
settingsVideoModeAspectRatioCorrection.setChecked(config.video.aspectRatioCorrection);
settingsVideoModeSmoothVideo.setChecked(config.video.smooth);
if(config.video.region == 0) settingsVideoModeNTSC.setChecked();
if(config.video.region == 1) settingsVideoModePAL.setChecked();
settingsSynchronizeVideo.setChecked(config.video.synchronize);
settingsSynchronizeAudio.setChecked(config.audio.synchronize);
settingsMuteAudio.setChecked(config.audio.mute);
layout.append(viewport, { 0, 0, 595, 448 });
append(layout);
utility.setStatus("");
setMenuVisible(true);
setStatusVisible(true);
systemLoadCartridge.onTick = [] {
fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) {
cartridge.loadNormal(filename);
});
};
systemLoadCartridgeBsxSlotted.onTick = [] { singleSlotLoader.loadCartridgeBsxSlotted(); };
systemLoadCartridgeBsx.onTick = [] { singleSlotLoader.loadCartridgeBsx(); };
systemLoadCartridgeSufamiTurbo.onTick = [] { doubleSlotLoader.loadCartridgeSufamiTurbo(); };
systemLoadCartridgeSuperGameBoy.onTick = [] { singleSlotLoader.loadCartridgeSuperGameBoy(); };
systemPower.onTick = [] {
SNES::system.power();
utility.showMessage("System was power cycled");
};
systemReset.onTick = [] {
SNES::system.reset();
utility.showMessage("System was reset");
};
systemPort1None.onTick = [] { config.controller.port1 = 0; utility.setControllers(); };
systemPort1Gamepad.onTick = [] { config.controller.port1 = 1; utility.setControllers(); };
systemPort1Multitap.onTick = [] { config.controller.port1 = 2; utility.setControllers(); };
systemPort1Mouse.onTick = [] { config.controller.port1 = 3; utility.setControllers(); };
systemPort2None.onTick = [] { config.controller.port2 = 0; utility.setControllers(); };
systemPort2Gamepad.onTick = [] { config.controller.port2 = 1; utility.setControllers(); };
systemPort2Multitap.onTick = [] { config.controller.port2 = 2; utility.setControllers(); };
systemPort2Mouse.onTick = [] { config.controller.port2 = 3; utility.setControllers(); };
systemPort2SuperScope.onTick = [] { config.controller.port2 = 4; utility.setControllers(); };
systemPort2Justifier.onTick = [] { config.controller.port2 = 5; utility.setControllers(); };
systemPort2Justifiers.onTick = [] { config.controller.port2 = 6; utility.setControllers(); };
systemPort2Serial.onTick = [] { config.controller.port2 = 7; utility.setControllers(); };
settingsVideoMode1x.onTick = [] { utility.setScale(1); };
settingsVideoMode2x.onTick = [] { utility.setScale(2); };
settingsVideoMode3x.onTick = [] { utility.setScale(3); };
settingsVideoMode4x.onTick = [] { utility.setScale(4); };
settingsVideoMode5x.onTick = [] { utility.setScale(5); };
settingsVideoModeAspectRatioCorrection.onTick = [] {
config.video.aspectRatioCorrection = mainWindow.settingsVideoModeAspectRatioCorrection.checked();
utility.setScale();
};
settingsVideoModeSmoothVideo.onTick = [] {
config.video.smooth = mainWindow.settingsVideoModeSmoothVideo.checked();
video.set(Video::Filter, (unsigned)config.video.smooth);
};
settingsVideoModeNTSC.onTick = [] { config.video.region = 0; utility.setScale(); };
settingsVideoModePAL.onTick = [] { config.video.region = 1; utility.setScale(); };
settingsVideoFilterNone.onTick = [] {
config.video.filter = "";
utility.setFilter();
};
settingsVideoShaderNone.onTick = [] {
config.video.shader = "";
utility.setShader();
};
settingsSynchronizeVideo.onTick = [] {
config.video.synchronize = mainWindow.settingsSynchronizeVideo.checked();
video.set(Video::Synchronize, config.video.synchronize);
};
settingsSynchronizeAudio.onTick = [] {
config.audio.synchronize = mainWindow.settingsSynchronizeAudio.checked();
audio.set(Audio::Synchronize, config.audio.synchronize);
};
settingsMuteAudio.onTick = [] { config.audio.mute = mainWindow.settingsMuteAudio.checked(); };
settingsConfiguration.onTick = [] {
settingsWindow.setVisible();
settingsWindow.panel.setFocused();
};
toolsStateSave1.onTick = [] { utility.saveState(1); };
toolsStateSave2.onTick = [] { utility.saveState(2); };
toolsStateSave3.onTick = [] { utility.saveState(3); };
toolsStateSave4.onTick = [] { utility.saveState(4); };
toolsStateSave5.onTick = [] { utility.saveState(5); };
toolsStateLoad1.onTick = [] { utility.loadState(1); };
toolsStateLoad2.onTick = [] { utility.loadState(2); };
toolsStateLoad3.onTick = [] { utility.loadState(3); };
toolsStateLoad4.onTick = [] { utility.loadState(4); };
toolsStateLoad5.onTick = [] { utility.loadState(5); };
toolsCaptureScreenshot.onTick = [] { interface.captureScreenshot = true; };
toolsCheatEditor.onTick = [] { cheatEditor.setVisible(); };
toolsStateManager.onTick = [] { stateManager.setVisible(); };
#if defined(DEBUGGER)
toolsDebugger.onTick = [] { debugger.setVisible(); };
#endif
helpAbout.onTick = [] {
aboutWindow.show();
};
onClose = [] {
application.quit = true;
};
synchronize();
}
void MainWindow::synchronize() {
bool loaded = SNES::cartridge.loaded();
systemPower.setEnabled(loaded);
systemReset.setEnabled(loaded);
toolsStateSave.setEnabled(loaded);
toolsStateLoad.setEnabled(loaded);
toolsCaptureScreenshot.setEnabled(loaded);
}
void MainWindow::setupFiltersAndShaders() {
string folderPath;
lstring files;
reference_array<RadioItem&> group;
signed active;
settingsVideoFilter.setText("Video Filter");
settingsVideoFilterNone.setText("None");
settingsVideoFilter.append(settingsVideoFilterNone);
settingsVideoFilter.append(settingsVideoFilterSeparator);
group.append(settingsVideoFilterNone);
active = -1;
folderPath = { path.base, "filters/" };
files = directory::files(folderPath, "*.filter");
if(files.size() == 0) {
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
folderPath = { path.user, ".config/bsnes/filters/" };
#else
folderPath = { path.user, "bsnes/filters/" };
#endif
files = directory::files(folderPath, "*.filter");
}
foreach(filename, files) {
settingsVideoFilterName.append({ folderPath, filename });
}
if(settingsVideoFilterName.size() == 0) {
config.video.filter = ""; //as the list (and thus the 'None' option) is invisible,
utility.setFilter(); //erase any previously saved filter name
} else {
settingsVideoFilterItem = new RadioItem[settingsVideoFilterName.size()];
foreach(filename, settingsVideoFilterName, n) {
settingsVideoFilterItem[n].onTick = [n]() {
config.video.filter = mainWindow.settingsVideoFilterName[n];
utility.setFilter();
};
settingsVideoFilterItem[n].setText(nall::basename(notdir(filename)));
settingsVideoFilter.append(settingsVideoFilterItem[n]);
group.append(settingsVideoFilterItem[n]);
if(filename == config.video.filter) active = n;
}
RadioItem::group(group);
group.reset();
active < 0 ? settingsVideoFilterNone.setChecked() : settingsVideoFilterItem[active].setChecked();
settings.append(settingsVideoFilter);
}
settingsVideoShader.setText("Video Shader");
settingsVideoShaderNone.setText("None");
settingsVideoShader.append(settingsVideoShaderNone);
settingsVideoShader.append(settingsVideoShaderSeparator);
group.append(settingsVideoShaderNone);
active = -1;
folderPath = { path.base, "shaders/" };
files = directory::files(folderPath, { "*.", config.video.driver, ".shader" });
if(files.size() == 0) {
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
folderPath = { path.user, ".config/bsnes/shaders/" };
#else
folderPath = { path.user, "bsnes/shaders/" };
#endif
files = directory::files(folderPath, { "*.", config.video.driver, ".shader" });
}
foreach(filename, files) {
settingsVideoShaderName.append({ folderPath, filename });
}
if(settingsVideoShaderName.size() == 0) {
config.video.shader = "";
utility.setShader();
} else {
settingsVideoShaderItem = new RadioItem[settingsVideoShaderName.size()];
foreach(filename, settingsVideoShaderName, n) {
settingsVideoShaderItem[n].onTick = [n]() {
config.video.shader = mainWindow.settingsVideoShaderName[n];
utility.setShader();
};
settingsVideoShaderItem[n].setText(nall::basename(nall::basename(notdir(filename))));
settingsVideoShader.append(settingsVideoShaderItem[n]);
group.append(settingsVideoShaderItem[n]);
if(filename == config.video.shader) active = n;
}
RadioItem::group(group);
group.reset();
active < 0 ? settingsVideoShaderNone.setChecked() : settingsVideoShaderItem[active].setChecked();
settings.append(settingsVideoShader);
}
}

View File

@ -1,94 +0,0 @@
struct MainWindow : TopLevelWindow {
Menu system;
Item systemLoadCartridge;
Menu systemLoadCartridgeSpecial;
Item systemLoadCartridgeBsxSlotted;
Item systemLoadCartridgeBsx;
Item systemLoadCartridgeSufamiTurbo;
Item systemLoadCartridgeSuperGameBoy;
Separator systemSeparator1;
Item systemPower;
Item systemReset;
Separator systemSeparator2;
Menu systemPort1;
RadioItem systemPort1None;
RadioItem systemPort1Gamepad;
RadioItem systemPort1Multitap;
RadioItem systemPort1Mouse;
Menu systemPort2;
RadioItem systemPort2None;
RadioItem systemPort2Gamepad;
RadioItem systemPort2Multitap;
RadioItem systemPort2Mouse;
RadioItem systemPort2SuperScope;
RadioItem systemPort2Justifier;
RadioItem systemPort2Justifiers;
RadioItem systemPort2Serial;
Menu settings;
Menu settingsVideoMode;
RadioItem settingsVideoMode1x;
RadioItem settingsVideoMode2x;
RadioItem settingsVideoMode3x;
RadioItem settingsVideoMode4x;
RadioItem settingsVideoMode5x;
Separator settingsVideoModeSeparator1;
CheckItem settingsVideoModeAspectRatioCorrection;
CheckItem settingsVideoModeSmoothVideo;
Separator settingsVideoModeSeparator2;
RadioItem settingsVideoModeNTSC;
RadioItem settingsVideoModePAL;
Menu settingsVideoFilter;
RadioItem settingsVideoFilterNone;
Separator settingsVideoFilterSeparator;
RadioItem *settingsVideoFilterItem;
lstring settingsVideoFilterName;
Menu settingsVideoShader;
RadioItem settingsVideoShaderNone;
Separator settingsVideoShaderSeparator;
RadioItem *settingsVideoShaderItem;
lstring settingsVideoShaderName;
Separator settingsSeparator1;
CheckItem settingsSynchronizeVideo;
CheckItem settingsSynchronizeAudio;
CheckItem settingsMuteAudio;
Separator settingsSeparator2;
Item settingsConfiguration;
Menu tools;
Menu toolsStateSave;
Item toolsStateSave1;
Item toolsStateSave2;
Item toolsStateSave3;
Item toolsStateSave4;
Item toolsStateSave5;
Menu toolsStateLoad;
Item toolsStateLoad1;
Item toolsStateLoad2;
Item toolsStateLoad3;
Item toolsStateLoad4;
Item toolsStateLoad5;
Separator toolsSeparator1;
Item toolsCaptureScreenshot;
Item toolsCheatEditor;
Item toolsStateManager;
#if defined(DEBUGGER)
Separator toolsSeparator2;
Item toolsDebugger;
#endif
Menu help;
Item helpAbout;
FixedLayout layout;
Viewport viewport;
void create();
void synchronize();
void setupFiltersAndShaders();
};
extern MainWindow mainWindow;

View File

@ -1,91 +0,0 @@
NSSDipWindow nssDipWindow;
void NSSDipWindow::create() {
application.addWindow(this, "NSSDipWindow", "160,160");
setTitle("NSS DIP Settings");
for(unsigned n = 0; n < 16; n++) {
dipName[n].setText("Unused");
dipName[n].setVisible(false);
layout.append(dipName[n], { 0, 0, 16, 16 });
dipValue[n].setVisible(false);
layout.append(dipValue[n], { 0, 0, 16, 16 });
}
loadButton.setText("Load Cartridge");
layout.append(loadButton, { 0, 0, 16, 16 });
append(layout);
setResizable(false);
setGeometry({ 0, 0, 400, 240 });
onClose = loadButton.onTick = { &NSSDipWindow::assign, this };
}
void NSSDipWindow::select() {
setVisible(false);
for(unsigned n = 0; n < 16; n++) {
dipName[n].setText({ "DIP #", 1 + n, ":" });
dipName[n].setVisible(false);
dipValue[n].reset();
dipValue[n].setVisible(false);
}
unsigned dipCount = SNES::cartridge.information.nss.setting.size();
for(unsigned n = 0; n < dipCount; n++) {
dipName[n].setText({ "DIP #", 1 + n, ": ", SNES::cartridge.information.nss.setting[n] });
for(unsigned z = 0; z < SNES::cartridge.information.nss.option[n].size(); z++) {
lstring part;
part.split<1>(":", SNES::cartridge.information.nss.option[n][z]);
dipValue[n].append(part[1]);
}
}
unsigned maximumLabelWidth = 50;
unsigned maximumComboWidth = 100;
unsigned controlHeight = dipValue[0].minimumGeometry().height;
for(unsigned n = 0; n < dipCount; n++) {
maximumLabelWidth = max(maximumLabelWidth, dipName[n].minimumGeometry().width);
maximumComboWidth = max(maximumComboWidth, dipValue[n].minimumGeometry().width);
}
for(unsigned n = 0; n < dipCount; n++) {
dipName[n].setGeometry({ 5, 5 + (controlHeight + 5) * n, maximumLabelWidth, controlHeight });
dipName[n].setVisible(true);
dipValue[n].setGeometry({ 5 + maximumLabelWidth + 5, 5 + (controlHeight + 5) * n, maximumComboWidth, controlHeight });
dipValue[n].setVisible(true);
}
unsigned buttonWidth = loadButton.minimumGeometry().width;
unsigned buttonHeight = loadButton.minimumGeometry().height;
unsigned windowWidth = 5 + maximumLabelWidth + 5 + maximumComboWidth + 5;
unsigned windowHeight = 5 + (controlHeight + 5) * dipCount + buttonHeight + 5;
loadButton.setGeometry({ windowWidth - 5 - buttonWidth, windowHeight - 5 - buttonHeight, buttonWidth, buttonHeight });
setGeometry({ geometry().x, geometry().y, windowWidth, windowHeight });
setVisible(true);
loadButton.setFocused();
}
void NSSDipWindow::assign() {
unsigned dip = 0;
for(unsigned n = 0; n < SNES::cartridge.information.nss.setting.size(); n++) {
unsigned position = dipValue[n].selection();
lstring part;
part.split<1>(":", SNES::cartridge.information.nss.option[n][position]);
dip |= hex(part[0]);
}
SNES::nss.set_dip(dip);
setVisible(false);
application.pause = false;
}

View File

@ -1,12 +0,0 @@
struct NSSDipWindow : TopLevelWindow {
FixedLayout layout;
Label dipName[16];
ComboBox dipValue[16];
Button loadButton;
void create();
void select();
void assign();
};
extern NSSDipWindow nssDipWindow;

View File

@ -1,163 +0,0 @@
SingleSlotLoader singleSlotLoader;
DoubleSlotLoader doubleSlotLoader;
void SingleSlotLoader::create() {
application.addWindow(this, "SingleSlotLoader", "160,160");
baseLabel.setText("Base:");
baseBrowse.setText("...");
slotLabel.setText("Slot:");
slotBrowse.setText("...");
okButton.setText("Ok");
const unsigned sq = baseBrowse.minimumGeometry().height;
layout.setMargin(5);
baseLayout.append(baseLabel, 40, 0, 5);
baseLayout.append(basePath, ~0, 0, 5);
baseLayout.append(baseBrowse, sq, sq );
layout.append(baseLayout, ~0, 0, 5);
slotLayout.append(slotLabel, 40, 0, 5);
slotLayout.append(slotPath, ~0, 0, 5);
slotLayout.append(slotBrowse, sq, sq );
layout.append(slotLayout, ~0, 0, 5);
controlLayout.append(spacer, ~0, 0 );
controlLayout.append(okButton, 80, 0 );
layout.append(controlLayout, ~0, 0 );
append(layout);
setGeometry({ 0, 0, 480, layout.minimumGeometry().height });
baseBrowse.onTick = []() {
fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) {
singleSlotLoader.basePath.setText(filename);
});
};
slotBrowse.onTick = []() {
FileBrowser::Mode fileMode = (singleSlotLoader.mode == Mode::SuperGameBoy
? FileBrowser::Mode::GameBoy : FileBrowser::Mode::Satellaview);
fileBrowser.fileOpen(fileMode, [](string filename) {
singleSlotLoader.slotPath.setText(filename);
});
};
okButton.onTick = { &SingleSlotLoader::load, this };
}
void SingleSlotLoader::loadCartridgeBsxSlotted() {
mode = Mode::BsxSlotted;
setTitle("Load BS-X Slotted Cartridge");
basePath.setText("");
slotPath.setText("");
setVisible();
okButton.setFocused();
}
void SingleSlotLoader::loadCartridgeBsx() {
mode = Mode::Bsx;
setTitle("Load BS-X Cartridge");
basePath.setText(path.satellaviewBios);
slotPath.setText("");
setVisible();
okButton.setFocused();
}
void SingleSlotLoader::loadCartridgeSuperGameBoy() {
mode = Mode::SuperGameBoy;
setTitle("Load Super Game Boy cartridge");
basePath.setText(path.superGameBoyBios);
slotPath.setText("");
setVisible();
okButton.setFocused();
}
void SingleSlotLoader::load() {
setVisible(false);
switch(mode) {
case Mode::BsxSlotted: {
cartridge.loadBsxSlotted(basePath.text(), slotPath.text());
break;
}
case Mode::Bsx: {
path.satellaviewBios = basePath.text();
cartridge.loadBsx(basePath.text(), slotPath.text());
break;
}
case Mode::SuperGameBoy: {
path.superGameBoyBios = basePath.text();
cartridge.loadSuperGameBoy(basePath.text(), slotPath.text());
break;
}
}
}
//
void DoubleSlotLoader::create() {
application.addWindow(this, "DoubleSlotLoader", "160,160");
baseLabel.setText("Base:");
baseBrowse.setText("...");
slotALabel.setText("Slot A:");
slotABrowse.setText("...");
slotBLabel.setText("Slot B:");
slotBBrowse.setText("...");
okButton.setText("Ok");
const unsigned sq = baseBrowse.minimumGeometry().height;
layout.setMargin(5);
baseLayout.append(baseLabel, 40, 0, 5);
baseLayout.append(basePath, ~0, 0, 5);
baseLayout.append(baseBrowse, sq, sq );
layout.append(baseLayout, ~0, 0, 5);
slotALayout.append(slotALabel, 40, 0, 5);
slotALayout.append(slotAPath, ~0, 0, 5);
slotALayout.append(slotABrowse, sq, sq );
layout.append(slotALayout, ~0, 0, 5);
slotBLayout.append(slotBLabel, 40, 0, 5);
slotBLayout.append(slotBPath, ~0, 0, 5);
slotBLayout.append(slotBBrowse, sq, sq );
layout.append(slotBLayout, ~0, 0, 5);
controlLayout.append(spacer, ~0, 0 );
controlLayout.append(okButton, 80, 0 );
layout.append(controlLayout, ~0, 0 );
append(layout);
setGeometry({ 0, 0, 480, layout.minimumGeometry().height });
baseBrowse.onTick = []() {
fileBrowser.fileOpen(FileBrowser::Mode::Cartridge, [](string filename) {
doubleSlotLoader.basePath.setText(filename);
});
};
slotABrowse.onTick = []() {
fileBrowser.fileOpen(FileBrowser::Mode::SufamiTurbo, [](string filename) {
doubleSlotLoader.slotAPath.setText(filename);
});
};
slotBBrowse.onTick = []() {
fileBrowser.fileOpen(FileBrowser::Mode::SufamiTurbo, [](string filename) {
doubleSlotLoader.slotBPath.setText(filename);
});
};
okButton.onTick = { &DoubleSlotLoader::load, this };
}
void DoubleSlotLoader::loadCartridgeSufamiTurbo() {
setTitle("Load Sufami Turbo Cartridge");
basePath.setText(path.sufamiTurboBios);
slotAPath.setText("");
slotBPath.setText("");
setVisible();
okButton.setFocused();
}
void DoubleSlotLoader::load() {
setVisible(false);
path.sufamiTurboBios = basePath.text();
cartridge.loadSufamiTurbo(basePath.text(), slotAPath.text(), slotBPath.text());
}

View File

@ -1,49 +0,0 @@
struct SingleSlotLoader : TopLevelWindow {
VerticalLayout layout;
HorizontalLayout baseLayout;
Label baseLabel;
LineEdit basePath;
Button baseBrowse;
HorizontalLayout slotLayout;
Label slotLabel;
LineEdit slotPath;
Button slotBrowse;
HorizontalLayout controlLayout;
Label spacer;
Button okButton;
void create();
void loadCartridgeBsxSlotted();
void loadCartridgeBsx();
void loadCartridgeSuperGameBoy();
enum class Mode : unsigned { BsxSlotted, Bsx, SuperGameBoy } mode;
void load();
};
struct DoubleSlotLoader : TopLevelWindow {
VerticalLayout layout;
HorizontalLayout baseLayout;
Label baseLabel;
LineEdit basePath;
Button baseBrowse;
HorizontalLayout slotALayout;
Label slotALabel;
LineEdit slotAPath;
Button slotABrowse;
HorizontalLayout slotBLayout;
Label slotBLabel;
LineEdit slotBPath;
Button slotBBrowse;
HorizontalLayout controlLayout;
Label spacer;
Button okButton;
void create();
void loadCartridgeSufamiTurbo();
void load();
};
extern SingleSlotLoader singleSlotLoader;
extern DoubleSlotLoader doubleSlotLoader;

View File

@ -1,141 +0,0 @@
void InputMapper::poll_hotkeys(unsigned scancode, int16_t value) {
//internal state
static unsigned activeSlot = 1;
static bool videoSync = false;
static bool audioSync = false;
if(value) {
//key pressed
if(mainWindow.focused() == false) return;
//save states
if(scancode == hotkeysGeneral.stateSave.scancode) {
utility.saveState(activeSlot);
}
if(scancode == hotkeysGeneral.stateLoad.scancode) {
utility.loadState(activeSlot);
}
if(scancode == hotkeysGeneral.stateDecrement.scancode) {
activeSlot = (activeSlot == 1 ? 5 : activeSlot - 1);
utility.showMessage({ "Slot ", activeSlot, " selected" });
}
if(scancode == hotkeysGeneral.stateIncrement.scancode) {
activeSlot = (activeSlot == 5 ? 1 : activeSlot + 1);
utility.showMessage({ "Slot ", activeSlot, " selected" });
}
//capture screenshot
if(scancode == hotkeysGeneral.captureScreenshot.scancode) {
interface.captureScreenshot = true;
}
//fullscreen
if(scancode == hotkeysGeneral.fullscreenToggle.scancode) {
utility.setFullScreen(!utility.fullScreen);
}
//mouse capture
if(scancode == hotkeysGeneral.mouseCaptureToggle.scancode) {
if(input.acquired() == false) {
input.acquire();
} else {
input.unacquire();
}
}
//pause
if(scancode == hotkeysGeneral.pauseToggle.scancode) {
application.pause = !application.pause;
}
//fast forward
if(scancode == hotkeysGeneral.fastForward.scancode) {
videoSync = config.video.synchronize;
audioSync = config.audio.synchronize;
video.set(Video::Synchronize, config.video.synchronize = false);
audio.set(Audio::Synchronize, config.audio.synchronize = false);
#if defined(PROFILE_COMPATIBILITY) || defined(PROFILE_PERFORMANCE)
SNES::ppu.set_frameskip(9);
#endif
}
//power cycle
if(scancode == hotkeysGeneral.power.scancode) {
mainWindow.systemPower.onTick();
}
//reset
if(scancode == hotkeysGeneral.reset.scancode) {
mainWindow.systemReset.onTick();
}
//exit emulator
if(scancode == hotkeysGeneral.exitEmulator.scancode) {
application.quit = true;
}
} else {
//key released
if(mainWindow.focused() == false) return;
//fast forward
if(scancode == hotkeysGeneral.fastForward.scancode) {
video.set(Video::Synchronize, config.video.synchronize = videoSync);
audio.set(Audio::Synchronize, config.audio.synchronize = audioSync);
#if defined(PROFILE_COMPATIBILITY) || defined(PROFILE_PERFORMANCE)
SNES::ppu.set_frameskip(0);
#endif
}
}
}
void InputMapper::HotkeysGeneral::create(const char *deviceName, const char *configName) {
name = deviceName;
stateSave.name = "Save State";
stateLoad.name = "Load State";
stateDecrement.name = "Decrement State";
stateIncrement.name = "Increment State";
captureScreenshot.name = "Capture Screenshot";
fullscreenToggle.name = "Fullscreen";
mouseCaptureToggle.name = "Mouse Capture";
pauseToggle.name = "Pause Emulation";
fastForward.name = "Fast-Forward";
power.name = "Power Cycle";
reset.name = "Reset";
exitEmulator.name = "Exit Emulator";
append(&stateSave);
append(&stateLoad);
append(&stateDecrement);
append(&stateIncrement);
append(&captureScreenshot);
append(&fullscreenToggle);
append(&mouseCaptureToggle);
append(&pauseToggle);
append(&fastForward);
append(&power);
append(&reset);
append(&exitEmulator);
config.attach(stateSave.mapping = "KB0::F5", string("input.", configName, ".stateSave"));
config.attach(stateLoad.mapping = "KB0::F7", string("input.", configName, ".stateLoad"));
config.attach(stateDecrement.mapping = "KB0::F6", string("input.", configName, ".stateDecrement"));
config.attach(stateIncrement.mapping = "KB0::F8", string("input.", configName, ".stateIncrement"));
config.attach(captureScreenshot.mapping = "", string("input.", configName, ".captureScreenshot"));
config.attach(fullscreenToggle.mapping = "KB0::F11", string("input.", configName, ".fullscreenToggle"));
config.attach(mouseCaptureToggle.mapping = "KB0::F12", string("input.", configName, ".mouseCaptureToggle"));
config.attach(pauseToggle.mapping = "KB0::P", string("input.", configName, ".pauseToggle"));
config.attach(fastForward.mapping = "KB0::Tilde", string("input.", configName, ".fastForward"));
config.attach(power.mapping = "", string("input.", configName, ".power"));
config.attach(reset.mapping = "", string("input.", configName, ".reset"));
config.attach(exitEmulator.mapping = "", string("input.", configName, ".exitEmulator"));
}
void InputMapper::create_hotkeys() {
hotkeys.name = "Hotkeys";
hotkeysGeneral.create("General", "hotkeys.general");
hotkeys.append(&hotkeysGeneral);
}

View File

@ -1,18 +0,0 @@
struct HotkeysGeneral : Controller {
DigitalInput stateSave;
DigitalInput stateLoad;
DigitalInput stateDecrement;
DigitalInput stateIncrement;
DigitalInput captureScreenshot;
DigitalInput fullscreenToggle;
DigitalInput mouseCaptureToggle;
DigitalInput pauseToggle;
DigitalInput fastForward;
DigitalInput power;
DigitalInput reset;
DigitalInput exitEmulator;
void create(const char *deviceName, const char *configName);
} hotkeysGeneral;
struct Hotkeys : ControllerPort {
} hotkeys;

View File

@ -1,292 +0,0 @@
#include "../base.hpp"
#include "hotkeys.cpp"
InputMapper inputMapper;
void InputMapper::AbstractInput::bind() {
if(strend(mapping, ".Up")) type = Type::HatUp;
else if(strend(mapping, ".Down")) type = Type::HatDown;
else if(strend(mapping, ".Left")) type = Type::HatLeft;
else if(strend(mapping, ".Right")) type = Type::HatRight;
else if(strend(mapping, ".Lo")) type = Type::AxisLo;
else if(strend(mapping, ".Hi")) type = Type::AxisHi;
else if(strbegin(mapping, "MS") && strend(mapping, "axis")) type = Type::MouseAxis;
else if(strbegin(mapping, "MS")) type = Type::MouseButton;
else type = Type::Button;
string mappingValue = mapping;
if(auto position = strpos(mappingValue, ".")) mappingValue[position()] = 0;
scancode = Scancode::decode(mappingValue);
}
int16_t InputMapper::AnalogInput::poll() {
int16_t value = inputMapper.state[inputMapper.activeState][scancode];
switch(type) {
case AbstractInput::Type::MouseAxis: {
if(input.acquired() == false) return 0;
return value;
}
}
return 0;
}
int16_t InputMapper::DigitalInput::poll() {
int16_t value = inputMapper.state[inputMapper.activeState][scancode];
switch(type) {
case AbstractInput::Type::Button: return (bool)value;
case AbstractInput::Type::MouseButton: return (bool)value & input.acquired();
case AbstractInput::Type::HatUp: return (bool)(value & Joypad::HatUp);
case AbstractInput::Type::HatDown: return (bool)(value & Joypad::HatDown);
case AbstractInput::Type::HatLeft: return (bool)(value & Joypad::HatLeft);
case AbstractInput::Type::HatRight: return (bool)(value & Joypad::HatRight);
case AbstractInput::Type::AxisLo: return (bool)(value < -16384);
case AbstractInput::Type::AxisHi: return (bool)(value > +16384);
}
}
void InputMapper::Gamepad::create(const char *deviceName, const char *configName) {
name = deviceName;
up.name = "Up"; down.name = "Down"; left.name = "Left"; right.name = "Right";
b.name = "B"; a.name = "A"; y.name = "Y"; x.name = "X";
l.name = "L"; r.name = "R"; select.name = "Select"; start.name = "Start";
append(&up); append(&down); append(&left); append(&right);
append(&b); append(&a); append(&y); append(&x);
append(&l); append(&r); append(&select); append(&start);
config.attach(up.mapping = "", string("input.", configName, ".up"));
config.attach(down.mapping = "", string("input.", configName, ".down"));
config.attach(left.mapping = "", string("input.", configName, ".left"));
config.attach(right.mapping = "", string("input.", configName, ".right"));
config.attach(b.mapping = "", string("input.", configName, ".b"));
config.attach(a.mapping = "", string("input.", configName, ".a"));
config.attach(y.mapping = "", string("input.", configName, ".y"));
config.attach(x.mapping = "", string("input.", configName, ".x"));
config.attach(l.mapping = "", string("input.", configName, ".l"));
config.attach(r.mapping = "", string("input.", configName, ".r"));
config.attach(select.mapping = "", string("input.", configName, ".select"));
config.attach(start.mapping = "", string("input.", configName, ".start"));
if(!strcmp(configName, "port1.gamepad")) {
up.mapping = "KB0::Up";
down.mapping = "KB0::Down";
left.mapping = "KB0::Left";
right.mapping = "KB0::Right";
b.mapping = "KB0::Z";
a.mapping = "KB0::X";
y.mapping = "KB0::A";
x.mapping = "KB0::S";
l.mapping = "KB0::D";
r.mapping = "KB0::C";
select.mapping = "KB0::Apostrophe";
start.mapping = "KB0::Return";
}
}
int16_t InputMapper::Gamepad::poll(unsigned id) {
switch((SNES::Input::JoypadID)id) {
case SNES::Input::JoypadID::Up: return up.poll();
case SNES::Input::JoypadID::Down: return down.poll() & !up.poll();
case SNES::Input::JoypadID::Left: return left.poll();
case SNES::Input::JoypadID::Right: return right.poll() & !left.poll();
case SNES::Input::JoypadID::B: return b.poll();
case SNES::Input::JoypadID::A: return a.poll();
case SNES::Input::JoypadID::Y: return y.poll();
case SNES::Input::JoypadID::X: return x.poll();
case SNES::Input::JoypadID::L: return l.poll();
case SNES::Input::JoypadID::R: return r.poll();
case SNES::Input::JoypadID::Select: return select.poll();
case SNES::Input::JoypadID::Start: return start.poll();
}
return 0;
}
void InputMapper::Mouse::create(const char *deviceName, const char *configName) {
name = deviceName;
x.name = "X-axis"; y.name = "Y-axis";
left.name = "Left Button"; right.name = "Right Button";
append(&x); append(&y);
append(&left); append(&right);
config.attach(x.mapping = "", string("input.", configName, ".x"));
config.attach(y.mapping = "", string("input.", configName, ".y"));
config.attach(left.mapping = "", string("input.", configName, ".left"));
config.attach(right.mapping = "", string("input.", configName, ".right"));
x.mapping = "MS0::Xaxis";
y.mapping = "MS0::Yaxis";
left.mapping = "MS0::Button0";
right.mapping = "MS0::Button2";
}
int16_t InputMapper::Mouse::poll(unsigned id) {
switch((SNES::Input::MouseID)id) {
case SNES::Input::MouseID::X: return x.poll();
case SNES::Input::MouseID::Y: return y.poll();
case SNES::Input::MouseID::Left: return left.poll();
case SNES::Input::MouseID::Right: return right.poll();
}
return 0;
}
void InputMapper::SuperScope::create(const char *deviceName, const char *configName) {
name = deviceName;
x.name = "X-axis"; y.name = "Y-axis";
trigger.name = "Trigger"; cursor.name = "Cursor"; turbo.name = "Turbo"; pause.name = "Pause";
append(&x); append(&y);
append(&trigger); append(&cursor); append(&turbo); append(&pause);
config.attach(x.mapping = "", string("input.", configName, ".x"));
config.attach(y.mapping = "", string("input.", configName, ".y"));
config.attach(trigger.mapping = "", string("input.", configName, ".trigger"));
config.attach(cursor.mapping = "", string("input.", configName, ".cursor"));
config.attach(turbo.mapping = "", string("input.", configName, ".turbo"));
config.attach(pause.mapping = "", string("input.", configName, ".pause"));
x.mapping = "MS0::Xaxis";
y.mapping = "MS0::Yaxis";
trigger.mapping = "MS0::Button0";
cursor.mapping = "MS0::Button2";
turbo.mapping = "KB0::T";
pause.mapping = "KB0::P";
}
int16_t InputMapper::SuperScope::poll(unsigned id) {
switch((SNES::Input::SuperScopeID)id) {
case SNES::Input::SuperScopeID::X: return x.poll();
case SNES::Input::SuperScopeID::Y: return y.poll();
case SNES::Input::SuperScopeID::Trigger: return trigger.poll();
case SNES::Input::SuperScopeID::Cursor: return cursor.poll();
case SNES::Input::SuperScopeID::Turbo: return turbo.poll();
case SNES::Input::SuperScopeID::Pause: return pause.poll();
}
return 0;
}
void InputMapper::Justifier::create(const char *deviceName, const char *configName) {
name = deviceName;
x.name = "X-axis"; y.name = "Y-axis";
trigger.name = "Trigger"; start.name = "Start";
append(&x); append(&y);
append(&trigger); append(&start);
config.attach(x.mapping = "", string("input.", configName, ".x"));
config.attach(y.mapping = "", string("input.", configName, ".y"));
config.attach(trigger.mapping = "", string("input.", configName, ".trigger"));
config.attach(start.mapping = "", string("input.", configName, ".start"));
if(!strcmp(configName, "port2.justifierA")) {
x.mapping = "MS0::Xaxis";
y.mapping = "MS0::Yaxis";
trigger.mapping = "MS0::Button0";
start.mapping = "MS0::Button2";
}
}
int16_t InputMapper::Justifier::poll(unsigned id) {
switch((SNES::Input::JustifierID)id) {
case SNES::Input::JustifierID::X: return x.poll();
case SNES::Input::JustifierID::Y: return y.poll();
case SNES::Input::JustifierID::Trigger: return trigger.poll();
case SNES::Input::JustifierID::Start: return start.poll();
}
return 0;
}
void InputMapper::create() {
activeState = 0;
port1.name = "Controller Port 1";
port1.gamepad.create("Gamepad", "port1.gamepad");
port1.multitapA.create("Multitap - Port 1", "port1.multitapA");
port1.multitapB.create("Multitap - Port 2", "port1.multitapB");
port1.multitapC.create("Multitap - Port 3", "port1.multitapC");
port1.multitapD.create("Multitap - Port 4", "port1.multitapD");
port1.mouse.create("Mouse", "port1.mouse");
port1.append(&port1.gamepad);
port1.append(&port1.multitapA);
port1.append(&port1.multitapB);
port1.append(&port1.multitapC);
port1.append(&port1.multitapD);
port1.append(&port1.mouse);
port2.name = "Controller Port 2";
port2.gamepad.create("Gamepad", "port2.gamepad");
port2.multitapA.create("Multitap - Port 1", "port2.multitapA");
port2.multitapB.create("Multitap - Port 2", "port2.multitapB");
port2.multitapC.create("Multitap - Port 3", "port2.multitapC");
port2.multitapD.create("Multitap - Port 4", "port2.multitapD");
port2.mouse.create("Mouse", "port2.mouse");
port2.superScope.create("Super Scope", "port2.superScope");
port2.justifierA.create("Justifier - Port 1", "port2.justifierA");
port2.justifierB.create("Justifier - Port 2", "port2.justifierB");
port2.append(&port2.gamepad);
port2.append(&port2.multitapA);
port2.append(&port2.multitapB);
port2.append(&port2.multitapC);
port2.append(&port2.multitapD);
port2.append(&port2.mouse);
port2.append(&port2.superScope);
port2.append(&port2.justifierA);
port2.append(&port2.justifierB);
create_hotkeys();
}
void InputMapper::bind() {
for(unsigned i = 0; i < port1.size(); i++) {
Controller &controller = *port1[i];
for(unsigned n = 0; n < controller.size(); n++) controller[n]->bind();
}
for(unsigned i = 0; i < port2.size(); i++) {
Controller &controller = *port2[i];
for(unsigned n = 0; n < controller.size(); n++) controller[n]->bind();
}
for(unsigned i = 0; i < hotkeys.size(); i++) {
Controller &controller = *hotkeys[i];
for(unsigned n = 0; n < controller.size(); n++) controller[n]->bind();
}
}
void InputMapper::poll() {
activeState ^= 1;
input.poll(state[activeState]);
for(unsigned i = 0; i < Scancode::Limit; i++) {
if(state[0][i] != state[1][i]) {
poll_hotkeys(i, state[activeState][i]);
inputSettings.inputEvent(i, state[activeState][i]);
}
}
}
int16_t InputMapper::poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
if(port == 0) {
if(device == SNES::Input::Device::Joypad) return port1.gamepad.poll(id);
if(device == SNES::Input::Device::Multitap) switch(index) {
case 0: return port1.multitapA.poll(id);
case 1: return port1.multitapB.poll(id);
case 2: return port1.multitapC.poll(id);
case 3: return port1.multitapD.poll(id);
}
if(device == SNES::Input::Device::Mouse) return port1.mouse.poll(id);
} else {
if(device == SNES::Input::Device::Joypad) return port2.gamepad.poll(id);
if(device == SNES::Input::Device::Multitap) switch(index) {
case 0: return port2.multitapA.poll(id);
case 1: return port2.multitapB.poll(id);
case 2: return port2.multitapC.poll(id);
case 3: return port2.multitapD.poll(id);
}
if(device == SNES::Input::Device::Mouse) return port2.mouse.poll(id);
if(device == SNES::Input::Device::SuperScope) return port2.superScope.poll(id);
if(device == SNES::Input::Device::Justifier) return port2.justifierA.poll(id);
if(device == SNES::Input::Device::Justifiers) switch(index) {
case 0: return port2.justifierA.poll(id);
case 1: return port2.justifierB.poll(id);
}
}
return 0;
}
int16_t InputMapper::value(unsigned scancode) {
return state[activeState][scancode];
}

View File

@ -1,90 +0,0 @@
struct InputMapper {
int16_t state[2][Scancode::Limit];
bool activeState;
struct AbstractInput {
enum class Type : unsigned { Button, MouseAxis, MouseButton, HatUp, HatDown, HatLeft, HatRight, AxisLo, AxisHi } type;
string name;
string mapping;
unsigned scancode;
virtual void bind();
};
struct AnalogInput : AbstractInput {
int16_t poll();
};
struct DigitalInput : AbstractInput {
int16_t poll();
};
struct Controller : array<AbstractInput*> {
string name;
};
struct Gamepad : Controller {
DigitalInput up, down, left, right;
DigitalInput b, a, y, x;
DigitalInput l, r, select, start;
void create(const char *deviceName, const char *configName);
int16_t poll(unsigned id);
};
struct Mouse : Controller {
AnalogInput x, y;
DigitalInput left, right;
void create(const char *deviceName, const char *configName);
int16_t poll(unsigned id);
};
struct SuperScope : Controller {
AnalogInput x, y;
DigitalInput trigger, cursor, turbo, pause;
void create(const char *deviceName, const char *configName);
int16_t poll(unsigned id);
};
struct Justifier : Controller {
AnalogInput x, y;
DigitalInput trigger, start;
void create(const char *deviceName, const char *configName);
int16_t poll(unsigned id);
};
struct ControllerPort : array<Controller*> {
string name;
};
struct Port1 : ControllerPort {
Gamepad gamepad;
Gamepad multitapA;
Gamepad multitapB;
Gamepad multitapC;
Gamepad multitapD;
Mouse mouse;
} port1;
struct Port2 : ControllerPort {
Gamepad gamepad;
Gamepad multitapA;
Gamepad multitapB;
Gamepad multitapC;
Gamepad multitapD;
Mouse mouse;
SuperScope superScope;
Justifier justifierA;
Justifier justifierB;
} port2;
#include "hotkeys.hpp"
void create();
void bind();
void poll();
int16_t poll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
void create_hotkeys();
void poll_hotkeys(unsigned scancode, int16_t value);
int16_t value(unsigned scancode);
};
extern InputMapper inputMapper;

View File

@ -1,165 +0,0 @@
Palette palette;
Filter filter;
Interface interface;
const uint8_t Palette::gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};
uint8_t Palette::contrastAdjust(uint8_t input) {
signed contrast = config.video.contrast - 100;
signed result = input - contrast + (2 * contrast * input + 127) / 255;
return max(0, min(255, result));
}
uint8_t Palette::brightnessAdjust(uint8_t input) {
signed brightness = config.video.brightness - 100;
signed result = input + brightness;
return max(0, min(255, result));
}
uint8_t Palette::gammaAdjust(uint8_t input) {
signed result = (signed)(pow(((double)input / 255.0), (double)config.video.gamma / 100.0) * 255.0 + 0.5);
return max(0, min(255, result));
}
void Palette::update() {
for(unsigned i = 0; i < 32768; i++) {
unsigned r = (i >> 10) & 31;
unsigned g = (i >> 5) & 31;
unsigned b = (i >> 0) & 31;
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
if(config.video.useGammaRamp) {
r = gammaRamp[r >> 3];
g = gammaRamp[g >> 3];
b = gammaRamp[b >> 3];
}
if(config.video.contrast != 100) {
r = contrastAdjust(r);
g = contrastAdjust(g);
b = contrastAdjust(b);
}
if(config.video.brightness != 100) {
r = brightnessAdjust(r);
g = brightnessAdjust(g);
b = brightnessAdjust(b);
}
if(config.video.gamma != 100) {
r = gammaAdjust(r);
g = gammaAdjust(g);
b = gammaAdjust(b);
}
color[i] = (r << 16) | (g << 8) | (b << 0);
}
}
void Filter::size(unsigned &width, unsigned &height) {
if(opened() && dl_size) return dl_size(width, height);
}
void Filter::render(uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, unsigned width, unsigned height) {
if(opened() && dl_render) return dl_render(palette.color, output, outpitch, input, pitch, width, height);
for(unsigned y = 0; y < height; y++) {
uint32_t *outputLine = output + y * (outpitch >> 2);
const uint16_t *inputLine = input + y * (pitch >> 1);
for(unsigned x = 0; x < width; x++) *outputLine++ = palette.color[*inputLine++];
}
}
//data is a 512x512x16bpp buffer, broken in two-scanline pairs (for interlace):
// 0 - 7 = front porch
// 8 = empty scanline 0
// 9 - 232 = standard scanlines 1 - 224
//233 - 247 = overscan scanlines 225-239
//248 - 255 = back porch
void Interface::video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
unsigned width = hires ? 512 : 256;
unsigned height = config.video.region == 0 ? 224 : 239;
if(interlace) height <<= 1;
unsigned inpitch = interlace ? 1024 : 2048;
if(config.video.region == 0) {
if(overscan == false) data += 9 * 1024; // 0 + 224 + 0
if(overscan == true ) data += 16 * 1024; //-7 + 224 + -7
}
if(config.video.region == 1) {
if(overscan == false) data += 1 * 1024; // 8 + 224 + 7
if(overscan == true ) data += 9 * 1024; // 0 + 239 + 0
}
unsigned outwidth = width, outheight = height;
filter.size(outwidth, outheight);
uint32_t *buffer;
unsigned outpitch;
if(video.lock(buffer, outpitch, outwidth, outheight)) {
filter.render(buffer, outpitch, data, inpitch, width, height);
video.unlock();
video.refresh();
}
static unsigned frameCounter = 0;
static time_t previous, current;
frameCounter++;
time(&current);
if(current != previous) {
utility.setStatus({ "FPS: ", frameCounter });
frameCounter = 0;
previous = current;
}
if(captureScreenshot) {
captureScreenshot = false;
time_t currentTime = time(0);
tm *info = localtime(&currentTime);
string filename = { "-",
decimal<4, '0'>(info->tm_year + 1900), "-", decimal<2, '0'>(info->tm_mon + 1), "-", decimal<2, '0'>(info->tm_mday), " ",
decimal<2, '0'>(info->tm_hour), ".", decimal<2, '0'>(info->tm_min), ".", decimal<2, '0'>(info->tm_sec), ".bmp"
};
if(bmp::write(path(utility.activeSlot(), filename), buffer, outwidth, outheight, outpitch, false)) {
utility.showMessage("Screenshot captured");
}
}
}
void Interface::audio_sample(int16_t lchannel, int16_t rchannel) {
if(config.audio.mute) lchannel = 0, rchannel = 0;
dspaudio.sample(lchannel, rchannel);
while(dspaudio.pending()) {
signed lsample, rsample;
dspaudio.read(lsample, rsample);
audio.sample(lsample, rsample);
}
}
int16_t Interface::input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
if(config.settings.focusPolicy == 1 && mainWindow.focused() == false) return 0;
return inputMapper.poll(port, device, index, id);
}
void Interface::message(const string &text) {
MessageWindow::information(mainWindow, text);
}
string Interface::path(SNES::Cartridge::Slot slot, const string &hint) {
return ::path.load(slot, hint);
}
Interface::Interface() {
captureScreenshot = false;
}

View File

@ -1,31 +0,0 @@
struct Palette {
static const uint8_t gammaRamp[32];
uint32_t color[32768];
uint8_t contrastAdjust(uint8_t);
uint8_t brightnessAdjust(uint8_t);
uint8_t gammaAdjust(uint8_t);
void update();
};
struct Filter : public library {
function<void (unsigned&, unsigned&)> dl_size;
function<void (uint32_t*, uint32_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned)> dl_render;
void size(unsigned &width, unsigned &height);
void render(uint32_t *output, unsigned outpitch, const uint16_t *input, unsigned pitch, unsigned width, unsigned height);
};
struct Interface : public SNES::Interface {
void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
void audio_sample(int16_t left, int16_t right);
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
void message(const string &text);
string path(SNES::Cartridge::Slot slot, const string &hint);
Interface();
bool captureScreenshot;
};
extern Palette palette;
extern Filter filter;
extern Interface interface;

View File

@ -1,197 +0,0 @@
#include "base.hpp"
#include "interface.cpp"
#include "config.cpp"
nall::DSP dspaudio;
Application application;
void Application::main(int argc, char **argv) {
#if defined(PLATFORM_WIN)
utf8_args(argc, argv);
#endif
compositorActive = compositor::enabled();
config.create();
inputMapper.create();
path.base = dir(realpath(argv[0]));
path.user = userpath();
path.load();
path.save();
config.load();
config.save();
inputMapper.bind();
#if defined(PLATFORM_WIN)
proportionalFont = "Tahoma, 8";
proportionalFontBold = "Tahoma, 8, Bold";
monospaceFont = "Lucida Console, 8";
titleFont = "Segoe Print, 16, Bold";
#else
proportionalFont = "Sans, 8";
proportionalFontBold = "Sans, 8, Bold";
monospaceFont = "Liberation Mono, 8";
titleFont = "Sans, 16, Bold Italic";
#endif
SNES::system.init(&interface);
if(config.video.driver == "") config.video.driver = video.default_driver();
if(config.audio.driver == "") config.audio.driver = audio.default_driver();
if(config.input.driver == "") config.input.driver = input.default_driver();
palette.update();
mainWindow.create();
fileBrowser.create();
singleSlotLoader.create();
doubleSlotLoader.create();
nssDipWindow.create();
aboutWindow.create();
settingsWindow.create();
cheatEditor.create();
cheatDatabase.create();
stateManager.create();
#if defined(DEBUGGER)
debugger.create();
#endif
loadGeometry();
saveGeometry();
utility.setScale(config.video.scale);
mainWindow.setVisible();
OS::processEvents();
video.driver(config.video.driver);
video.set(Video::Handle, mainWindow.viewport.handle());
video.set(Video::Synchronize, config.video.synchronize);
video.set(Video::Filter, (unsigned)config.video.smooth);
if(video.init() == false) {
MessageWindow::critical(mainWindow, "Failed to initialize video.");
video.driver("None");
video.init();
}
audio.driver(config.audio.driver);
audio.set(Audio::Handle, mainWindow.viewport.handle());
audio.set(Audio::Synchronize, config.audio.synchronize);
audio.set(Audio::Latency, config.audio.latency);
audio.set(Audio::Frequency, config.audio.outputFrequency);
if(audio.init() == false) {
MessageWindow::critical(mainWindow, "Failed to initialize audio.");
audio.driver("None");
audio.init();
}
dspaudio.setPrecision(16); //16-bit signed audio
dspaudio.setVolume((double)config.audio.volume / 100.0);
dspaudio.setBalance((double)((signed)config.audio.balance - 100) / 100.0);
dspaudio.setFrequency(config.audio.inputFrequency);
dspaudio.setResampler(DSP::Resampler::Hermite);
dspaudio.setResamplerFrequency(config.audio.outputFrequency);
input.driver(config.input.driver);
input.set(Input::Handle, mainWindow.viewport.handle());
if(input.init() == false) {
MessageWindow::critical(mainWindow, "Failed to initialize input.");
input.driver("None");
input.init();
}
utility.setControllers();
utility.setFilter();
utility.setShader();
if(config.settings.startFullScreen) utility.setFullScreen();
if(argc == 2) cartridge.loadNormal(argv[1]);
while(quit == false) {
OS::processEvents();
inputMapper.poll();
utility.updateStatus();
if(SNES::cartridge.loaded()) {
if(application.pause == true || (config.settings.focusPolicy == 0 && mainWindow.focused() == false)) {
audio.clear();
usleep(20 * 1000);
continue;
}
#if defined(DEBUGGER)
debugger.run();
#else
SNES::system.run();
#endif
} else {
usleep(20 * 1000);
}
}
cartridge.unload();
utility.setFullScreen(false);
saveGeometry();
foreach(window, windows) window->setVisible(false);
OS::processEvents();
SNES::system.term();
path.save();
config.save();
video.term();
audio.term();
input.term();
if(compositorActive) compositor::enable(true);
}
void Application::addWindow(TopLevelWindow *window, const string &name, const string &position) {
windows.append(window);
window->name = name;
window->position = position;
window->setWidgetFont(proportionalFont);
geometryConfig.attach(window->position, window->name);
}
Application::Application() {
pause = false;
quit = false;
}
int main(int argc, char **argv) {
application.main(argc, argv);
return 0;
}
void Application::loadGeometry() {
geometryConfig.load(path.home("geometry.cfg"));
foreach(window, windows) {
lstring position;
position.split(",", window->position);
Geometry configGeometry = {
(signed)integer(position[0]), (signed)integer(position[1]),
(unsigned)decimal(position[2]), (unsigned)decimal(position[3])
};
Geometry windowGeometry = window->geometry();
//Windows places minimized windows offscreen at 32000,32000
//this is a fix for older releases that did not compensate for this
if(configGeometry.x >= 30000) configGeometry.x = 128;
if(configGeometry.y >= 30000) configGeometry.y = 128;
window->setGeometry({
configGeometry.x, configGeometry.y,
windowGeometry.width, windowGeometry.height
//configGeometry.width, configGeometry.height
});
}
}
void Application::saveGeometry() {
foreach(window, windows) {
Geometry geom = window->geometry();
window->position = { geom.x, ",", geom.y, ",", geom.width, ",", geom.height };
}
geometryConfig.save(path.home("geometry.cfg"));
}

View File

@ -1,138 +0,0 @@
#include "../base.hpp"
Path path;
string Path::home(const string &filename) {
string path = { base, filename };
if(file::exists(path)) return path;
path = user;
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
path.append(".config/");
mkdir(path, 0755);
path.append("bsnes/");
mkdir(path, 0755);
#else
path.append("bsnes/");
mkdir(path, 0755);
#endif
path.append(filename);
return path;
}
string Path::load(const string &path) {
string value;
if(path == "sfc") value = sfc;
if(path == "bs") value = bs;
if(path == "st") value = st;
if(path == "gb") value = gb;
if(value.beginswith("recent/")) value.ltrim<1>("recent/");
if(value == "") value = base;
return value;
}
void Path::save(const string &path, const string &value) {
string *output = 0;
if(path == "sfc") output = &sfc;
if(path == "bs") output = &bs;
if(path == "st") output = &st;
if(path == "gb") output = &gb;
if(output) {
string &s = *output;
if(s.beginswith("recent/") == false && s != "") return;
s = { "recent/", value };
return;
}
}
string Path::load(SNES::Cartridge::Slot slot, const string &hint) {
string baseName = utility.baseName(slot);
string fileName = notdir(baseName);
string filePath = dir(baseName);
if(hint == ".srm" && srm != "") filePath = srm;
if(hint == ".bsp" && bsp != "") filePath = bsp;
if(hint == ".bss" && bss != "") filePath = bss;
if(hint == ".sts" && sts != "") filePath = sts;
if(hint == ".sav" && sav != "") filePath = sav;
if(hint == ".rtc" && rtc != "") filePath = rtc;
if(hint.endswith(".dsp") && firmware != "") filePath = firmware;
if(hint.endswith(".msu") && msu1 != "") filePath = msu1;
if(hint.endswith(".pcm") && msu1 != "") filePath = msu1;
if(hint.endswith(".so") && serial != "") filePath = serial;
if(hint.endswith(".bsa") && bsa != "") filePath = bsa;
if(hint.endswith(".bst") && bst != "") filePath = bst;
if(hint.endswith(".cht") && cht != "") filePath = cht;
if(hint.endswith(".log") && log != "") filePath = log;
if(hint.endswith(".bmp") && bmp != "") filePath = bmp;
filePath = decode(filePath, baseName);
return { filePath, fileName, hint };
}
string Path::decode(const string &filePath, const string &basePath) {
string path = filePath;
if(path.beginswith("user/")) {
path.ltrim<1>("user/");
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
path = { user, ".config/bsnes/", path };
#else
path = { user, "bsnes/", path };
#endif
} else if(path.beginswith("base/")) {
path.ltrim<1>("base/");
path = { base, path };
} else if(path.beginswith("./")) {
path.ltrim<1>("./");
path = { dir(basePath), path };
} else if(path.beginswith("../")) {
string base = dir(basePath);
base.rtrim<1>("/");
path.ltrim<1>("../");
path = { dir(base), path };
}
return path;
}
void Path::load() {
configuration::load(home("paths.cfg"));
}
void Path::save() {
configuration::save(home("paths.cfg"));
}
Path::Path() {
attach(sfc = "", "sfc");
attach(bs = "", "bs");
attach(st = "", "st");
attach(gb = "", "gb");
attach(satellaviewBios = "", "satellaviewBios");
attach(sufamiTurboBios = "", "sufamiTurboBios");
attach(superGameBoyBios = "", "superGameBoyBios");
attach(firmware = "", "firmware");
attach(msu1 = "", "msu1");
attach(serial = "", "serial");
attach(srm = "", "srm");
attach(rtc = "", "rtc");
attach(bss = "", "bss");
attach(bsp = "", "bsp");
attach(sts = "", "sts");
attach(sav = "", "sav");
attach(bsa = "", "bsa");
attach(bst = "", "bst");
attach(cht = "", "cht");
attach(log = "", "log");
attach(bmp = "", "bmp");
}

View File

@ -1,43 +0,0 @@
struct Path : public configuration {
string user;
string base;
string sfc;
string bs;
string st;
string gb;
string satellaviewBios;
string sufamiTurboBios;
string superGameBoyBios;
string firmware;
string msu1;
string serial;
string srm;
string rtc;
string bss;
string bsp;
string sts;
string sav;
string bsa;
string bst;
string cht;
string log;
string bmp;
string home(const string &filename);
string load(const string &path);
void save(const string &path, const string &value);
string load(SNES::Cartridge::Slot slot, const string &hint);
string decode(const string &filePath, const string &basePath);
void load();
void save();
Path();
};
extern Path path;

View File

@ -1,2 +0,0 @@
1 24 "../data/bsnes.Manifest"
2 ICON DISCARDABLE "../data/bsnes.ico"

View File

@ -1,105 +0,0 @@
AdvancedSettings advancedSettings;
void AdvancedSettings::create() {
title.setText("Advanced Settings");
title.setFont(application.titleFont);
driverSelectionLabel.setText("Driver selection:");
driverSelectionLabel.setFont(application.proportionalFontBold);
videoDriverLabel.setText("Video:");
audioDriverLabel.setText("Audio:");
inputDriverLabel.setText("Input:");
focusPolicyLabel.setText("When emulation window lacks focus:");
focusPolicyLabel.setFont(application.proportionalFontBold);
focusPolicyPause.setText("Pause emulator");
focusPolicyIgnore.setText("Ignore input");
focusPolicyAllow.setText("Allow input");
RadioBox::group(focusPolicyPause, focusPolicyIgnore, focusPolicyAllow);
if(config.settings.focusPolicy == 0) focusPolicyPause.setChecked();
if(config.settings.focusPolicy == 1) focusPolicyIgnore.setChecked();
if(config.settings.focusPolicy == 2) focusPolicyAllow.setChecked();
compositorPolicyLabel.setText("Disable window compositor:");
compositorPolicyLabel.setFont(application.proportionalFontBold);
compositorPolicyNever.setText("Never");
compositorPolicyFullScreen.setText("Fullscreen");
compositorPolicyAlways.setText("Always");
RadioBox::group(compositorPolicyNever, compositorPolicyFullScreen, compositorPolicyAlways);
if(config.settings.compositorPolicy == 0) compositorPolicyNever.setChecked();
if(config.settings.compositorPolicy == 1) compositorPolicyFullScreen.setChecked();
if(config.settings.compositorPolicy == 2) compositorPolicyAlways.setChecked();
if(config.settings.compositorPolicy == 2) compositor::enable(false);
panelLayout.setMargin(5);
panelLayout.append(panel, SettingsWindow::PanelWidth, ~0, 5);
panelLayout.append(layout, ~0, ~0);
layout.append(title, ~0, 0, 5);
layout.append(driverSelectionLabel, ~0, 0);
driverLayout.append(videoDriverLabel, 0, 0, 5);
driverLayout.append(videoDriverBox, ~0, 0, 5);
driverLayout.append(audioDriverLabel, 0, 0, 5);
driverLayout.append(audioDriverBox, ~0, 0, 5);
driverLayout.append(inputDriverLabel, 0, 0, 5);
driverLayout.append(inputDriverBox, ~0, 0);
layout.append(driverLayout, ~0, 0, 5);
layout.append(focusPolicyLabel, ~0, 0);
focusPolicyLayout.append(focusPolicyPause, ~0, 0, 5);
focusPolicyLayout.append(focusPolicyIgnore, ~0, 0, 5);
focusPolicyLayout.append(focusPolicyAllow, ~0, 0);
layout.append(focusPolicyLayout, ~0, 0, 5);
layout.append(compositorPolicyLabel, ~0, 0);
compositorPolicyLayout.append(compositorPolicyNever, ~0, 0, 5);
compositorPolicyLayout.append(compositorPolicyFullScreen, ~0, 0, 5);
compositorPolicyLayout.append(compositorPolicyAlways, ~0, 0);
layout.append(compositorPolicyLayout, ~0, 0);
layout.append(spacer, ~0, ~0);
settingsWindow.append(panelLayout);
lstring list;
list.split(";", video.driver_list());
for(unsigned i = 0; i < list.size(); i++) {
videoDriverBox.append(list[i]);
if(list[i] == config.video.driver) videoDriverBox.setSelection(i);
}
list.split(";", audio.driver_list());
for(unsigned i = 0; i < list.size(); i++) {
audioDriverBox.append(list[i]);
if(list[i] == config.audio.driver) audioDriverBox.setSelection(i);
}
list.split(";", input.driver_list());
for(unsigned i = 0; i < list.size(); i++) {
inputDriverBox.append(list[i]);
if(list[i] == config.input.driver) inputDriverBox.setSelection(i);
}
videoDriverBox.onChange = [this]() {
lstring list;
list.split(";", video.driver_list());
config.video.driver = list[videoDriverBox.selection()];
};
audioDriverBox.onChange = [this]() {
lstring list;
list.split(";", audio.driver_list());
config.audio.driver = list[audioDriverBox.selection()];
};
inputDriverBox.onChange = [this]() {
lstring list;
list.split(";", input.driver_list());
config.input.driver = list[inputDriverBox.selection()];
};
focusPolicyPause.onTick = [] { config.settings.focusPolicy = 0; };
focusPolicyIgnore.onTick = [] { config.settings.focusPolicy = 1; };
focusPolicyAllow.onTick = [] { config.settings.focusPolicy = 2; };
compositorPolicyNever.onTick = [] { config.settings.compositorPolicy = 0; compositor::enable(application.compositorActive); };
compositorPolicyFullScreen.onTick = [] { config.settings.compositorPolicy = 1; };
compositorPolicyAlways.onTick = [] { config.settings.compositorPolicy = 2; compositor::enable(false); };
}

View File

@ -1,34 +0,0 @@
struct AdvancedSettings {
HorizontalLayout panelLayout;
Widget panel;
VerticalLayout layout;
Label title;
Label driverSelectionLabel;
HorizontalLayout driverLayout;
Label videoDriverLabel;
ComboBox videoDriverBox;
Label audioDriverLabel;
ComboBox audioDriverBox;
Label inputDriverLabel;
ComboBox inputDriverBox;
Label focusPolicyLabel;
HorizontalLayout focusPolicyLayout;
RadioBox focusPolicyPause;
RadioBox focusPolicyIgnore;
RadioBox focusPolicyAllow;
Label compositorPolicyLabel;
HorizontalLayout compositorPolicyLayout;
RadioBox compositorPolicyNever;
RadioBox compositorPolicyFullScreen;
RadioBox compositorPolicyAlways;
Widget spacer;
void create();
};
extern AdvancedSettings advancedSettings;

View File

@ -1,64 +0,0 @@
AudioSettings audioSettings;
void AudioSettings::create() {
title.setText("Audio Settings");
title.setFont(application.titleFont);
frequencyLabel.setText("Frequency:");
frequencySlider.setLength(2001);
volumeLabel.setText("Volume:");
volumeSlider.setLength(201);
balanceLabel.setText("Balance:");
balanceSlider.setLength(201);
panelLayout.setMargin(5);
panelLayout.append(panel, SettingsWindow::PanelWidth, ~0, 5);
panelLayout.append(layout, ~0, ~0);
layout.append(title, ~0, 0, 5);
frequencyLayout.append(frequencyLabel, 70, 0);
frequencyLayout.append(frequencyValue, 60, 0);
frequencyLayout.append(frequencySlider, ~0, 0);
layout.append(frequencyLayout, ~0, 0);
volumeLayout.append(volumeLabel, 70, 0);
volumeLayout.append(volumeValue, 60, 0);
volumeLayout.append(volumeSlider, ~0, 0);
layout.append(volumeLayout, ~0, 0);
balanceLayout.append(balanceLabel, 70, 0);
balanceLayout.append(balanceValue, 60, 0);
balanceLayout.append(balanceSlider, ~0, 0);
layout.append(balanceLayout, ~0, 0);
layout.append(spacer, ~0, ~0);
settingsWindow.append(panelLayout);
frequencySlider.onChange = [this] {
config.audio.inputFrequency = frequencySlider.position() + 31000;
dspaudio.setFrequency(config.audio.inputFrequency);
frequencyValue.setText({ config.audio.inputFrequency, "hz" });
};
volumeSlider.onChange = [this] {
config.audio.volume = volumeSlider.position();
dspaudio.setVolume((double)config.audio.volume / 100.0);
volumeValue.setText({ config.audio.volume, "%" });
};
balanceSlider.onChange = [this] {
config.audio.balance = balanceSlider.position();
dspaudio.setBalance((double)((signed)config.audio.balance - 100) / 100.0);
balanceValue.setText({ (signed)config.audio.balance - 100 });
};
frequencySlider.setPosition(config.audio.inputFrequency - 31000);
frequencyValue.setText({ config.audio.inputFrequency, "hz" });
volumeSlider.setPosition(config.audio.volume);
volumeValue.setText({ config.audio.volume, "%" });
balanceSlider.setPosition(config.audio.balance);
balanceValue.setText({ (signed)config.audio.balance - 100 });
}

View File

@ -1,27 +0,0 @@
struct AudioSettings {
HorizontalLayout panelLayout;
Widget panel;
VerticalLayout layout;
Label title;
HorizontalLayout frequencyLayout;
Label frequencyLabel;
Label frequencyValue;
HorizontalSlider frequencySlider;
HorizontalLayout volumeLayout;
Label volumeLabel;
Label volumeValue;
HorizontalSlider volumeSlider;
HorizontalLayout balanceLayout;
Label balanceLabel;
Label balanceValue;
HorizontalSlider balanceSlider;
Widget spacer;
void create();
};
extern AudioSettings audioSettings;

View File

@ -1,271 +0,0 @@
InputSettings inputSettings;
static InputMapper::AbstractInput *activeInput = 0;
void InputSettings::create() {
title.setText("Input Settings");
title.setFont(application.titleFont);
locked = false;
activeInput = 0;
activeMouse = 0;
joypadsCalibrated = false;
joypadsCalibrating = false;
portLabel.setText("Port:");
portBox.append(inputMapper.port1.name);
portBox.append(inputMapper.port2.name);
portBox.append(inputMapper.hotkeys.name);
deviceLabel.setText("Device:");
clearButton.setText("Clear");
mappingList.setHeaderText("Name", "Mapping");
mappingList.setHeaderVisible(true);
panelLayout.setMargin(5);
panelLayout.append(panel, SettingsWindow::PanelWidth, ~0, 5);
panelLayout.append(layout, ~0, ~0);
layout.append(title, ~0, 0, 5);
selectionLayout.append(portLabel, 0, 0, 5);
selectionLayout.append(portBox, ~0, 0, 5);
selectionLayout.append(deviceLabel, 0, 0, 5);
selectionLayout.append(deviceBox, ~0, 0);
layout.append(selectionLayout, ~0, 0, 5);
layout.append(mappingList, ~0, ~0, 5);
controlLayout.append(customButton1, 100, 0, 5);
controlLayout.append(customButton2, 100, 0, 5);
controlLayout.append(customButton3, 100, 0, 5);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(clearButton, 80, 0);
layout.append(controlLayout, ~0, 0);
settingsWindow.append(panelLayout);
clearButton.setEnabled(false);
portBox.onChange = { &InputSettings::portChanged, this };
deviceBox.onChange = { &InputSettings::deviceChanged, this };
mappingList.onActivate = { &InputSettings::assignInput, this };
mappingList.onChange = [this] {
clearButton.setEnabled(mappingList.selected());
};
customButton1.onTick = [this]() { manualInput(1); };
customButton2.onTick = [this]() { manualInput(2); };
customButton3.onTick = [this]() { manualInput(3); };
clearButton.onTick = [this]() { manualInput(0); };
portChanged();
}
void InputSettings::focus() {
mappingList.autoSizeColumns();
customButton1.setVisible(false);
customButton2.setVisible(false);
customButton3.setVisible(false);
}
void InputSettings::portChanged() {
locked = true;
deviceBox.reset();
InputMapper::ControllerPort &port = (
portBox.selection() == 0 ? (InputMapper::ControllerPort&)inputMapper.port1 :
portBox.selection() == 1 ? (InputMapper::ControllerPort&)inputMapper.port2 :
/*portBox.selection() == 2*/ (InputMapper::ControllerPort&)inputMapper.hotkeys
);
for(unsigned i = 0; i < port.size(); i++) deviceBox.append(port[i]->name);
locked = false;
deviceChanged();
}
void InputSettings::deviceChanged() {
if(locked) return;
locked = true;
mappingList.reset();
InputMapper::ControllerPort &port = (
portBox.selection() == 0 ? (InputMapper::ControllerPort&)inputMapper.port1 :
portBox.selection() == 1 ? (InputMapper::ControllerPort&)inputMapper.port2 :
/*portBox.selection() == 2*/ (InputMapper::ControllerPort&)inputMapper.hotkeys
);
InputMapper::Controller &controller = (InputMapper::Controller&)*port[deviceBox.selection()];
for(unsigned i = 0; i < controller.size(); i++) {
string mapping = controller[i]->mapping;
if(mapping == "") mapping = "None";
mappingList.append(controller[i]->name, mapping);
}
mappingList.autoSizeColumns();
clearButton.setEnabled(mappingList.selected());
locked = false;
}
void InputSettings::mappingChanged() {
if(locked) return;
locked = true;
InputMapper::ControllerPort &port = (
portBox.selection() == 0 ? (InputMapper::ControllerPort&)inputMapper.port1 :
portBox.selection() == 1 ? (InputMapper::ControllerPort&)inputMapper.port2 :
/*portBox.selection() == 2*/ (InputMapper::ControllerPort&)inputMapper.hotkeys
);
InputMapper::Controller &controller = (InputMapper::Controller&)*port[deviceBox.selection()];
for(unsigned i = 0; i < controller.size(); i++) {
string mapping = controller[i]->mapping;
if(mapping == "") mapping = "None";
mappingList.modify(i, controller[i]->name, mapping);
}
mappingList.autoSizeColumns();
locked = false;
}
void InputSettings::assignInput() {
if(mappingList.selected() == false) return;
InputMapper::ControllerPort &port = (
portBox.selection() == 0 ? (InputMapper::ControllerPort&)inputMapper.port1 :
portBox.selection() == 1 ? (InputMapper::ControllerPort&)inputMapper.port2 :
/*portBox.selection() == 2*/ (InputMapper::ControllerPort&)inputMapper.hotkeys
);
InputMapper::Controller &controller = (InputMapper::Controller&)*port[deviceBox.selection()];
settingsWindow.panel.setEnabled(false);
portBox.setEnabled(false);
deviceBox.setEnabled(false);
mappingList.setEnabled(false);
inputMapper.poll(); //flush any pending keypresses
activeInput = controller[mappingList.selection()];
settingsWindow.setStatusText({ "Set assignment for [", activeInput->name, "] ..." });
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
customButton1.setText("Mouse X-axis");
customButton2.setText("Mouse Y-axis");
customButton3.setText("Mouse Z-axis");
} else {
customButton1.setText("Mouse Left");
customButton2.setText("Mouse Middle");
customButton3.setText("Mouse Right");
}
customButton1.setVisible(true);
customButton2.setVisible(true);
customButton3.setVisible(true);
}
void InputSettings::manualInput(unsigned button) {
if(activeInput == 0 && button == 0) return clearInput();
if(activeInput == 0) return;
switch(button) {
case 0:
setMapping("");
break;
case 1:
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Xaxis]));
} else {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button0]));
}
break;
case 2:
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Yaxis]));
} else {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button1]));
}
break;
case 3:
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Zaxis]));
} else {
setMapping(Scancode::encode(mouse(inputSettings.activeMouse)[Mouse::Button2]));
}
break;
}
}
void InputSettings::clearInput() {
if(mappingList.selected() == false) return;
InputMapper::ControllerPort &port = (
portBox.selection() == 0 ? (InputMapper::ControllerPort&)inputMapper.port1 :
portBox.selection() == 1 ? (InputMapper::ControllerPort&)inputMapper.port2 :
/*portBox.selection() == 2*/ (InputMapper::ControllerPort&)inputMapper.hotkeys
);
InputMapper::Controller &controller = (InputMapper::Controller&)*port[deviceBox.selection()];
controller[mappingList.selection()]->mapping = "";
inputMapper.bind();
endAssignment();
}
void InputSettings::setMapping(const string &mapping) {
activeInput->mapping = mapping;
inputMapper.bind();
endAssignment();
}
void InputSettings::endAssignment() {
activeInput = 0;
settingsWindow.panel.setEnabled(true);
portBox.setEnabled(true);
deviceBox.setEnabled(true);
mappingList.setEnabled(true);
settingsWindow.setStatusText("");
customButton1.setVisible(false);
customButton2.setVisible(false);
customButton3.setVisible(false);
mappingChanged();
mappingList.setFocused();
}
void InputSettings::inputEvent(uint16_t scancode, int16_t value) {
if(activeInput == 0) return; //not remapping any keys right now?
string mapping = Scancode::encode(scancode);
if(auto position = strpos(mapping, "::Escape")) return setMapping("");
if(dynamic_cast<InputMapper::AnalogInput*>(activeInput)) {
} else if(dynamic_cast<InputMapper::DigitalInput*>(activeInput)) {
if(Keyboard::isAnyKey(scancode) && value) {
setMapping(mapping);
} else if(Mouse::isAnyButton(scancode) && value) {
activeMouse = Mouse::numberDecode(scancode);
} else if(Joypad::isAnyHat(scancode) && value) {
if(value == Joypad::HatUp) setMapping({ mapping, ".Up" });
else if(value == Joypad::HatDown) setMapping({ mapping, ".Down" });
else if(value == Joypad::HatLeft) setMapping({ mapping, ".Left" });
else if(value == Joypad::HatRight) setMapping({ mapping, ".Right" });
} else if(Joypad::isAnyAxis(scancode)) {
if(joypadsCalibrated == false) return calibrateJoypads();
unsigned joypadNumber = Joypad::numberDecode(scancode);
unsigned axisNumber = Joypad::axisDecode(scancode);
int16_t calibration = joypadCalibration[joypadNumber][axisNumber];
if(calibration > -12288 && calibration < +12288 && value < -24576) setMapping({ mapping, ".Lo" });
else if(calibration > -12288 && calibration < +12288 && value > +24576) setMapping({ mapping, ".Hi" });
else if(calibration <= -12288 && value >= +12288) setMapping({ mapping, ".Hi" });
else if(calibration >= +12288 && value <= -12288) setMapping({ mapping, ".Lo" });
} else if(Joypad::isAnyButton(scancode) && value) {
setMapping(mapping);
}
}
}
void InputSettings::calibrateJoypads() {
if(joypadsCalibrating == true) return;
joypadsCalibrating = true;
MessageWindow::information(settingsWindow,
"Analog joypads must be calibrated prior to use.\n\n"
"Please move all analog axes, and press all analog buttons.\n"
"Please do this for every controller you wish to use.\n"
"Once finished, please let go of all axes and buttons, and press OK."
);
inputMapper.poll();
for(unsigned j = 0; j < Joypad::Count &&j<2; j++) {
for(unsigned a = 0; a < Joypad::Axes; a++) {
joypadCalibration[j][a] = inputMapper.value(joypad(j).axis(a));
}
}
joypadsCalibrating = false;
joypadsCalibrated = true;
}

View File

@ -1,46 +0,0 @@
struct InputSettings {
HorizontalLayout panelLayout;
Widget panel;
VerticalLayout layout;
Label title;
HorizontalLayout selectionLayout;
Label portLabel;
ComboBox portBox;
Label deviceLabel;
ComboBox deviceBox;
ListView mappingList;
HorizontalLayout controlLayout;
Button customButton1;
Button customButton2;
Button customButton3;
Widget spacer;
Button clearButton;
void create();
void focus();
//
bool locked;
bool joypadsCalibrated;
bool joypadsCalibrating;
int16_t joypadCalibration[Joypad::Count][Joypad::Axes];
unsigned activeMouse;
void portChanged();
void deviceChanged();
void mappingChanged();
void assignInput();
void manualInput(unsigned button);
void clearInput();
void setMapping(const string &mapping);
void endAssignment();
void inputEvent(uint16_t scancode, int16_t value);
void calibrateJoypads();
};
extern InputSettings inputSettings;

View File

@ -1,43 +0,0 @@
#include "../base.hpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"
#include "advanced.cpp"
SettingsWindow settingsWindow;
void SettingsWindow::create() {
setTitle("Configuration Settings");
application.addWindow(this, "SettingsWindow", "160,160");
setStatusFont(application.proportionalFontBold);
setStatusVisible();
panel.append("Video");
panel.append("Audio");
panel.append("Input");
panel.append("Advanced");
layout.setMargin(5);
layout.append(panel, PanelWidth, ~0, 5);
append(layout);
setGeometry({ 0, 0, 640, 360 });
videoSettings.create();
audioSettings.create();
inputSettings.create();
advancedSettings.create();
panel.onChange = { &SettingsWindow::change, this };
panel.setSelection(2);
change();
onClose = [] { inputSettings.endAssignment(); };
}
void SettingsWindow::change() {
unsigned selection = panel.selection();
videoSettings.panelLayout.setVisible(selection == 0);
audioSettings.panelLayout.setVisible(selection == 1);
inputSettings.panelLayout.setVisible(selection == 2);
advancedSettings.panelLayout.setVisible(selection == 3);
if(selection == 2) inputSettings.focus();
}

View File

@ -1,16 +0,0 @@
struct SettingsWindow : TopLevelWindow {
enum : unsigned { PanelWidth = 120 };
HorizontalLayout layout;
ListView panel;
void create();
void change();
};
extern SettingsWindow settingsWindow;
#include "video.hpp"
#include "audio.hpp"
#include "input.hpp"
#include "advanced.hpp"

View File

@ -1,89 +0,0 @@
VideoSettings videoSettings;
void VideoSettings::create() {
title.setText("Video Settings");
title.setFont(application.titleFont);
colorAdjustmentLabel.setText("Color adjustment:");
colorAdjustmentLabel.setFont(application.proportionalFontBold);
brightnessLabel.setText("Brightness:");
brightnessSlider.setLength(201);
contrastLabel.setText("Contrast:");
contrastSlider.setLength(201);
gammaLabel.setText("Gamma:");
gammaSlider.setLength(201);
gammaRampCheck.setText("Enable NTSC gamma ramp simulation");
fullscreenLabel.setText("Fullscreen:");
fullscreenLabel.setFont(application.proportionalFontBold);
fullscreenCenter.setText("Center");
fullscreenScale.setText("Scale");
fullscreenStretch.setText("Stretch");
RadioBox::group(fullscreenCenter, fullscreenScale, fullscreenStretch);
panelLayout.setMargin(5);
panelLayout.append(panel, SettingsWindow::PanelWidth, ~0, 5);
panelLayout.append(layout, ~0, ~0);
layout.append(title, ~0, 0, 5);
layout.append(colorAdjustmentLabel, ~0, 0 );
brightnessLayout.append(brightnessLabel, 80, 0 );
brightnessLayout.append(brightnessValue, 50, 0 );
brightnessLayout.append(brightnessSlider, ~0, 0 );
layout.append(brightnessLayout, ~0, 0 );
contrastLayout.append(contrastLabel, 80, 0 );
contrastLayout.append(contrastValue, 50, 0 );
contrastLayout.append(contrastSlider, ~0, 0 );
layout.append(contrastLayout, ~0, 0 );
gammaLayout.append(gammaLabel, 80, 0 );
gammaLayout.append(gammaValue, 50, 0 );
gammaLayout.append(gammaSlider, ~0, 0 );
layout.append(gammaLayout, ~0, 0 );
layout.append(gammaRampCheck, ~0, 0, 5);
layout.append(fullscreenLabel, ~0, 0 );
fullscreenLayout.append(fullscreenCenter, ~0, 0, 5);
fullscreenLayout.append(fullscreenScale, ~0, 0, 5);
fullscreenLayout.append(fullscreenStretch, ~0, 0 );
layout.append(fullscreenLayout, ~0, 0 );
layout.append(spacer, ~0, ~0);
settingsWindow.append(panelLayout);
brightnessSlider.setPosition(config.video.brightness);
brightnessValue.setText({ config.video.brightness, "%" });
contrastSlider.setPosition(config.video.contrast);
contrastValue.setText({ config.video.contrast, "%" });
gammaSlider.setPosition(config.video.gamma);
gammaValue.setText({ config.video.gamma, "%" });
gammaRampCheck.setChecked(config.video.useGammaRamp);
switch(config.video.fullscreenScale) { default:
case 0: fullscreenCenter.setChecked(); break;
case 1: fullscreenScale.setChecked(); break;
case 2: fullscreenStretch.setChecked(); break;
}
contrastSlider.onChange = brightnessSlider.onChange = gammaSlider.onChange = gammaRampCheck.onTick =
{ &VideoSettings::adjust, this };
fullscreenCenter.onTick = [] { config.video.fullscreenScale = 0; };
fullscreenScale.onTick = [] { config.video.fullscreenScale = 1; };
fullscreenStretch.onTick = [] { config.video.fullscreenScale = 2; };
adjust();
}
void VideoSettings::adjust() {
brightnessValue.setText({ brightnessSlider.position(), "%" });
contrastValue.setText({ contrastSlider.position(), "%" });
gammaValue.setText({ gammaSlider.position(), "%" });
config.video.brightness = brightnessSlider.position();
config.video.contrast = contrastSlider.position();
config.video.gamma = gammaSlider.position();
config.video.useGammaRamp = gammaRampCheck.checked();
palette.update();
}

View File

@ -1,38 +0,0 @@
struct VideoSettings {
HorizontalLayout panelLayout;
Widget panel;
VerticalLayout layout;
Label title;
Label colorAdjustmentLabel;
Label brightnessLabel;
HorizontalLayout brightnessLayout;
Label brightnessValue;
HorizontalSlider brightnessSlider;
Label contrastLabel;
HorizontalLayout contrastLayout;
Label contrastValue;
HorizontalSlider contrastSlider;
Label gammaLabel;
HorizontalLayout gammaLayout;
Label gammaValue;
HorizontalSlider gammaSlider;
CheckBox gammaRampCheck;
Label fullscreenLabel;
HorizontalLayout fullscreenLayout;
RadioBox fullscreenCenter;
RadioBox fullscreenScale;
RadioBox fullscreenStretch;
Widget spacer;
void create();
void adjust();
};
extern VideoSettings videoSettings;

Some files were not shown because too many files have changed in this diff Show More