mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
98ec338285
commit
979aa640af
|
@ -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:;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace GameBoy {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgameboy";
|
||||
static unsigned SerializerVersion = 2;
|
||||
static const unsigned SerializerVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void DSP::setResamplerFrequency(double frequency) {
|
||||
resampler.frequency = frequency;
|
||||
resampler.fraction = 0;
|
||||
resampler.step = settings.frequency / resampler.frequency;
|
||||
void DSP::setResamplerFrequency(real frequency) {
|
||||
resampler->frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace nall {
|
|||
};
|
||||
|
||||
template<typename T> class optional {
|
||||
public:
|
||||
bool valid;
|
||||
T value;
|
||||
public:
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ struct Cartridge : property<Cartridge> {
|
|||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
|
||||
//privileged:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -40,7 +40,10 @@ struct CPU : Processor {
|
|||
|
||||
void oam_dma();
|
||||
|
||||
bool trace;
|
||||
void serialize(serializer&);
|
||||
|
||||
//internal:
|
||||
bool trace;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace NES {
|
||||
|
||||
#include "serialization.cpp"
|
||||
Input input;
|
||||
|
||||
void Input::latch(bool data) {
|
||||
|
|
|
@ -11,6 +11,8 @@ struct Input {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer &s);
|
||||
|
||||
private:
|
||||
Device port1;
|
||||
Device port2;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ struct AOROM : Mapper {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint4 prg_bank;
|
||||
bool mirror_select;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ struct BandaiFCG : Mapper {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
void clock();
|
||||
|
|
|
@ -40,3 +40,7 @@ void CNROM::power() {
|
|||
void CNROM::reset() {
|
||||
chr_bank = 0;
|
||||
}
|
||||
|
||||
void CNROM::serialize(serializer &s) {
|
||||
s.integer(chr_bank);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ struct CNROM : Mapper {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint2 chr_bank;
|
||||
};
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Mapper {
|
|||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void serialize(serializer&) = 0;
|
||||
};
|
||||
|
||||
#include "none/none.hpp"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ struct MMC1 : Mapper {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint8 prg_ram[8192];
|
||||
uint8 r[4];
|
||||
|
|
|
@ -37,3 +37,6 @@ void None::power() {
|
|||
|
||||
void None::reset() {
|
||||
}
|
||||
|
||||
void None::serialize(serializer &s) {
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ struct None : Mapper {
|
|||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern None none;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
namespace NES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bnes";
|
||||
static const unsigned SerializerVersion = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -35,6 +35,8 @@ struct PPU : Processor {
|
|||
void raster_sprite();
|
||||
void raster_scanline();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
struct Status {
|
||||
uint8 mdr;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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");
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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());
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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];
|
||||
}
|
|
@ -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;
|
|
@ -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(¤t);
|
||||
if(current != previous) {
|
||||
utility.setStatus({ "FPS: ", frameCounter });
|
||||
frameCounter = 0;
|
||||
previous = current;
|
||||
}
|
||||
|
||||
if(captureScreenshot) {
|
||||
captureScreenshot = false;
|
||||
time_t currentTime = time(0);
|
||||
tm *info = localtime(¤tTime);
|
||||
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;
|
||||
}
|
|
@ -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;
|
|
@ -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"));
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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;
|
|
@ -1,2 +0,0 @@
|
|||
1 24 "../data/bsnes.Manifest"
|
||||
2 ICON DISCARDABLE "../data/bsnes.ico"
|
|
@ -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); };
|
||||
}
|
|
@ -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;
|
|
@ -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 });
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
|
@ -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"
|
|
@ -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();
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue