diff --git a/bsnes/Makefile b/bsnes/Makefile
index ffe45eb2..4064864b 100755
--- a/bsnes/Makefile
+++ b/bsnes/Makefile
@@ -17,6 +17,7 @@ objects := libco
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize
+
ifeq ($(pgo),instrument)
flags += -fprofile-generate
link += -lgcov
diff --git a/bsnes/data/cheats.xml b/bsnes/data/cheats.xml
index ff2092ad..e010d7b0 100755
--- a/bsnes/data/cheats.xml
+++ b/bsnes/data/cheats.xml
@@ -7778,7 +7778,7 @@
Captain Commando (USA)
- Invincibility - p1
+ Invincibility - P1
CB4E-7FD4
D04E-7F04
B24E-7F64
@@ -8195,6 +8195,32 @@
F3C1-1F5E
+
+ Choujikuu Yousai Macross - Scrambled Valkyrie (Japan)
+
+ Invincibility
+ 7E0A90FF
+
+
+ Infinite health
+ C268-4F64
+ C269-47A4
+
+
+ Infinite absorb charge
+ C2B2-C4D1
+
+
+ Infinite continues
+ 7E02AA09
+
+
+ Hit anywhere
+ DD6B-3FD7
+ DD65-3467
+ DD66-34D7
+
+
Chrono Trigger (USA)
@@ -10112,10 +10138,6 @@
Infinite lives - P2
DD3B-6FA5
-
- Start with 99 lives - both players
- 7E106799
-
Infinite Shield - P1
7E177BFF
@@ -10137,9 +10159,24 @@
7E17EB08
- Max 2ndGun - P2
+ Max 2nd Gun - P2
7E177C08
+
+ Hit anywhere - both players
+ 6D23-6FA4
+ C22D-A764
+ DD28-67A4
+ DD2E-6D04
+
+
+ Start with the strongest main weapon and 8 lives - both players
+ 3CC3-6DA7
+ 62C3-6467
+ 62C3-6707
+ CBC3-6D07
+ D6C3-6D67
+
Start with 1 Green Power Cube - P1
62CD-AF67
@@ -10184,6 +10221,10 @@
CBC3-6D07
3CC3-6DA7
+
+ Start with 99 lives - both players
+ 7E106799
+
Start on planet B
DFB0-A7DD
@@ -25884,12 +25925,12 @@
Lion King, The (USA)
- Infinite lives
- CE6E-8464
+ Almost infinite health
+ C298-7DDE
- Almost infinite energy
- C298-7DDE
+ Infinite lives
+ CE6E-8464
Roaring doesn't decrease your roar meter
@@ -30419,11 +30460,19 @@
Mary Shelley's Frankenstein (USA)
+
+ Invincibility (blinking)
+ 7E200D09
+
Infinite health
C2B9-1DA4
8533-3F0D
+
+ Infinite health (alt)
+ 7E20431D
+
Energy balls don't deplete health
DDE5-3FB0
@@ -30939,6 +30988,11 @@
F0CA-3FA0
DDCA-34D0
+
+ View ending (select Capcom Championship)
+ 81829BEE
+ 81829CE0
+
Mega Man X (USA) (Rev 1)
@@ -31378,21 +31432,25 @@
Metal Morph (USA)
- Start with 255 continues
- EE6C-1FDD
+ Invincibility
+ 7E038703
- Freeze timer on continue screen
- C2AD-4D65
-
-
- Infinite ammo for all weapons (to change weapons, turn code off, pick up new weapon, turn back on)
+ Infinite ammo for all weapons (disable to change weapons)
C926-C7A1
Infinite continues
C9A9-4F65
+
+ Infinite time on continue screen
+ C2AD-4D65
+
+
+ Start with 255 continues
+ EE6C-1FDD
+
Metal Warriors (USA)
@@ -49328,6 +49386,20 @@
Infinite lives
A268-4716
+
+ Hit anywhere
+ 6DA9-1DA7
+ DD87-1D41
+ DDA5-4FD7
+
+
+ Keep main weapon after dying
+ C26C-4446
+
+
+ Start with strongest main weapon
+ 62C4-1F46
+
Super Off Road - The Baja (USA)
@@ -49558,18 +49630,19 @@
Super R-Type (USA)
-
- Start with Invincibility (blinking)
- 82C2-DF61
-
-
- Start with Invincibility (blinking) (alt)
- 7E15BD00
-
Infinite lives
C2C7-6D0F
+
+ Infinite FORCE once obtained
+ C267-A4D9
+
+
+ Always fire charged shots (hold B)
+ 7287-0FD1
+ DD87-0D61
+
Spiral motion gun takes less time to power up
6D80-6DD1
@@ -49602,8 +49675,12 @@
7D68-6D6B
- Once FORCE has been obtained, keep it forever
- C267-A4D9
+ Start with Invincibility (blinking)
+ 82C2-DF61
+
+
+ Start with Invincibility (blinking) (alt)
+ 7E15BD00
Start with 1 life instead of 3
@@ -57641,7 +57718,18 @@
Zombies Ate My Neighbors (USA)
- Infinite health
+ Invincibility, hit anywhere, get items from anywhere (do not use martian gun) - both players
+ 6DEC-14D4
+ DD2F-476D
+ DD8D-37D7
+ DD8E-C767
+
+
+ Invincibility - both players
+ DD2F-476D
+
+
+ Infinite health - both players
3C20-4D0D
@@ -57653,7 +57741,7 @@
7E1CBA0A
- Infinite weapons
+ Infinite weapons - both players
DD30-1FA7
@@ -57665,7 +57753,7 @@
DD39-34D4
- Infinite lives
+ Infinite lives - both players
82AA-CF07
@@ -57860,6 +57948,10 @@
Infinite Weed Wacker - P2
7E1CF250
+
+ Victims are invincible
+ 18CC-37DA
+
Package of 99 Squirtgun shots worth 999
DBEC-4704
diff --git a/bsnes/nall/bps/delta.hpp b/bsnes/nall/bps/delta.hpp
index 53bae28c..a3af047c 100755
--- a/bsnes/nall/bps/delta.hpp
+++ b/bsnes/nall/bps/delta.hpp
@@ -10,6 +10,9 @@
namespace nall {
struct bpsdelta {
+ inline void source(const uint8_t *data, unsigned size);
+ inline void target(const uint8_t *data, unsigned size);
+
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
@@ -34,17 +37,25 @@ protected:
unsigned targetSize;
};
+void bpsdelta::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+void bpsdelta::target(const uint8_t *data, unsigned size) {
+ targetData = data;
+ targetSize = size;
+}
+
bool bpsdelta::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
- sourceData = sourceFile.data();
- sourceSize = sourceFile.size();
+ source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpsdelta::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
- targetData = targetFile.data();
- targetSize = targetFile.size();
+ target(targetFile.data(), targetFile.size());
return true;
}
@@ -85,11 +96,8 @@ bool bpsdelta::create(const string &filename, const string &metadata) {
encode(markupSize);
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
- Node *sourceTree[65536];
- Node *targetTree[65536];
-
- for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0;
- for(unsigned n = 0; n < 65536; n++) targetTree[n] = 0;
+ Node *sourceTree[65536], *targetTree[65536];
+ for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
//source tree creation
for(unsigned offset = 0; offset < sourceSize; offset++) {
@@ -118,6 +126,15 @@ bool bpsdelta::create(const string &filename, const string &metadata) {
uint16_t symbol = targetData[outputOffset + 0];
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
+ { //source read
+ unsigned length = 0, offset = outputOffset;
+ while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
+ length++;
+ offset++;
+ }
+ if(length > maxLength) maxLength = length, mode = SourceRead;
+ }
+
{ //source copy
Node *node = sourceTree[symbol];
while(node) {
@@ -144,15 +161,6 @@ bool bpsdelta::create(const string &filename, const string &metadata) {
targetTree[symbol] = node;
}
- { //source read
- unsigned length = 0, offset = outputOffset;
- while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
- length++;
- offset++;
- }
- if(length > maxLength) maxLength = length, mode = SourceRead;
- }
-
{ //target read
if(maxLength < 4) {
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
diff --git a/bsnes/nall/bps/linear.hpp b/bsnes/nall/bps/linear.hpp
index c22df39a..df840283 100755
--- a/bsnes/nall/bps/linear.hpp
+++ b/bsnes/nall/bps/linear.hpp
@@ -10,6 +10,9 @@
namespace nall {
struct bpslinear {
+ inline void source(const uint8_t *data, unsigned size);
+ inline void target(const uint8_t *data, unsigned size);
+
inline bool source(const string &filename);
inline bool target(const string &filename);
inline bool create(const string &filename, const string &metadata = "");
@@ -27,17 +30,25 @@ protected:
unsigned targetSize;
};
+void bpslinear::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+void bpslinear::target(const uint8_t *data, unsigned size) {
+ targetData = data;
+ targetSize = size;
+}
+
bool bpslinear::source(const string &filename) {
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
- sourceData = sourceFile.data();
- sourceSize = sourceFile.size();
+ source(sourceFile.data(), sourceFile.size());
return true;
}
bool bpslinear::target(const string &filename) {
if(targetFile.open(filename, filemap::mode::read) == false) return false;
- targetData = targetFile.data();
- targetSize = targetFile.size();
+ target(targetFile.data(), targetFile.size());
return true;
}
@@ -90,7 +101,7 @@ bool bpslinear::create(const string &filename, const string &metadata) {
while(outputOffset < targetSize) {
unsigned sourceLength = 0;
- for(unsigned n = 0; outputOffset + n < sourceSize; n++) {
+ for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
sourceLength++;
}
diff --git a/bsnes/nall/bps/metadata.hpp b/bsnes/nall/bps/metadata.hpp
new file mode 100755
index 00000000..46759e6f
--- /dev/null
+++ b/bsnes/nall/bps/metadata.hpp
@@ -0,0 +1,121 @@
+#ifndef NALL_BPS_METADATA_HPP
+#define NALL_BPS_METADATA_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct bpsmetadata {
+ inline bool load(const string &filename);
+ inline bool save(const string &filename, const string &metadata);
+ inline string metadata() const;
+
+protected:
+ file sourceFile;
+ string metadataString;
+};
+
+bool bpsmetadata::load(const string &filename) {
+ if(sourceFile.open(filename, file::mode::read) == false) return false;
+
+ auto read = [&]() -> uint8_t {
+ return sourceFile.read();
+ };
+
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = read();
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ if(read() != 'B') return false;
+ if(read() != 'P') return false;
+ if(read() != 'S') return false;
+ if(read() != '1') return false;
+ decode();
+ decode();
+ unsigned metadataSize = decode();
+ char data[metadataSize + 1];
+ for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
+ data[metadataSize] = 0;
+ metadataString = (const char*)data;
+
+ return true;
+}
+
+bool bpsmetadata::save(const string &filename, const string &metadata) {
+ file targetFile;
+ if(targetFile.open(filename, file::mode::write) == false) return false;
+ if(sourceFile.open() == false) return false;
+ sourceFile.seek(0);
+
+ auto read = [&]() -> uint8_t {
+ return sourceFile.read();
+ };
+
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = read();
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ uint32_t checksum = ~0;
+
+ auto write = [&](uint8_t data) {
+ targetFile.write(data);
+ checksum = crc32_adjust(checksum, data);
+ };
+
+ auto encode = [&](uint64_t data) {
+ while(true) {
+ uint64_t x = data & 0x7f;
+ data >>= 7;
+ if(data == 0) {
+ write(0x80 | x);
+ break;
+ }
+ write(x);
+ data--;
+ }
+ };
+
+ for(unsigned n = 0; n < 4; n++) write(read());
+ encode(decode());
+ encode(decode());
+ unsigned sourceLength = decode();
+ unsigned targetLength = metadata.length();
+ encode(targetLength);
+ sourceFile.seek(sourceLength, file::index::relative);
+ for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
+ unsigned length = sourceFile.size() - sourceFile.offset() - 4;
+ for(unsigned n = 0; n < length; n++) write(read());
+ uint32_t outputChecksum = ~checksum;
+ for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
+
+ targetFile.close();
+ return true;
+}
+
+string bpsmetadata::metadata() const {
+ return metadataString;
+}
+
+}
+
+#endif
diff --git a/bsnes/nall/bps/patch.hpp b/bsnes/nall/bps/patch.hpp
index e24eba90..85c4dcae 100755
--- a/bsnes/nall/bps/patch.hpp
+++ b/bsnes/nall/bps/patch.hpp
@@ -18,6 +18,7 @@ struct bpspatch {
inline bool source(const string &filename);
inline bool target(const string &filename);
+ inline string metadata() const;
inline unsigned size() const;
enum result : unsigned {
@@ -49,11 +50,10 @@ protected:
uint8_t *targetData;
unsigned targetSize;
-public:
unsigned modifySourceSize;
unsigned modifyTargetSize;
unsigned modifyMarkupSize;
- string metadata;
+ string metadataString;
};
bool bpspatch::modify(const uint8_t *data, unsigned size) {
@@ -77,6 +77,12 @@ bool bpspatch::modify(const uint8_t *data, unsigned size) {
modifySourceSize = decode();
modifyTargetSize = decode();
modifyMarkupSize = decode();
+
+ char buffer[modifyMarkupSize + 1];
+ for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
+ buffer[modifyMarkupSize] = 0;
+ metadataString = (const char*)buffer;
+
return true;
}
@@ -112,6 +118,10 @@ bool bpspatch::target(const string &filename) {
return true;
}
+string bpspatch::metadata() const {
+ return metadataString;
+}
+
unsigned bpspatch::size() const {
return modifyTargetSize;
}
@@ -152,12 +162,8 @@ bpspatch::result bpspatch::apply() {
modifySourceSize = decode();
modifyTargetSize = decode();
-
modifyMarkupSize = decode();
- char data[modifyMarkupSize + 1];
- for(unsigned n = 0; n < modifyMarkupSize; n++) data[n] = read();
- data[modifyMarkupSize] = 0;
- metadata = (const char*)data;
+ for(unsigned n = 0; n < modifyMarkupSize; n++) read();
if(modifySourceSize > sourceSize) return result::source_too_small;
if(modifyTargetSize > targetSize) return result::target_too_small;
diff --git a/bsnes/nall/dsp.hpp b/bsnes/nall/dsp.hpp
index 52e43327..009c8b6c 100755
--- a/bsnes/nall/dsp.hpp
+++ b/bsnes/nall/dsp.hpp
@@ -1,6 +1,8 @@
#ifndef NALL_DSP_HPP
#define NALL_DSP_HPP
+#define NALL_DSP_INTERNAL_HPP
#include
+#undef NALL_DSP_INTERNAL_HPP
#endif
diff --git a/bsnes/nall/dsp/buffer.hpp b/bsnes/nall/dsp/buffer.hpp
new file mode 100755
index 00000000..c1b85b8d
--- /dev/null
+++ b/bsnes/nall/dsp/buffer.hpp
@@ -0,0 +1,36 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+struct Buffer {
+ double *sample[2];
+ uint16_t rdoffset;
+ uint16_t wroffset;
+
+ inline double& read(bool channel, signed offset = 0) {
+ return sample[channel][(uint16_t)(rdoffset + offset)];
+ }
+
+ inline double& write(bool channel, signed offset = 0) {
+ return sample[channel][(uint16_t)(wroffset + offset)];
+ }
+
+ inline void clear() {
+ for(unsigned n = 0; n < 65536; n++) {
+ sample[0][n] = 0;
+ sample[1][n] = 0;
+ }
+ rdoffset = 0;
+ wroffset = 0;
+ }
+
+ Buffer() {
+ sample[0] = new double[65536];
+ sample[1] = new double[65536];
+ }
+
+ ~Buffer() {
+ delete[] sample[0];
+ delete[] sample[1];
+ }
+};
+
+#endif
diff --git a/bsnes/nall/dsp/core.hpp b/bsnes/nall/dsp/core.hpp
index 20cfdfdc..bec46d93 100755
--- a/bsnes/nall/dsp/core.hpp
+++ b/bsnes/nall/dsp/core.hpp
@@ -1,27 +1,35 @@
-#ifndef NALL_DSP_EFFECT_HPP
-#define NALL_DSP_EFFECT_HPP
+#ifdef NALL_DSP_INTERNAL_HPP
+#include
#include
namespace nall {
-struct dsp {
- inline void set_precision(unsigned precision);
- inline void set_frequency(double frequency);
- inline void set_volume(double volume);
- inline void set_balance(double balance);
- inline void set_echo(double echo);
+struct DSP {
+ enum class Resampler : unsigned {
+ Point,
+ Linear,
+ Cosine,
+ Cubic,
+ Hermite,
+ Average,
+ };
- inline void set_resampler_frequency(double frequency);
+ inline void setPrecision(unsigned precision);
+ inline void setFrequency(double frequency); //inputFrequency
+ inline void setVolume(double volume);
+ inline void setBalance(double balance);
+
+ inline void setResampler(Resampler resampler);
+ inline void setResamplerFrequency(double frequency); //outputFrequency
inline void sample(signed lchannel, signed rchannel);
inline bool pending();
inline void read(signed &lchannel, signed &rchannel);
- inline signed clamp(const unsigned bits, const signed x);
inline void clear();
- inline dsp();
- inline ~dsp();
+ inline DSP();
+ inline ~DSP();
protected:
struct Settings {
@@ -29,196 +37,116 @@ protected:
double frequency;
double volume;
double balance;
- double echo;
//internal
double intensity;
} settings;
- struct Resampler {
+ struct ResamplerSettings {
+ Resampler engine;
double frequency;
//internal
double fraction;
double step;
} resampler;
- inline void resampler_run();
+ inline void resamplerRun();
+ inline void resamplerWrite(double lchannel, double rchannel);
- struct Buffer {
- double *sample[2];
- uint16_t rdoffset;
- uint16_t wroffset;
- inline double& operator()(bool channel, signed offset) { return sample[channel][(uint16_t)(rdoffset + offset)]; }
- inline void write(bool channel, signed offset, double data) { sample[channel][(uint16_t)(wroffset + offset)] = data; }
- } buffer;
+ inline void resamplePoint();
+ inline void resampleLinear();
+ inline void resampleCosine();
+ inline void resampleCubic();
+ inline void resampleHermite();
+ inline void resampleAverage();
- struct Output {
- double *sample[2];
- uint16_t rdoffset;
- uint16_t wroffset;
- inline double& operator()(bool channel, signed offset) { return sample[channel][(uint16_t)(rdoffset + offset)]; }
- inline void write(bool channel, signed offset, double data) { sample[channel][(uint16_t)(wroffset + offset)] = data; }
- } output;
+ #include "buffer.hpp"
+ Buffer buffer;
+ Buffer output;
- inline void adjust_volume();
- inline void adjust_balance();
- inline void adjust_echo();
+ inline void adjustVolume();
+ inline void adjustBalance();
+ inline signed clamp(const unsigned bits, const signed x);
};
-void dsp::set_precision(unsigned precision) {
- settings.precision = precision;
- settings.intensity = 1 << (settings.precision - 1);
-}
+#include "settings.hpp"
-void dsp::set_frequency(double frequency) {
- settings.frequency = frequency;
- resampler.fraction = 0;
- resampler.step = settings.frequency / resampler.frequency;
-}
-
-void dsp::set_volume(double volume) {
- settings.volume = volume;
-}
-
-void dsp::set_balance(double balance) {
- settings.balance = balance;
-}
-
-void dsp::set_echo(double echo) {
- settings.echo = echo;
-}
-
-void dsp::set_resampler_frequency(double frequency) {
- resampler.frequency = frequency;
- resampler.fraction = 0;
- resampler.step = settings.frequency / resampler.frequency;
-}
-
-void dsp::sample(signed lchannel, signed rchannel) {
- buffer.write(0, 0, (double)lchannel / settings.intensity);
- buffer.write(1, 0, (double)rchannel / settings.intensity);
+void DSP::sample(signed lchannel, signed rchannel) {
+ buffer.write(0) = (double)lchannel / settings.intensity;
+ buffer.write(1) = (double)rchannel / settings.intensity;
buffer.wroffset++;
-
-#ifdef DSP_NO_RESAMPLER
- output.write(0, 0, buffer(0, 0));
- output.write(1, 0, buffer(1, 0));
- output.wroffset++;
- buffer.rdoffset++;
-#else
- resampler_run();
-#endif
+ resamplerRun();
}
-bool dsp::pending() {
+bool DSP::pending() {
return output.rdoffset != output.wroffset;
}
-void dsp::read(signed &lchannel, signed &rchannel) {
- adjust_volume();
- adjust_balance();
- adjust_echo();
+void DSP::read(signed &lchannel, signed &rchannel) {
+ adjustVolume();
+ adjustBalance();
- lchannel = clamp(settings.precision, output(0, 0) * settings.intensity);
- rchannel = clamp(settings.precision, output(1, 0) * settings.intensity);
+ lchannel = clamp(settings.precision, output.read(0) * settings.intensity);
+ rchannel = clamp(settings.precision, output.read(1) * settings.intensity);
output.rdoffset++;
}
-void dsp::resampler_run() {
- //4-tap hermite
- while(resampler.fraction <= 1.0) {
- for(unsigned n = 0; n < 2; n++) {
- double a = buffer(n, -3);
- double b = buffer(n, -2);
- double c = buffer(n, -1);
- double d = buffer(n, -0);
-
- const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
- const double bias = 0.0; //-1 = left, 0 = even, +1 = right
-
- double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
-
- mu1 = resampler.fraction;
- mu2 = mu1 * mu1;
- mu3 = mu2 * mu1;
-
- m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
- m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
- m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
- m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
-
- a0 = +2 * mu3 - 3 * mu2 + 1;
- a1 = mu3 - 2 * mu2 + mu1;
- a2 = mu3 - mu2;
- a3 = -2 * mu3 + 3 * mu2;
-
- double result = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
- output.write(n, 0, result);
- }
-
- output.wroffset++;
- resampler.fraction += resampler.step;
+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();
}
-
- buffer.rdoffset++;
- resampler.fraction -= 1.0;
}
-void dsp::adjust_volume() {
- output(0, 0) *= settings.volume;
- output(1, 0) *= settings.volume;
+void DSP::resamplerWrite(double lchannel, double rchannel) {
+ output.write(0) = lchannel;
+ output.write(1) = rchannel;
+ output.wroffset++;
}
-void dsp::adjust_balance() {
- if(settings.balance < 0.0) output(1, 0) *= 1.0 + settings.balance;
- if(settings.balance > 0.0) output(0, 0) *= 1.0 - settings.balance;
+#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() {
+ output.read(0) *= settings.volume;
+ output.read(1) *= settings.volume;
}
-void dsp::adjust_echo() {
- //...
+void DSP::adjustBalance() {
+ if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
+ if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
-signed dsp::clamp(const unsigned bits, const signed x) {
+signed DSP::clamp(const unsigned bits, const signed x) {
const signed b = 1U << (bits - 1);
const signed m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
-void dsp::clear() {
+void DSP::clear() {
resampler.fraction = 0.0;
- resampler.step = 1.0;
- for(unsigned n = 0; n < 65536; n++) {
- buffer.sample[0][n] = 0.0;
- buffer.sample[1][n] = 0.0;
- output.sample[0][n] = 0.0;
- output.sample[1][n] = 0.0;
- }
- buffer.rdoffset = 0;
- buffer.wroffset = 0;
- output.rdoffset = 0;
- output.wroffset = 0;
+ buffer.clear();
+ output.clear();
}
-dsp::dsp() {
- settings.precision = 16;
- settings.frequency = 44100.0;
- settings.volume = 1.0;
- settings.balance = 0.0;
- settings.echo = 0.0;
- settings.intensity = 1 << (settings.precision - 1);
- resampler.frequency = 44100.0;
-
- buffer.sample[0] = new double[65536];
- buffer.sample[1] = new double[65536];
- output.sample[0] = new double[65536];
- output.sample[1] = new double[65536];
-
+DSP::DSP() {
+ setPrecision(16);
+ setFrequency(44100.0);
+ setVolume(1.0);
+ setBalance(0.0);
+ setResampler(Resampler::Hermite);
+ setResamplerFrequency(44100.0);
clear();
}
-dsp::~dsp() {
- delete[] buffer.sample[0];
- delete[] buffer.sample[1];
- delete[] output.sample[0];
- delete[] output.sample[1];
+DSP::~DSP() {
}
}
diff --git a/bsnes/nall/dsp/resample/average.hpp b/bsnes/nall/dsp/resample/average.hpp
new file mode 100755
index 00000000..ca97050e
--- /dev/null
+++ b/bsnes/nall/dsp/resample/average.hpp
@@ -0,0 +1,28 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleAverage() {
+ //can only average if input frequency >= output frequency
+ if(resampler.step < 1.0) return resampleHermite();
+
+ resampler.fraction += 1.0;
+
+ double scalar = 1.0;
+ if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
+
+ output.write(0) += buffer.read(0) * scalar;
+ output.write(1) += buffer.read(1) * scalar;
+
+ if(resampler.fraction >= resampler.step) {
+ output.write(0) /= resampler.step;
+ output.write(1) /= resampler.step;
+ output.wroffset++;
+
+ resampler.fraction -= resampler.step;
+ output.write(0) = buffer.read(0) * resampler.fraction;
+ output.write(1) = buffer.read(1) * resampler.fraction;
+ }
+
+ buffer.rdoffset++;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/resample/cosine.hpp b/bsnes/nall/dsp/resample/cosine.hpp
new file mode 100755
index 00000000..56fcf8fe
--- /dev/null
+++ b/bsnes/nall/dsp/resample/cosine.hpp
@@ -0,0 +1,25 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleCosine() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; n++) {
+ double a = buffer.read(n, -1);
+ double b = buffer.read(n, -0);
+
+ double mu = resampler.fraction;
+ mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
+
+ channel[n] = a * (1.0 - mu) + b * mu;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/resample/cubic.hpp b/bsnes/nall/dsp/resample/cubic.hpp
new file mode 100755
index 00000000..f975f6aa
--- /dev/null
+++ b/bsnes/nall/dsp/resample/cubic.hpp
@@ -0,0 +1,31 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleCubic() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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);
+
+ double mu = resampler.fraction;
+
+ double A = d - c - a + b;
+ double B = a - b - A;
+ double C = c - a;
+ double D = b;
+
+ channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/resample/hermite.hpp b/bsnes/nall/dsp/resample/hermite.hpp
new file mode 100755
index 00000000..a042a55b
--- /dev/null
+++ b/bsnes/nall/dsp/resample/hermite.hpp
@@ -0,0 +1,43 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleHermite() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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);
+
+ const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
+ const double bias = 0.0; //-1 = left, 0 = even, +1 = right
+
+ double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
+
+ mu1 = resampler.fraction;
+ mu2 = mu1 * mu1;
+ mu3 = mu2 * mu1;
+
+ m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
+ m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
+ m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
+ m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
+
+ a0 = +2 * mu3 - 3 * mu2 + 1;
+ a1 = mu3 - 2 * mu2 + mu1;
+ a2 = mu3 - mu2;
+ a3 = -2 * mu3 + 3 * mu2;
+
+ channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/resample/linear.hpp b/bsnes/nall/dsp/resample/linear.hpp
new file mode 100755
index 00000000..f4e48f57
--- /dev/null
+++ b/bsnes/nall/dsp/resample/linear.hpp
@@ -0,0 +1,24 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleLinear() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; n++) {
+ double a = buffer.read(n, -1);
+ double b = buffer.read(n, -0);
+
+ double mu = resampler.fraction;
+
+ channel[n] = a * (1.0 - mu) + b * mu;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/resample/point.hpp b/bsnes/nall/dsp/resample/point.hpp
new file mode 100755
index 00000000..51d35935
--- /dev/null
+++ b/bsnes/nall/dsp/resample/point.hpp
@@ -0,0 +1,24 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resamplePoint() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/bsnes/nall/dsp/settings.hpp b/bsnes/nall/dsp/settings.hpp
new file mode 100755
index 00000000..c7313d3d
--- /dev/null
+++ b/bsnes/nall/dsp/settings.hpp
@@ -0,0 +1,32 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::setPrecision(unsigned precision) {
+ settings.precision = precision;
+ settings.intensity = 1 << (settings.precision - 1);
+}
+
+void DSP::setFrequency(double frequency) {
+ settings.frequency = frequency;
+ resampler.fraction = 0;
+ resampler.step = settings.frequency / resampler.frequency;
+}
+
+void DSP::setVolume(double volume) {
+ settings.volume = volume;
+}
+
+void DSP::setBalance(double balance) {
+ settings.balance = balance;
+}
+
+void DSP::setResampler(Resampler engine) {
+ resampler.engine = engine;
+}
+
+void DSP::setResamplerFrequency(double frequency) {
+ resampler.frequency = frequency;
+ resampler.fraction = 0;
+ resampler.step = settings.frequency / resampler.frequency;
+}
+
+#endif
diff --git a/bsnes/nall/filemap.hpp b/bsnes/nall/filemap.hpp
index f04ec899..7eeac2b0 100755
--- a/bsnes/nall/filemap.hpp
+++ b/bsnes/nall/filemap.hpp
@@ -47,6 +47,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
+ if(file::exists(filename) && file::size(filename) == 0) {
+ p_handle = 0;
+ p_size = 0;
+ return true;
+ }
+
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
@@ -133,6 +139,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
+ if(file::exists(filename) && file::size(filename) == 0) {
+ p_handle = 0;
+ p_size = 0;
+ return true;
+ }
+
int open_flags, mmap_flags;
switch(mode_) {
diff --git a/bsnes/nall/platform.hpp b/bsnes/nall/platform.hpp
index 5c226e3e..8e7bb5fe 100755
--- a/bsnes/nall/platform.hpp
+++ b/bsnes/nall/platform.hpp
@@ -95,6 +95,7 @@
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
+ for(unsigned n = 0; resolvedname[n]; n++) if(resolvedname[n] == '\\') resolvedname[n] = '/';
return resolvedname;
}
@@ -102,6 +103,7 @@
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
+ for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
@@ -109,6 +111,7 @@
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
+ for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
#else
diff --git a/bsnes/snes/audio/audio.cpp b/bsnes/snes/audio/audio.cpp
index eaea1c63..d715c69c 100755
--- a/bsnes/snes/audio/audio.cpp
+++ b/bsnes/snes/audio/audio.cpp
@@ -4,19 +4,17 @@ Audio audio;
void Audio::coprocessor_enable(bool state) {
coprocessor = state;
+ dspaudio.clear();
dsp_rdoffset = cop_rdoffset = 0;
dsp_wroffset = cop_wroffset = 0;
dsp_length = cop_length = 0;
-
- r_sum_l = r_sum_r = 0;
}
void Audio::coprocessor_frequency(double input_frequency) {
- double output_frequency;
- output_frequency = system.apu_frequency() / 768.0;
- r_step = input_frequency / output_frequency;
- r_frac = 0;
+ dspaudio.setFrequency(input_frequency);
+ dspaudio.setResampler(nall::DSP::Resampler::Average);
+ dspaudio.setResamplerFrequency(system.apu_frequency() / 768.0);
}
void Audio::sample(int16 left, int16 right) {
@@ -31,28 +29,16 @@ void Audio::sample(int16 left, int16 right) {
}
void Audio::coprocessor_sample(int16 left, int16 right) {
- if(r_frac >= 1.0) {
- r_frac -= 1.0;
- r_sum_l += left;
- r_sum_r += right;
- return;
+ dspaudio.sample(left, right);
+ while(dspaudio.pending()) {
+ signed left, right;
+ dspaudio.read(left, right);
+
+ cop_buffer[cop_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
+ cop_wroffset = (cop_wroffset + 1) & buffer_mask;
+ cop_length = (cop_length + 1) & buffer_mask;
+ flush();
}
-
- r_sum_l += left * r_frac;
- r_sum_r += right * r_frac;
-
- uint16 output_left = sclamp<16>(int(r_sum_l / r_step));
- uint16 output_right = sclamp<16>(int(r_sum_r / r_step));
-
- double first = 1.0 - r_frac;
- r_sum_l = left * first;
- r_sum_r = right * first;
- r_frac = r_step - first;
-
- cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16);
- cop_wroffset = (cop_wroffset + 1) & buffer_mask;
- cop_length = (cop_length + 1) & buffer_mask;
- flush();
}
void Audio::init() {
diff --git a/bsnes/snes/audio/audio.hpp b/bsnes/snes/audio/audio.hpp
index 29d6dbae..f3bc8a4d 100755
--- a/bsnes/snes/audio/audio.hpp
+++ b/bsnes/snes/audio/audio.hpp
@@ -8,15 +8,13 @@ public:
private:
bool coprocessor;
- enum : unsigned { buffer_size = 32768, buffer_mask = buffer_size - 1 };
+ nall::DSP dspaudio;
+ enum : unsigned { buffer_size = 256, buffer_mask = buffer_size - 1 };
uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size];
unsigned dsp_rdoffset, cop_rdoffset;
unsigned dsp_wroffset, cop_wroffset;
unsigned dsp_length, cop_length;
- double r_step, r_frac;
- int r_sum_l, r_sum_r;
-
void flush();
};
diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp
index 03d96414..befdc5ab 100755
--- a/bsnes/snes/snes.hpp
+++ b/bsnes/snes/snes.hpp
@@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
- static const char Version[] = "081.04";
+ static const char Version[] = "082";
static const unsigned SerializerVersion = 21;
}
}
@@ -19,6 +19,7 @@ namespace SNES {
#include
#include
#include
+#include
#include
#include
#include
diff --git a/bsnes/ui-gameboy/base.hpp b/bsnes/ui-gameboy/base.hpp
index 0cf51c0b..f1afddfe 100755
--- a/bsnes/ui-gameboy/base.hpp
+++ b/bsnes/ui-gameboy/base.hpp
@@ -30,5 +30,5 @@ struct Application {
void main(int argc, char **argv);
};
-extern nall::dsp dspaudio;
+extern nall::DSP dspaudio;
extern Application application;
diff --git a/bsnes/ui-gameboy/main.cpp b/bsnes/ui-gameboy/main.cpp
index 5224d28e..3bd04b82 100755
--- a/bsnes/ui-gameboy/main.cpp
+++ b/bsnes/ui-gameboy/main.cpp
@@ -1,5 +1,5 @@
#include "base.hpp"
-nall::dsp dspaudio;
+nall::DSP dspaudio;
Application application;
#include "interface.cpp"
@@ -56,11 +56,12 @@ void Application::main(int argc, char **argv) {
audio.set(Audio::Frequency, 44100u);
audio.init();
- dspaudio.set_precision(16);
- dspaudio.set_volume(1.0);
- dspaudio.set_balance(0.0);
- dspaudio.set_frequency(4194304.0);
- dspaudio.set_resampler_frequency(44100.0);
+ dspaudio.setPrecision(16);
+ dspaudio.setVolume(1.0);
+ dspaudio.setBalance(0.0);
+ dspaudio.setFrequency(4194304.0);
+ dspaudio.setResampler(DSP::Resampler::Average);
+ dspaudio.setResamplerFrequency(44100.0);
#if defined(PLATFORM_WIN)
input.driver("RawInput");
diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp
index 12e13dc4..acf9dcbb 100755
--- a/bsnes/ui/base.hpp
+++ b/bsnes/ui/base.hpp
@@ -61,5 +61,5 @@ private:
void saveGeometry();
};
-extern nall::dsp dspaudio;
+extern nall::DSP dspaudio;
extern Application application;
diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp
index 6dfa414b..bb8310d8 100755
--- a/bsnes/ui/main.cpp
+++ b/bsnes/ui/main.cpp
@@ -1,7 +1,7 @@
#include "base.hpp"
#include "interface.cpp"
#include "config.cpp"
-nall::dsp dspaudio;
+nall::DSP dspaudio;
Application application;
void Application::main(int argc, char **argv) {
@@ -105,11 +105,12 @@ void Application::main(int argc, char **argv) {
audio.init();
}
- dspaudio.set_precision(16); //16-bit signed audio
- dspaudio.set_volume((double)config.audio.volume / 100.0);
- dspaudio.set_balance((double)((signed)config.audio.balance - 100) / 100.0);
- dspaudio.set_frequency(config.audio.inputFrequency);
- dspaudio.set_resampler_frequency(config.audio.outputFrequency);
+ 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());
diff --git a/bsnes/ui/settings/audio.cpp b/bsnes/ui/settings/audio.cpp
index 3a6c0fb8..20fc6b6a 100755
--- a/bsnes/ui/settings/audio.cpp
+++ b/bsnes/ui/settings/audio.cpp
@@ -37,19 +37,19 @@ void AudioSettings::create() {
frequencySlider.onChange = [this] {
config.audio.inputFrequency = frequencySlider.position() + 31000;
- dspaudio.set_frequency(config.audio.inputFrequency);
+ dspaudio.setFrequency(config.audio.inputFrequency);
frequencyValue.setText({ config.audio.inputFrequency, "hz" });
};
volumeSlider.onChange = [this] {
config.audio.volume = volumeSlider.position();
- dspaudio.set_volume((double)config.audio.volume / 100.0);
+ dspaudio.setVolume((double)config.audio.volume / 100.0);
volumeValue.setText({ config.audio.volume, "%" });
};
balanceSlider.onChange = [this] {
config.audio.balance = balanceSlider.position();
- dspaudio.set_balance((double)((signed)config.audio.balance - 100) / 100.0);
+ dspaudio.setBalance((double)((signed)config.audio.balance - 100) / 100.0);
balanceValue.setText({ (signed)config.audio.balance - 100 });
};
diff --git a/snesfilter/HQ2x/HQ2x.cpp b/snesfilter/HQ2x/HQ2x.cpp
index 08aec9b2..f4662b6d 100755
--- a/snesfilter/HQ2x/HQ2x.cpp
+++ b/snesfilter/HQ2x/HQ2x.cpp
@@ -51,7 +51,7 @@ static void initialize() {
double g = (G << 3) | (G >> 2);
double b = (B << 3) | (B >> 2);
- //bgr888->yuv888
+ //bgr888->yuv
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
@@ -59,6 +59,10 @@ static void initialize() {
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
}
+ //counter-clockwise rotation table; one revolution:
+ //123 369 12346789
+ //4.6 -> 2.8 =
+ //789 147 36928147
for(unsigned n = 0; n < 256; n++) {
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
@@ -83,8 +87,7 @@ static uint16_t pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
static uint16_t blend1(uint32_t A, uint32_t B) {
grow(A); grow(B);
- A = (A * 3 + B) >> 2;
- return pack(A);
+ return pack((A * 3 + B) >> 2);
}
static uint16_t blend2(uint32_t A, uint32_t B, uint32_t C) {
diff --git a/snespurify/cc-gtk.sh b/snespurify/cc-gtk.sh
index 30b4e71a..d5724b6f 100755
--- a/snespurify/cc-gtk.sh
+++ b/snespurify/cc-gtk.sh
@@ -1,4 +1,4 @@
g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp `pkg-config --cflags gtk+-2.0` -DPHOENIX_GTK
-g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_GTK
+g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp
g++-4.5 -s -o snespurify-gtk snespurify.o phoenix.o `pkg-config --libs gtk+-2.0` -lX11
rm *.o
diff --git a/snespurify/cc-qt.sh b/snespurify/cc-qt.sh
index 6a063a4f..fd25e7d1 100755
--- a/snespurify/cc-qt.sh
+++ b/snespurify/cc-qt.sh
@@ -1,5 +1,5 @@
moc -i -o phoenix/qt/qt.moc phoenix/qt/qt.moc.hpp
g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp `pkg-config --cflags QtCore QtGui` -DPHOENIX_QT
-g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_QT
+g++-4.5 -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp
g++-4.5 -s -o snespurify-qt snespurify.o phoenix.o `pkg-config --libs QtCore QtGui`
rm *.o
diff --git a/snespurify/cc-windows.bat b/snespurify/cc-windows.bat
index 72d3af6f..992e70e2 100755
--- a/snespurify/cc-windows.bat
+++ b/snespurify/cc-windows.bat
@@ -1,6 +1,6 @@
-windres phoenix/windows/phoenix.rc phoenix-resource.o
-g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp -DPHOENIX_WINDOWS
-g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp -DPHOENIX_WINDOWS
-g++ -mwindows -s -o snespurify snespurify.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
-@pause
-@del *.o
+windres phoenix/windows/phoenix.rc phoenix-resource.o
+g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c phoenix/phoenix.cpp -DPHOENIX_WINDOWS
+g++ -std=gnu++0x -I. -O3 -fomit-frame-pointer -c snespurify.cpp
+g++ -mwindows -s -o snespurify snespurify.o phoenix.o phoenix-resource.o -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
+@pause
+@del *.o
diff --git a/snespurify/nall/array.hpp b/snespurify/nall/array.hpp
index 9cfe7758..e1fb1fcb 100755
--- a/snespurify/nall/array.hpp
+++ b/snespurify/nall/array.hpp
@@ -54,6 +54,10 @@ namespace nall {
operator[](buffersize) = data;
}
+ void remove() {
+ if(size > 0) resize(size - 1); //remove last element only
+ }
+
template void insert(unsigned index, const U list) {
unsigned listsize = container_size(list);
resize(buffersize + listsize);
@@ -133,6 +137,12 @@ namespace nall {
if(index >= buffersize) throw "array[] out of bounds";
return pool[index];
}
+
+ //iteration
+ T* begin() { return &pool[0]; }
+ T* end() { return &pool[buffersize]; }
+ const T* begin() const { return &pool[0]; }
+ const T* end() const { return &pool[buffersize]; }
};
template struct has_size> { enum { value = true }; };
diff --git a/snespurify/nall/base64.hpp b/snespurify/nall/base64.hpp
index e41c87b7..ee59c1be 100755
--- a/snespurify/nall/base64.hpp
+++ b/snespurify/nall/base64.hpp
@@ -72,6 +72,7 @@ namespace nall {
private:
static char enc(uint8_t n) {
+ //base64 for URL encodings
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
return lookup_table[n & 63];
}
diff --git a/snespurify/nall/bmp.hpp b/snespurify/nall/bmp.hpp
new file mode 100755
index 00000000..33cdf4dc
--- /dev/null
+++ b/snespurify/nall/bmp.hpp
@@ -0,0 +1,101 @@
+#ifndef NALL_BMP_HPP
+#define NALL_BMP_HPP
+
+#include
+
+//BMP reader / writer
+//author: byuu
+//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
+
+namespace nall {
+
+struct bmp {
+ inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
+ inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
+};
+
+bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
+ file fp;
+ if(fp.open(filename, file::mode::read) == false) return false;
+ if(fp.size() < 0x36) return false;
+
+ if(fp.readm(2) != 0x424d) return false;
+ fp.seek(0x000a);
+ unsigned offset = fp.readl(4);
+ unsigned dibsize = fp.readl(4);
+ if(dibsize != 40) return false;
+ signed headerWidth = fp.readl(4);
+ if(headerWidth < 0) return false;
+ signed headerHeight = fp.readl(4);
+ fp.readl(2);
+ unsigned bitsPerPixel = fp.readl(2);
+ if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
+ unsigned compression = fp.readl(4);
+ if(compression != 0) return false;
+ fp.seek(offset);
+
+ bool noFlip = headerHeight < 0;
+ width = headerWidth, height = abs(headerHeight);
+ data = new uint32_t[width * height];
+
+ unsigned bytesPerPixel = bitsPerPixel / 8;
+ unsigned alignedWidth = width * bytesPerPixel;
+ unsigned paddingLength = 0;
+ while(alignedWidth % 4) alignedWidth++, paddingLength++;
+
+ for(unsigned y = 0; y < height; y++) {
+ uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
+ for(unsigned x = 0; x < width; x++, p++) {
+ *p = fp.readl(bytesPerPixel);
+ if(bytesPerPixel == 3) *p |= 255 << 24;
+ }
+ if(paddingLength) fp.readl(paddingLength);
+ }
+
+ fp.close();
+ return true;
+}
+
+bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
+ file fp;
+ if(fp.open(filename, file::mode::write) == false) return false;
+
+ unsigned bitsPerPixel = alpha ? 32 : 24;
+ unsigned bytesPerPixel = bitsPerPixel / 8;
+ unsigned alignedWidth = width * bytesPerPixel;
+ unsigned paddingLength = 0;
+ unsigned imageSize = alignedWidth * height;
+ unsigned fileSize = 0x36 + imageSize;
+ while(alignedWidth % 4) alignedWidth++, paddingLength++;
+
+ fp.writem(0x424d, 2); //signature
+ fp.writel(fileSize, 4); //file size
+ fp.writel(0, 2); //reserved
+ fp.writel(0, 2); //reserved
+ fp.writel(0x36, 4); //offset
+
+ fp.writel(40, 4); //DIB size
+ fp.writel(width, 4); //width
+ fp.writel(-height, 4); //height
+ fp.writel(1, 2); //color planes
+ fp.writel(bitsPerPixel, 2); //bits per pixel
+ fp.writel(0, 4); //compression method (BI_RGB)
+ fp.writel(imageSize, 4); //image data size
+ fp.writel(3780, 4); //horizontal resolution
+ fp.writel(3780, 4); //vertical resolution
+ fp.writel(0, 4); //palette size
+ fp.writel(0, 4); //important color count
+
+ for(unsigned y = 0; y < height; y++) {
+ const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
+ for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
+ if(paddingLength) fp.writel(0, paddingLength);
+ }
+
+ fp.close();
+ return true;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/bps/delta.hpp b/snespurify/nall/bps/delta.hpp
new file mode 100755
index 00000000..a3af047c
--- /dev/null
+++ b/snespurify/nall/bps/delta.hpp
@@ -0,0 +1,214 @@
+#ifndef NALL_BPS_DELTA_HPP
+#define NALL_BPS_DELTA_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct bpsdelta {
+ inline void source(const uint8_t *data, unsigned size);
+ inline void target(const uint8_t *data, unsigned size);
+
+ inline bool source(const string &filename);
+ inline bool target(const string &filename);
+ inline bool create(const string &filename, const string &metadata = "");
+
+protected:
+ enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
+ enum : unsigned { Granularity = 1 };
+
+ struct Node {
+ unsigned offset;
+ Node *next;
+ inline Node() : offset(0), next(0) {}
+ inline ~Node() { if(next) delete next; }
+ };
+
+ filemap sourceFile;
+ const uint8_t *sourceData;
+ unsigned sourceSize;
+
+ filemap targetFile;
+ const uint8_t *targetData;
+ unsigned targetSize;
+};
+
+void bpsdelta::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+void bpsdelta::target(const uint8_t *data, unsigned size) {
+ targetData = data;
+ targetSize = size;
+}
+
+bool bpsdelta::source(const string &filename) {
+ if(sourceFile.open(filename, filemap::mode::read) == false) return false;
+ source(sourceFile.data(), sourceFile.size());
+ return true;
+}
+
+bool bpsdelta::target(const string &filename) {
+ if(targetFile.open(filename, filemap::mode::read) == false) return false;
+ target(targetFile.data(), targetFile.size());
+ return true;
+}
+
+bool bpsdelta::create(const string &filename, const string &metadata) {
+ file modifyFile;
+ if(modifyFile.open(filename, file::mode::write) == false) return false;
+
+ uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
+ unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
+
+ auto write = [&](uint8_t data) {
+ modifyFile.write(data);
+ modifyChecksum = crc32_adjust(modifyChecksum, data);
+ };
+
+ auto encode = [&](uint64_t data) {
+ while(true) {
+ uint64_t x = data & 0x7f;
+ data >>= 7;
+ if(data == 0) {
+ write(0x80 | x);
+ break;
+ }
+ write(x);
+ data--;
+ }
+ };
+
+ write('B');
+ write('P');
+ write('S');
+ write('1');
+
+ encode(sourceSize);
+ encode(targetSize);
+
+ unsigned markupSize = metadata.length();
+ encode(markupSize);
+ for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
+
+ Node *sourceTree[65536], *targetTree[65536];
+ for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
+
+ //source tree creation
+ for(unsigned offset = 0; offset < sourceSize; offset++) {
+ uint16_t symbol = sourceData[offset + 0];
+ sourceChecksum = crc32_adjust(sourceChecksum, symbol);
+ if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
+ Node *node = new Node;
+ node->offset = offset;
+ node->next = sourceTree[symbol];
+ sourceTree[symbol] = node;
+ }
+
+ unsigned targetReadLength = 0;
+
+ auto targetReadFlush = [&]() {
+ if(targetReadLength) {
+ encode(TargetRead | ((targetReadLength - 1) << 2));
+ unsigned offset = outputOffset - targetReadLength;
+ while(targetReadLength) write(targetData[offset++]), targetReadLength--;
+ }
+ };
+
+ while(outputOffset < targetSize) {
+ unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
+
+ uint16_t symbol = targetData[outputOffset + 0];
+ if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
+
+ { //source read
+ unsigned length = 0, offset = outputOffset;
+ while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
+ length++;
+ offset++;
+ }
+ if(length > maxLength) maxLength = length, mode = SourceRead;
+ }
+
+ { //source copy
+ Node *node = sourceTree[symbol];
+ while(node) {
+ unsigned length = 0, x = node->offset, y = outputOffset;
+ while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
+ if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
+ node = node->next;
+ }
+ }
+
+ { //target copy
+ Node *node = targetTree[symbol];
+ while(node) {
+ unsigned length = 0, x = node->offset, y = outputOffset;
+ while(y < targetSize && targetData[x++] == targetData[y++]) length++;
+ if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
+ node = node->next;
+ }
+
+ //target tree append
+ node = new Node;
+ node->offset = outputOffset;
+ node->next = targetTree[symbol];
+ targetTree[symbol] = node;
+ }
+
+ { //target read
+ if(maxLength < 4) {
+ maxLength = min((unsigned)Granularity, targetSize - outputOffset);
+ mode = TargetRead;
+ }
+ }
+
+ if(mode != TargetRead) targetReadFlush();
+
+ switch(mode) {
+ case SourceRead:
+ encode(SourceRead | ((maxLength - 1) << 2));
+ break;
+ case TargetRead:
+ //delay write to group sequential TargetRead commands into one
+ targetReadLength += maxLength;
+ break;
+ case SourceCopy:
+ case TargetCopy:
+ encode(mode | ((maxLength - 1) << 2));
+ signed relativeOffset;
+ if(mode == SourceCopy) {
+ relativeOffset = maxOffset - sourceRelativeOffset;
+ sourceRelativeOffset = maxOffset + maxLength;
+ } else {
+ relativeOffset = maxOffset - targetRelativeOffset;
+ targetRelativeOffset = maxOffset + maxLength;
+ }
+ encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
+ break;
+ }
+
+ outputOffset += maxLength;
+ }
+
+ targetReadFlush();
+
+ sourceChecksum = ~sourceChecksum;
+ for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
+ uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
+ for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
+ uint32_t outputChecksum = ~modifyChecksum;
+ for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
+
+ modifyFile.close();
+ return true;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/bps/linear.hpp b/snespurify/nall/bps/linear.hpp
new file mode 100755
index 00000000..df840283
--- /dev/null
+++ b/snespurify/nall/bps/linear.hpp
@@ -0,0 +1,152 @@
+#ifndef NALL_BPS_LINEAR_HPP
+#define NALL_BPS_LINEAR_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct bpslinear {
+ inline void source(const uint8_t *data, unsigned size);
+ inline void target(const uint8_t *data, unsigned size);
+
+ inline bool source(const string &filename);
+ inline bool target(const string &filename);
+ inline bool create(const string &filename, const string &metadata = "");
+
+protected:
+ enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
+ enum : unsigned { Granularity = 1 };
+
+ filemap sourceFile;
+ const uint8_t *sourceData;
+ unsigned sourceSize;
+
+ filemap targetFile;
+ const uint8_t *targetData;
+ unsigned targetSize;
+};
+
+void bpslinear::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+void bpslinear::target(const uint8_t *data, unsigned size) {
+ targetData = data;
+ targetSize = size;
+}
+
+bool bpslinear::source(const string &filename) {
+ if(sourceFile.open(filename, filemap::mode::read) == false) return false;
+ source(sourceFile.data(), sourceFile.size());
+ return true;
+}
+
+bool bpslinear::target(const string &filename) {
+ if(targetFile.open(filename, filemap::mode::read) == false) return false;
+ target(targetFile.data(), targetFile.size());
+ return true;
+}
+
+bool bpslinear::create(const string &filename, const string &metadata) {
+ file modifyFile;
+ if(modifyFile.open(filename, file::mode::write) == false) return false;
+
+ uint32_t modifyChecksum = ~0;
+ unsigned targetRelativeOffset = 0, outputOffset = 0;
+
+ auto write = [&](uint8_t data) {
+ modifyFile.write(data);
+ modifyChecksum = crc32_adjust(modifyChecksum, data);
+ };
+
+ auto encode = [&](uint64_t data) {
+ while(true) {
+ uint64_t x = data & 0x7f;
+ data >>= 7;
+ if(data == 0) {
+ write(0x80 | x);
+ break;
+ }
+ write(x);
+ data--;
+ }
+ };
+
+ unsigned targetReadLength = 0;
+
+ auto targetReadFlush = [&]() {
+ if(targetReadLength) {
+ encode(TargetRead | ((targetReadLength - 1) << 2));
+ unsigned offset = outputOffset - targetReadLength;
+ while(targetReadLength) write(targetData[offset++]), targetReadLength--;
+ }
+ };
+
+ write('B');
+ write('P');
+ write('S');
+ write('1');
+
+ encode(sourceSize);
+ encode(targetSize);
+
+ unsigned markupSize = metadata.length();
+ encode(markupSize);
+ for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
+
+ while(outputOffset < targetSize) {
+ unsigned sourceLength = 0;
+ for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
+ if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
+ sourceLength++;
+ }
+
+ unsigned rleLength = 0;
+ for(unsigned n = 1; outputOffset + n < targetSize; n++) {
+ if(targetData[outputOffset] != targetData[outputOffset + n]) break;
+ rleLength++;
+ }
+
+ if(rleLength >= 4) {
+ //write byte to repeat
+ targetReadLength++;
+ outputOffset++;
+ targetReadFlush();
+
+ //copy starting from repetition byte
+ encode(TargetCopy | ((rleLength - 1) << 2));
+ unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
+ encode(relativeOffset << 1);
+ outputOffset += rleLength;
+ targetRelativeOffset = outputOffset - 1;
+ } else if(sourceLength >= 4) {
+ targetReadFlush();
+ encode(SourceRead | ((sourceLength - 1) << 2));
+ outputOffset += sourceLength;
+ } else {
+ targetReadLength += Granularity;
+ outputOffset += Granularity;
+ }
+ }
+
+ targetReadFlush();
+
+ uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
+ for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
+ uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
+ for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
+ uint32_t outputChecksum = ~modifyChecksum;
+ for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
+
+ modifyFile.close();
+ return true;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/bps/metadata.hpp b/snespurify/nall/bps/metadata.hpp
new file mode 100755
index 00000000..46759e6f
--- /dev/null
+++ b/snespurify/nall/bps/metadata.hpp
@@ -0,0 +1,121 @@
+#ifndef NALL_BPS_METADATA_HPP
+#define NALL_BPS_METADATA_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct bpsmetadata {
+ inline bool load(const string &filename);
+ inline bool save(const string &filename, const string &metadata);
+ inline string metadata() const;
+
+protected:
+ file sourceFile;
+ string metadataString;
+};
+
+bool bpsmetadata::load(const string &filename) {
+ if(sourceFile.open(filename, file::mode::read) == false) return false;
+
+ auto read = [&]() -> uint8_t {
+ return sourceFile.read();
+ };
+
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = read();
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ if(read() != 'B') return false;
+ if(read() != 'P') return false;
+ if(read() != 'S') return false;
+ if(read() != '1') return false;
+ decode();
+ decode();
+ unsigned metadataSize = decode();
+ char data[metadataSize + 1];
+ for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
+ data[metadataSize] = 0;
+ metadataString = (const char*)data;
+
+ return true;
+}
+
+bool bpsmetadata::save(const string &filename, const string &metadata) {
+ file targetFile;
+ if(targetFile.open(filename, file::mode::write) == false) return false;
+ if(sourceFile.open() == false) return false;
+ sourceFile.seek(0);
+
+ auto read = [&]() -> uint8_t {
+ return sourceFile.read();
+ };
+
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = read();
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ uint32_t checksum = ~0;
+
+ auto write = [&](uint8_t data) {
+ targetFile.write(data);
+ checksum = crc32_adjust(checksum, data);
+ };
+
+ auto encode = [&](uint64_t data) {
+ while(true) {
+ uint64_t x = data & 0x7f;
+ data >>= 7;
+ if(data == 0) {
+ write(0x80 | x);
+ break;
+ }
+ write(x);
+ data--;
+ }
+ };
+
+ for(unsigned n = 0; n < 4; n++) write(read());
+ encode(decode());
+ encode(decode());
+ unsigned sourceLength = decode();
+ unsigned targetLength = metadata.length();
+ encode(targetLength);
+ sourceFile.seek(sourceLength, file::index::relative);
+ for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
+ unsigned length = sourceFile.size() - sourceFile.offset() - 4;
+ for(unsigned n = 0; n < length; n++) write(read());
+ uint32_t outputChecksum = ~checksum;
+ for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
+
+ targetFile.close();
+ return true;
+}
+
+string bpsmetadata::metadata() const {
+ return metadataString;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/bps/patch.hpp b/snespurify/nall/bps/patch.hpp
new file mode 100755
index 00000000..85c4dcae
--- /dev/null
+++ b/snespurify/nall/bps/patch.hpp
@@ -0,0 +1,219 @@
+#ifndef NALL_BPS_PATCH_HPP
+#define NALL_BPS_PATCH_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct bpspatch {
+ inline bool modify(const uint8_t *data, unsigned size);
+ inline void source(const uint8_t *data, unsigned size);
+ inline void target(uint8_t *data, unsigned size);
+
+ inline bool modify(const string &filename);
+ inline bool source(const string &filename);
+ inline bool target(const string &filename);
+
+ inline string metadata() const;
+ inline unsigned size() const;
+
+ enum result : unsigned {
+ unknown,
+ success,
+ patch_too_small,
+ patch_invalid_header,
+ source_too_small,
+ target_too_small,
+ source_checksum_invalid,
+ target_checksum_invalid,
+ patch_checksum_invalid,
+ };
+
+ inline result apply();
+
+protected:
+ enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
+
+ filemap modifyFile;
+ const uint8_t *modifyData;
+ unsigned modifySize;
+
+ filemap sourceFile;
+ const uint8_t *sourceData;
+ unsigned sourceSize;
+
+ filemap targetFile;
+ uint8_t *targetData;
+ unsigned targetSize;
+
+ unsigned modifySourceSize;
+ unsigned modifyTargetSize;
+ unsigned modifyMarkupSize;
+ string metadataString;
+};
+
+bool bpspatch::modify(const uint8_t *data, unsigned size) {
+ if(size < 19) return false;
+ modifyData = data;
+ modifySize = size;
+
+ unsigned offset = 4;
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = modifyData[offset++];
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ modifySourceSize = decode();
+ modifyTargetSize = decode();
+ modifyMarkupSize = decode();
+
+ char buffer[modifyMarkupSize + 1];
+ for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
+ buffer[modifyMarkupSize] = 0;
+ metadataString = (const char*)buffer;
+
+ return true;
+}
+
+void bpspatch::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+void bpspatch::target(uint8_t *data, unsigned size) {
+ targetData = data;
+ targetSize = size;
+}
+
+bool bpspatch::modify(const string &filename) {
+ if(modifyFile.open(filename, filemap::mode::read) == false) return false;
+ return modify(modifyFile.data(), modifyFile.size());
+}
+
+bool bpspatch::source(const string &filename) {
+ if(sourceFile.open(filename, filemap::mode::read) == false) return false;
+ source(sourceFile.data(), sourceFile.size());
+ return true;
+}
+
+bool bpspatch::target(const string &filename) {
+ file fp;
+ if(fp.open(filename, file::mode::write) == false) return false;
+ fp.truncate(modifyTargetSize);
+ fp.close();
+
+ if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
+ target(targetFile.data(), targetFile.size());
+ return true;
+}
+
+string bpspatch::metadata() const {
+ return metadataString;
+}
+
+unsigned bpspatch::size() const {
+ return modifyTargetSize;
+}
+
+bpspatch::result bpspatch::apply() {
+ if(modifySize < 19) return result::patch_too_small;
+
+ uint32_t modifyChecksum = ~0, targetChecksum = ~0;
+ unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
+
+ auto read = [&]() -> uint8_t {
+ uint8_t data = modifyData[modifyOffset++];
+ modifyChecksum = crc32_adjust(modifyChecksum, data);
+ return data;
+ };
+
+ auto decode = [&]() -> uint64_t {
+ uint64_t data = 0, shift = 1;
+ while(true) {
+ uint8_t x = read();
+ data += (x & 0x7f) * shift;
+ if(x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+ };
+
+ auto write = [&](uint8_t data) {
+ targetData[outputOffset++] = data;
+ targetChecksum = crc32_adjust(targetChecksum, data);
+ };
+
+ if(read() != 'B') return result::patch_invalid_header;
+ if(read() != 'P') return result::patch_invalid_header;
+ if(read() != 'S') return result::patch_invalid_header;
+ if(read() != '1') return result::patch_invalid_header;
+
+ modifySourceSize = decode();
+ modifyTargetSize = decode();
+ modifyMarkupSize = decode();
+ for(unsigned n = 0; n < modifyMarkupSize; n++) read();
+
+ if(modifySourceSize > sourceSize) return result::source_too_small;
+ if(modifyTargetSize > targetSize) return result::target_too_small;
+
+ while(modifyOffset < modifySize - 12) {
+ unsigned length = decode();
+ unsigned mode = length & 3;
+ length = (length >> 2) + 1;
+
+ switch(mode) {
+ case SourceRead:
+ while(length--) write(sourceData[outputOffset]);
+ break;
+ case TargetRead:
+ while(length--) write(read());
+ break;
+ case SourceCopy:
+ case TargetCopy:
+ signed offset = decode();
+ bool negative = offset & 1;
+ offset >>= 1;
+ if(negative) offset = -offset;
+
+ if(mode == SourceCopy) {
+ sourceRelativeOffset += offset;
+ while(length--) write(sourceData[sourceRelativeOffset++]);
+ } else {
+ targetRelativeOffset += offset;
+ while(length--) write(targetData[targetRelativeOffset++]);
+ }
+ break;
+ }
+ }
+
+ uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
+ for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
+ for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
+ uint32_t checksum = ~modifyChecksum;
+ for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
+
+ uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
+ targetChecksum = ~targetChecksum;
+
+ if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
+ if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
+ if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
+
+ return result::success;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/compositor.hpp b/snespurify/nall/compositor.hpp
new file mode 100755
index 00000000..6d5c46c9
--- /dev/null
+++ b/snespurify/nall/compositor.hpp
@@ -0,0 +1,79 @@
+#ifndef NALL_COMPOSITOR_HPP
+#define NALL_COMPOSITOR_HPP
+
+#include
+
+namespace nall {
+
+struct compositor {
+ inline static bool enabled();
+ inline static bool enable(bool status);
+};
+
+#if defined(PLATFORM_X)
+
+bool compositor::enabled() {
+ FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
+ if(fp == 0) return false;
+
+ char buffer[512];
+ if(fgets(buffer, sizeof buffer, fp) == 0) return false;
+
+ if(!memcmp(buffer, "true", 4)) return true;
+ return false;
+}
+
+bool compositor::enable(bool status) {
+ FILE *fp;
+ if(status) {
+ fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
+ } else {
+ fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
+ }
+ if(fp == 0) return false;
+ pclose(fp);
+ return true;
+}
+
+#elif defined(PLATFORM_WIN)
+
+bool compositor::enabled() {
+ HMODULE module = GetModuleHandleW(L"dwmapi");
+ if(module == 0) module = LoadLibraryW(L"dwmapi");
+ if(module == 0) return false;
+
+ auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
+ if(pDwmIsCompositionEnabled == 0) return false;
+
+ BOOL result;
+ if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
+ return result;
+}
+
+bool compositor::enable(bool status) {
+ HMODULE module = GetModuleHandleW(L"dwmapi");
+ if(module == 0) module = LoadLibraryW(L"dwmapi");
+ if(module == 0) return false;
+
+ auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
+ if(pDwmEnableComposition == 0) return false;
+
+ if(pDwmEnableComposition(status) != S_OK) return false;
+ return true;
+}
+
+#else
+
+bool compositor::enabled() {
+ return false;
+}
+
+bool compositor::enable(bool) {
+ return false;
+}
+
+#endif
+
+}
+
+#endif
diff --git a/snespurify/nall/config.hpp b/snespurify/nall/config.hpp
index b8381b16..99aaee08 100755
--- a/snespurify/nall/config.hpp
+++ b/snespurify/nall/config.hpp
@@ -34,11 +34,11 @@ namespace nall {
string get() const {
switch(type) {
- case boolean_t: return string() << *(bool*)data;
- case signed_t: return string() << *(signed*)data;
- case unsigned_t: return string() << *(unsigned*)data;
- case double_t: return string() << *(double*)data;
- case string_t: return string() << "\"" << *(string*)data << "\"";
+ case boolean_t: return { *(bool*)data };
+ case signed_t: return { *(signed*)data };
+ case unsigned_t: return { *(unsigned*)data };
+ case double_t: return { *(double*)data };
+ case string_t: return { "\"", *(string*)data, "\"" };
}
return "???";
}
@@ -105,9 +105,9 @@ namespace nall {
if(fp.open(filename, file::mode::write)) {
for(unsigned i = 0; i < list.size(); i++) {
string output;
- output << list[i].name << " = " << list[i].get();
- if(list[i].desc != "") output << " # " << list[i].desc;
- output << "\r\n";
+ output.append(list[i].name, " = ", list[i].get());
+ if(list[i].desc != "") output.append(" # ", list[i].desc);
+ output.append("\r\n");
fp.print(output);
}
diff --git a/snespurify/nall/dictionary.hpp b/snespurify/nall/dictionary.hpp
deleted file mode 100755
index dcb04151..00000000
--- a/snespurify/nall/dictionary.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef NALL_DICTIONARY_HPP
-#define NALL_DICTIONARY_HPP
-
-#include
-#include
-#include
-
-namespace nall {
- class dictionary {
- public:
- string operator[](const char *input) {
- for(unsigned i = 0; i < index_input.size(); i++) {
- if(index_input[i] == input) return index_output[i];
- }
-
- //no match, use input; remove input identifier, if one exists
- if(strbegin(input, "{{")) {
- if(auto pos = strpos(input, "}}")) {
- string temp = substr(input, pos() + 2);
- return temp;
- }
- }
-
- return input;
- }
-
- bool import(const char *filename) {
- string data;
- if(data.readfile(filename) == false) return false;
- data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
- data.replace("\r", "");
-
- lstring line;
- line.split("\n", data);
- for(unsigned i = 0; i < line.size(); i++) {
- lstring part;
- //format: "Input" = "Output"
- part.qsplit("=", line[i]);
- if(part.size() != 2) continue;
-
- //remove whitespace
- part[0].trim();
- part[1].trim();
-
- //remove quotes
- part[0].trim<1>("\"");
- part[1].trim<1>("\"");
-
- unsigned n = index_input.size();
- index_input[n] = part[0];
- index_output[n] = part[1];
- }
-
- return true;
- }
-
- void reset() {
- index_input.reset();
- index_output.reset();
- }
-
- ~dictionary() {
- reset();
- }
-
- dictionary& operator=(const dictionary&) = delete;
- dictionary(const dictionary&) = delete;
-
- protected:
- lstring index_input;
- lstring index_output;
- };
-}
-
-#endif
diff --git a/snespurify/nall/directory.hpp b/snespurify/nall/directory.hpp
index c4f94c9a..7fbc15f4 100755
--- a/snespurify/nall/directory.hpp
+++ b/snespurify/nall/directory.hpp
@@ -6,7 +6,7 @@
#include
#if defined(_WIN32)
- #include
+ #include
#else
#include
#include
@@ -42,20 +42,21 @@ struct directory {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
- if(wildcard(name, pattern)) list.append(string(name, "/"));
+ if(wildcard(name, pattern)) list.append(name);
}
}
while(FindNextFile(handle, &data) != false) {
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
string name = (const char*)utf8_t(data.cFileName);
- if(wildcard(name, pattern)) list.append(string(name, "/"));
+ if(wildcard(name, pattern)) list.append(name);
}
}
}
FindClose(handle);
}
if(list.size() > 0) sort(&list[0], list.size());
+ foreach(name, list) name.append("/"); //must append after sorting
return list;
}
@@ -109,14 +110,14 @@ struct directory {
if(!strcmp(ep->d_name, ".")) continue;
if(!strcmp(ep->d_name, "..")) continue;
if(ep->d_type & DT_DIR) {
- if(wildcard(ep->d_name, pattern)) list.append(string(ep->d_name, "/"));
+ if(wildcard(ep->d_name, pattern)) list.append(ep->d_name);
}
}
closedir(dp);
}
if(list.size() > 0) sort(&list[0], list.size());
+ foreach(name, list) name.append("/"); //must append after sorting
return list;
-
}
inline lstring directory::files(const string &pathname, const string &pattern) {
diff --git a/snespurify/nall/dl.hpp b/snespurify/nall/dl.hpp
index ebfa5585..c697958c 100755
--- a/snespurify/nall/dl.hpp
+++ b/snespurify/nall/dl.hpp
@@ -12,7 +12,7 @@
#include
#elif defined(PLATFORM_WIN)
#include
- #include
+ #include
#endif
namespace nall {
diff --git a/snespurify/nall/dsp.hpp b/snespurify/nall/dsp.hpp
new file mode 100755
index 00000000..009c8b6c
--- /dev/null
+++ b/snespurify/nall/dsp.hpp
@@ -0,0 +1,8 @@
+#ifndef NALL_DSP_HPP
+#define NALL_DSP_HPP
+
+#define NALL_DSP_INTERNAL_HPP
+#include
+#undef NALL_DSP_INTERNAL_HPP
+
+#endif
diff --git a/snespurify/nall/dsp/buffer.hpp b/snespurify/nall/dsp/buffer.hpp
new file mode 100755
index 00000000..c1b85b8d
--- /dev/null
+++ b/snespurify/nall/dsp/buffer.hpp
@@ -0,0 +1,36 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+struct Buffer {
+ double *sample[2];
+ uint16_t rdoffset;
+ uint16_t wroffset;
+
+ inline double& read(bool channel, signed offset = 0) {
+ return sample[channel][(uint16_t)(rdoffset + offset)];
+ }
+
+ inline double& write(bool channel, signed offset = 0) {
+ return sample[channel][(uint16_t)(wroffset + offset)];
+ }
+
+ inline void clear() {
+ for(unsigned n = 0; n < 65536; n++) {
+ sample[0][n] = 0;
+ sample[1][n] = 0;
+ }
+ rdoffset = 0;
+ wroffset = 0;
+ }
+
+ Buffer() {
+ sample[0] = new double[65536];
+ sample[1] = new double[65536];
+ }
+
+ ~Buffer() {
+ delete[] sample[0];
+ delete[] sample[1];
+ }
+};
+
+#endif
diff --git a/snespurify/nall/dsp/core.hpp b/snespurify/nall/dsp/core.hpp
new file mode 100755
index 00000000..bec46d93
--- /dev/null
+++ b/snespurify/nall/dsp/core.hpp
@@ -0,0 +1,154 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+#include
+#include
+
+namespace nall {
+
+struct DSP {
+ enum class Resampler : unsigned {
+ Point,
+ Linear,
+ Cosine,
+ Cubic,
+ Hermite,
+ Average,
+ };
+
+ inline void setPrecision(unsigned precision);
+ inline void setFrequency(double frequency); //inputFrequency
+ inline void setVolume(double volume);
+ inline void setBalance(double balance);
+
+ inline void setResampler(Resampler resampler);
+ inline void setResamplerFrequency(double frequency); //outputFrequency
+
+ inline void sample(signed lchannel, signed rchannel);
+ inline bool pending();
+ inline void read(signed &lchannel, signed &rchannel);
+
+ inline void clear();
+ inline DSP();
+ inline ~DSP();
+
+protected:
+ struct Settings {
+ unsigned precision;
+ double frequency;
+ double volume;
+ double balance;
+ //internal
+ double intensity;
+ } settings;
+
+ struct ResamplerSettings {
+ Resampler engine;
+ double frequency;
+ //internal
+ double fraction;
+ double step;
+ } resampler;
+
+ inline void resamplerRun();
+ inline void resamplerWrite(double lchannel, double rchannel);
+
+ inline void resamplePoint();
+ inline void resampleLinear();
+ inline void resampleCosine();
+ inline void resampleCubic();
+ inline void resampleHermite();
+ inline void resampleAverage();
+
+ #include "buffer.hpp"
+ Buffer buffer;
+ Buffer output;
+
+ inline void adjustVolume();
+ inline void adjustBalance();
+ inline signed clamp(const unsigned bits, const signed x);
+};
+
+#include "settings.hpp"
+
+void DSP::sample(signed lchannel, signed rchannel) {
+ buffer.write(0) = (double)lchannel / settings.intensity;
+ buffer.write(1) = (double)rchannel / settings.intensity;
+ buffer.wroffset++;
+ resamplerRun();
+}
+
+bool DSP::pending() {
+ return output.rdoffset != output.wroffset;
+}
+
+void DSP::read(signed &lchannel, signed &rchannel) {
+ adjustVolume();
+ adjustBalance();
+
+ lchannel = clamp(settings.precision, output.read(0) * settings.intensity);
+ rchannel = clamp(settings.precision, output.read(1) * settings.intensity);
+ 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 lchannel, double rchannel) {
+ output.write(0) = lchannel;
+ output.write(1) = rchannel;
+ 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() {
+ output.read(0) *= settings.volume;
+ output.read(1) *= settings.volume;
+}
+
+void DSP::adjustBalance() {
+ if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
+ if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
+}
+
+signed DSP::clamp(const unsigned bits, const signed x) {
+ const signed b = 1U << (bits - 1);
+ const signed m = (1U << (bits - 1)) - 1;
+ return (x > m) ? m : (x < -b) ? -b : x;
+}
+
+void DSP::clear() {
+ resampler.fraction = 0.0;
+ buffer.clear();
+ output.clear();
+}
+
+DSP::DSP() {
+ setPrecision(16);
+ setFrequency(44100.0);
+ setVolume(1.0);
+ setBalance(0.0);
+ setResampler(Resampler::Hermite);
+ setResamplerFrequency(44100.0);
+ clear();
+}
+
+DSP::~DSP() {
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/average.hpp b/snespurify/nall/dsp/resample/average.hpp
new file mode 100755
index 00000000..ca97050e
--- /dev/null
+++ b/snespurify/nall/dsp/resample/average.hpp
@@ -0,0 +1,28 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleAverage() {
+ //can only average if input frequency >= output frequency
+ if(resampler.step < 1.0) return resampleHermite();
+
+ resampler.fraction += 1.0;
+
+ double scalar = 1.0;
+ if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
+
+ output.write(0) += buffer.read(0) * scalar;
+ output.write(1) += buffer.read(1) * scalar;
+
+ if(resampler.fraction >= resampler.step) {
+ output.write(0) /= resampler.step;
+ output.write(1) /= resampler.step;
+ output.wroffset++;
+
+ resampler.fraction -= resampler.step;
+ output.write(0) = buffer.read(0) * resampler.fraction;
+ output.write(1) = buffer.read(1) * resampler.fraction;
+ }
+
+ buffer.rdoffset++;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/cosine.hpp b/snespurify/nall/dsp/resample/cosine.hpp
new file mode 100755
index 00000000..56fcf8fe
--- /dev/null
+++ b/snespurify/nall/dsp/resample/cosine.hpp
@@ -0,0 +1,25 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleCosine() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; n++) {
+ double a = buffer.read(n, -1);
+ double b = buffer.read(n, -0);
+
+ double mu = resampler.fraction;
+ mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
+
+ channel[n] = a * (1.0 - mu) + b * mu;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/cubic.hpp b/snespurify/nall/dsp/resample/cubic.hpp
new file mode 100755
index 00000000..f975f6aa
--- /dev/null
+++ b/snespurify/nall/dsp/resample/cubic.hpp
@@ -0,0 +1,31 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleCubic() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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);
+
+ double mu = resampler.fraction;
+
+ double A = d - c - a + b;
+ double B = a - b - A;
+ double C = c - a;
+ double D = b;
+
+ channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/hermite.hpp b/snespurify/nall/dsp/resample/hermite.hpp
new file mode 100755
index 00000000..a042a55b
--- /dev/null
+++ b/snespurify/nall/dsp/resample/hermite.hpp
@@ -0,0 +1,43 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleHermite() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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);
+
+ const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
+ const double bias = 0.0; //-1 = left, 0 = even, +1 = right
+
+ double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
+
+ mu1 = resampler.fraction;
+ mu2 = mu1 * mu1;
+ mu3 = mu2 * mu1;
+
+ m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
+ m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
+ m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
+ m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
+
+ a0 = +2 * mu3 - 3 * mu2 + 1;
+ a1 = mu3 - 2 * mu2 + mu1;
+ a2 = mu3 - mu2;
+ a3 = -2 * mu3 + 3 * mu2;
+
+ channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/linear.hpp b/snespurify/nall/dsp/resample/linear.hpp
new file mode 100755
index 00000000..f4e48f57
--- /dev/null
+++ b/snespurify/nall/dsp/resample/linear.hpp
@@ -0,0 +1,24 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resampleLinear() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; n++) {
+ double a = buffer.read(n, -1);
+ double b = buffer.read(n, -0);
+
+ double mu = resampler.fraction;
+
+ channel[n] = a * (1.0 - mu) + b * mu;
+ }
+
+ resamplerWrite(channel[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/resample/point.hpp b/snespurify/nall/dsp/resample/point.hpp
new file mode 100755
index 00000000..51d35935
--- /dev/null
+++ b/snespurify/nall/dsp/resample/point.hpp
@@ -0,0 +1,24 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::resamplePoint() {
+ while(resampler.fraction <= 1.0) {
+ double channel[2];
+
+ for(unsigned n = 0; n < 2; 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[0], channel[1]);
+ resampler.fraction += resampler.step;
+ }
+
+ buffer.rdoffset++;
+ resampler.fraction -= 1.0;
+}
+
+#endif
diff --git a/snespurify/nall/dsp/settings.hpp b/snespurify/nall/dsp/settings.hpp
new file mode 100755
index 00000000..c7313d3d
--- /dev/null
+++ b/snespurify/nall/dsp/settings.hpp
@@ -0,0 +1,32 @@
+#ifdef NALL_DSP_INTERNAL_HPP
+
+void DSP::setPrecision(unsigned precision) {
+ settings.precision = precision;
+ settings.intensity = 1 << (settings.precision - 1);
+}
+
+void DSP::setFrequency(double frequency) {
+ settings.frequency = frequency;
+ resampler.fraction = 0;
+ resampler.step = settings.frequency / resampler.frequency;
+}
+
+void DSP::setVolume(double volume) {
+ settings.volume = volume;
+}
+
+void DSP::setBalance(double balance) {
+ settings.balance = balance;
+}
+
+void DSP::setResampler(Resampler engine) {
+ resampler.engine = engine;
+}
+
+void DSP::setResamplerFrequency(double frequency) {
+ resampler.frequency = frequency;
+ resampler.fraction = 0;
+ resampler.step = settings.frequency / resampler.frequency;
+}
+
+#endif
diff --git a/snespurify/nall/file.hpp b/snespurify/nall/file.hpp
index 103c7d4a..40453448 100755
--- a/snespurify/nall/file.hpp
+++ b/snespurify/nall/file.hpp
@@ -1,22 +1,14 @@
#ifndef NALL_FILE_HPP
#define NALL_FILE_HPP
-#include
-#include
-
-#if !defined(_WIN32)
- #include
-#else
- #include
-#endif
-
+#include
#include
#include
-#include
#include
+#include
namespace nall {
- inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
+ inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
#if !defined(_WIN32)
return fopen(utf8_filename, mode);
#else
@@ -28,6 +20,29 @@ namespace nall {
public:
enum class mode : unsigned { read, write, readwrite, writeread };
enum class index : unsigned { absolute, relative };
+ enum class time : unsigned { create, modify, access };
+
+ static bool read(const string &filename, uint8_t *&data, unsigned &size) {
+ file fp;
+ if(fp.open(filename, mode::read) == false) return false;
+ size = fp.size();
+ data = new uint8_t[size];
+ fp.read(data, size);
+ fp.close();
+ return true;
+ }
+
+ static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
+ return file::read(filename, (uint8_t*&)data, size);
+ }
+
+ static bool write(const string &filename, const uint8_t *data, unsigned size) {
+ file fp;
+ if(fp.open(filename, mode::write) == false) return false;
+ fp.write(data, size);
+ fp.close();
+ return true;
+ }
uint8_t read() {
if(!fp) return 0xff; //file not open
@@ -142,52 +157,60 @@ namespace nall {
return file_offset >= file_size;
}
- static bool exists(const char *fn) {
+ static bool exists(const string &filename) {
#if !defined(_WIN32)
- FILE *fp = fopen(fn, "rb");
+ struct stat64 data;
+ return stat64(filename, &data) == 0;
#else
- FILE *fp = _wfopen(utf16_t(fn), L"rb");
+ struct __stat64 data;
+ return _wstat64(utf16_t(filename), &data) == 0;
#endif
- if(fp) {
- fclose(fp);
- return true;
- }
- return false;
}
- static unsigned size(const char *fn) {
+ static uintmax_t size(const string &filename) {
#if !defined(_WIN32)
- FILE *fp = fopen(fn, "rb");
+ struct stat64 data;
+ stat64(filename, &data);
#else
- FILE *fp = _wfopen(utf16_t(fn), L"rb");
+ struct __stat64 data;
+ _wstat64(utf16_t(filename), &data);
#endif
- unsigned filesize = 0;
- if(fp) {
- fseek(fp, 0, SEEK_END);
- filesize = ftell(fp);
- fclose(fp);
+ return S_ISREG(data.st_mode) ? data.st_size : 0u;
+ }
+
+ static time_t timestamp(const string &filename, file::time mode = file::time::create) {
+ #if !defined(_WIN32)
+ struct stat64 data;
+ stat64(filename, &data);
+ #else
+ struct __stat64 data;
+ _wstat64(utf16_t(filename), &data);
+ #endif
+ switch(mode) { default:
+ case file::time::create: return data.st_ctime;
+ case file::time::modify: return data.st_mtime;
+ case file::time::access: return data.st_atime;
}
- return filesize;
}
bool open() {
return fp;
}
- bool open(const char *fn, mode mode_) {
+ bool open(const string &filename, mode mode_) {
if(fp) return false;
switch(file_mode = mode_) {
#if !defined(_WIN32)
- case mode::read: fp = fopen(fn, "rb"); break;
- case mode::write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
- case mode::readwrite: fp = fopen(fn, "rb+"); break;
- case mode::writeread: fp = fopen(fn, "wb+"); break;
+ case mode::read: fp = fopen(filename, "rb" ); break;
+ case mode::write: fp = fopen(filename, "wb+"); break; //need read permission for buffering
+ case mode::readwrite: fp = fopen(filename, "rb+"); break;
+ case mode::writeread: fp = fopen(filename, "wb+"); break;
#else
- case mode::read: fp = _wfopen(utf16_t(fn), L"rb"); break;
- case mode::write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
- case mode::readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
- case mode::writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
+ case mode::read: fp = _wfopen(utf16_t(filename), L"rb" ); break;
+ case mode::write: fp = _wfopen(utf16_t(filename), L"wb+"); break;
+ case mode::readwrite: fp = _wfopen(utf16_t(filename), L"rb+"); break;
+ case mode::writeread: fp = _wfopen(utf16_t(filename), L"wb+"); break;
#endif
}
if(!fp) return false;
diff --git a/snespurify/nall/filemap.hpp b/snespurify/nall/filemap.hpp
index 5e8cc059..7eeac2b0 100755
--- a/snespurify/nall/filemap.hpp
+++ b/snespurify/nall/filemap.hpp
@@ -2,7 +2,7 @@
#define NALL_FILEMAP_HPP
#include
-#include
+#include
#include
#include
@@ -47,6 +47,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
+ if(file::exists(filename) && file::size(filename) == 0) {
+ p_handle = 0;
+ p_size = 0;
+ return true;
+ }
+
int desired_access, creation_disposition, flprotect, map_access;
switch(mode_) {
@@ -133,6 +139,12 @@ namespace nall {
}
bool p_open(const char *filename, mode mode_) {
+ if(file::exists(filename) && file::size(filename) == 0) {
+ p_handle = 0;
+ p_size = 0;
+ return true;
+ }
+
int open_flags, mmap_flags;
switch(mode_) {
diff --git a/snespurify/nall/gameboy/cartridge.hpp b/snespurify/nall/gameboy/cartridge.hpp
index 0e1b28d8..af04e0bb 100755
--- a/snespurify/nall/gameboy/cartridge.hpp
+++ b/snespurify/nall/gameboy/cartridge.hpp
@@ -6,7 +6,7 @@ namespace nall {
class GameBoyCartridge {
public:
string xml;
- inline GameBoyCartridge(const uint8_t *data, unsigned size);
+ inline GameBoyCartridge(uint8_t *data, unsigned size);
//private:
struct Information {
@@ -21,7 +21,7 @@ public:
} info;
};
-GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
+GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
xml = "\n";
if(romsize < 0x4000) return;
@@ -34,6 +34,20 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
info.romsize = 0;
info.ramsize = 0;
+ unsigned base = romsize - 0x8000;
+ if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
+ && romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
+ && romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
+ && romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
+ ) {
+ //MMM01 stores header at bottom of image
+ //flip this around for consistency with all other mappers
+ uint8_t header[0x8000];
+ memcpy(header, romdata + base, 0x8000);
+ memmove(romdata + 0x8000, romdata, romsize - 0x8000);
+ memcpy(romdata, header, 0x8000);
+ }
+
switch(romdata[0x0147]) {
case 0x00: info.mapper = "none"; break;
case 0x01: info.mapper = "MBC1"; break;
@@ -86,17 +100,17 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
- xml << "\n";
+ xml.append("\n");
- xml << " \n"; //TODO: trust/check info.romsize?
+ xml.append(" \n"); //TODO: trust/check info.romsize?
if(info.ramsize > 0)
- xml << " \n";
+ xml.append(" \n");
- xml << "\n";
+ xml.append("\n");
xml.transform("'", "\"");
}
diff --git a/snespurify/nall/gzip.hpp b/snespurify/nall/gzip.hpp
new file mode 100755
index 00000000..635d3277
--- /dev/null
+++ b/snespurify/nall/gzip.hpp
@@ -0,0 +1,87 @@
+#ifndef NALL_GZIP_HPP
+#define NALL_GZIP_HPP
+
+#include
+#include
+
+namespace nall {
+
+struct gzip {
+ string filename;
+ uint8_t *data;
+ unsigned size;
+
+ bool decompress(const string &filename);
+ bool decompress(const uint8_t *data, unsigned size);
+
+ gzip();
+ ~gzip();
+};
+
+bool gzip::decompress(const string &filename) {
+ uint8_t *data;
+ unsigned size;
+ if(file::read(filename, data, size) == false) return false;
+ bool result = decompress(data, size);
+ delete[] data;
+ return result;
+}
+
+bool gzip::decompress(const uint8_t *data, unsigned size) {
+ if(size < 18) return false;
+ if(data[0] != 0x1f) return false;
+ if(data[1] != 0x8b) return false;
+ unsigned cm = data[2];
+ unsigned flg = data[3];
+ unsigned mtime = data[4];
+ mtime |= data[5] << 8;
+ mtime |= data[6] << 16;
+ mtime |= data[7] << 24;
+ unsigned xfl = data[8];
+ unsigned os = data[9];
+ unsigned p = 10;
+ unsigned isize = data[size - 4];
+ isize |= data[size - 3] << 8;
+ isize |= data[size - 2] << 16;
+ isize |= data[size - 1] << 24;
+ filename = "";
+
+ if(flg & 0x04) { //FEXTRA
+ unsigned xlen = data[p + 0];
+ xlen |= data[p + 1] << 8;
+ p += 2 + xlen;
+ }
+
+ if(flg & 0x08) { //FNAME
+ char buffer[PATH_MAX];
+ for(unsigned n = 0; n < PATH_MAX; n++, p++) {
+ buffer[n] = data[p];
+ if(data[p] == 0) break;
+ }
+ if(data[p++]) return false;
+ filename = buffer;
+ }
+
+ if(flg & 0x10) { //FCOMMENT
+ while(data[p++]);
+ }
+
+ if(flg & 0x02) { //FHCRC
+ p += 2;
+ }
+
+ this->size = isize;
+ this->data = new uint8_t[this->size];
+ return inflate(this->data, this->size, data + p, size - p - 8);
+}
+
+gzip::gzip() : data(0) {
+}
+
+gzip::~gzip() {
+ if(data) delete[] data;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/http.hpp b/snespurify/nall/http.hpp
new file mode 100755
index 00000000..1b2eab4f
--- /dev/null
+++ b/snespurify/nall/http.hpp
@@ -0,0 +1,176 @@
+#ifndef NALL_HTTP_HPP
+#define NALL_HTTP_HPP
+
+#if !defined(_WIN32)
+ #include
+ #include
+ #include
+ #include
+#else
+ #include
+ #include
+ #include
+#endif
+
+#include
+#include
+
+namespace nall {
+
+struct http {
+ string hostname;
+ addrinfo *serverinfo;
+ int serversocket;
+ string header;
+
+ inline void download(const string &path, uint8_t *&data, unsigned &size) {
+ data = 0;
+ size = 0;
+
+ send({
+ "GET ", path, " HTTP/1.1\r\n"
+ "Host: ", hostname, "\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ });
+
+ header = downloadHeader();
+ downloadContent(data, size);
+ }
+
+ inline bool connect(string host, unsigned port) {
+ hostname = host;
+
+ addrinfo hints;
+ memset(&hints, 0, sizeof(addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
+ if(status != 0) return false;
+
+ serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
+ if(serversocket == -1) return false;
+
+ int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
+ if(result == -1) return false;
+
+ return true;
+ }
+
+ inline bool send(const string &data) {
+ return send((const uint8_t*)(const char*)data, data.length());
+ }
+
+ inline bool send(const uint8_t *data, unsigned size) {
+ while(size) {
+ int length = ::send(serversocket, (const char*)data, size, 0);
+ if(length == -1) return false;
+ data += length;
+ size -= length;
+ }
+ return true;
+ }
+
+ inline string downloadHeader() {
+ string output;
+ do {
+ char buffer[2];
+ int length = recv(serversocket, buffer, 1, 0);
+ if(length <= 0) return output;
+ buffer[1] = 0;
+ output.append(buffer);
+ } while(output.endswith("\r\n\r\n") == false);
+ return output;
+ }
+
+ inline string downloadChunkLength() {
+ string output;
+ do {
+ char buffer[2];
+ int length = recv(serversocket, buffer, 1, 0);
+ if(length <= 0) return output;
+ buffer[1] = 0;
+ output.append(buffer);
+ } while(output.endswith("\r\n") == false);
+ return output;
+ }
+
+ inline void downloadContent(uint8_t *&data, unsigned &size) {
+ unsigned capacity = 0;
+
+ if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
+ while(true) {
+ unsigned length = hex(downloadChunkLength());
+ if(length == 0) break;
+ capacity += length;
+ data = (uint8_t*)realloc(data, capacity);
+
+ char buffer[length];
+ while(length) {
+ int packetlength = recv(serversocket, buffer, length, 0);
+ if(packetlength <= 0) break;
+ memcpy(data + size, buffer, packetlength);
+ size += packetlength;
+ length -= packetlength;
+ }
+ }
+ } else if(auto position = header.iposition("\r\nContent-Length: ")) {
+ unsigned length = decimal((const char*)header + position() + 16);
+ while(length) {
+ char buffer[256];
+ int packetlength = recv(serversocket, buffer, min(256, length), 0);
+ if(packetlength <= 0) break;
+ capacity += packetlength;
+ data = (uint8_t*)realloc(data, capacity);
+ memcpy(data + size, buffer, packetlength);
+ size += packetlength;
+ length -= packetlength;
+ }
+ } else {
+ while(true) {
+ char buffer[256];
+ int packetlength = recv(serversocket, buffer, 256, 0);
+ if(packetlength <= 0) break;
+ capacity += packetlength;
+ data = (uint8_t*)realloc(data, capacity);
+ memcpy(data + size, buffer, packetlength);
+ size += packetlength;
+ }
+ }
+
+ data = (uint8_t*)realloc(data, capacity + 1);
+ data[capacity] = 0;
+ }
+
+ inline void disconnect() {
+ close(serversocket);
+ freeaddrinfo(serverinfo);
+ serverinfo = 0;
+ serversocket = -1;
+ }
+
+ #ifdef _WIN32
+ inline int close(int sock) {
+ return closesocket(sock);
+ }
+
+ inline http() {
+ int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
+ WSADATA wsaData;
+ if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
+ WSACleanup();
+ return;
+ }
+ } else {
+ close(sock);
+ }
+ }
+ #endif
+};
+
+}
+
+#endif
diff --git a/snespurify/nall/inflate.hpp b/snespurify/nall/inflate.hpp
new file mode 100755
index 00000000..c989e3f1
--- /dev/null
+++ b/snespurify/nall/inflate.hpp
@@ -0,0 +1,358 @@
+#ifndef NALL_INFLATE_HPP
+#define NALL_INFLATE_HPP
+
+#include
+
+namespace nall {
+
+namespace puff {
+ inline int puff(
+ unsigned char *dest, unsigned long *destlen,
+ unsigned char *source, unsigned long *sourcelen
+ );
+}
+
+inline bool inflate(
+ uint8_t *target, unsigned targetLength,
+ const uint8_t *source, unsigned sourceLength
+) {
+ unsigned long tl = targetLength, sl = sourceLength;
+ int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
+ return result == 0;
+}
+
+namespace puff {
+
+//zlib/contrib/puff.c
+//version 2.1*
+//author: Mark Adler
+//license: zlib
+//ported by: byuu
+
+//* I have corrected a bug in fixed(), where it was accessing uninitialized
+// memory: calling construct() with lencode prior to initializing lencode.count
+
+enum {
+ MAXBITS = 15,
+ MAXLCODES = 286,
+ MAXDCODES = 30,
+ FIXLCODES = 288,
+ MAXCODES = MAXLCODES + MAXDCODES,
+};
+
+struct state {
+ unsigned char *out;
+ unsigned long outlen;
+ unsigned long outcnt;
+
+ unsigned char *in;
+ unsigned long inlen;
+ unsigned long incnt;
+ int bitbuf;
+ int bitcnt;
+
+ jmp_buf env;
+};
+
+struct huffman {
+ short *count;
+ short *symbol;
+};
+
+inline int bits(state *s, int need) {
+ long val;
+
+ val = s->bitbuf;
+ while(s->bitcnt < need) {
+ if(s->incnt == s->inlen) longjmp(s->env, 1);
+ val |= (long)(s->in[s->incnt++]) << s->bitcnt;
+ s->bitcnt += 8;
+ }
+
+ s->bitbuf = (int)(val >> need);
+ s->bitcnt -= need;
+
+ return (int)(val & ((1L << need) - 1));
+}
+
+inline int stored(state *s) {
+ unsigned len;
+
+ s->bitbuf = 0;
+ s->bitcnt = 0;
+
+ if(s->incnt + 4 > s->inlen) return 2;
+ len = s->in[s->incnt++];
+ len |= s->in[s->incnt++] << 8;
+ if(s->in[s->incnt++] != (~len & 0xff) ||
+ s->in[s->incnt++] != ((~len >> 8) & 0xff)
+ ) return 2;
+
+ if(s->incnt + len > s->inlen) return 2;
+ if(s->out != 0) {
+ if(s->outcnt + len > s->outlen) return 1;
+ while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
+ } else {
+ s->outcnt += len;
+ s->incnt += len;
+ }
+
+ return 0;
+}
+
+inline int decode(state *s, huffman *h) {
+ int len, code, first, count, index, bitbuf, left;
+ short *next;
+
+ bitbuf = s->bitbuf;
+ left = s->bitcnt;
+ code = first = index = 0;
+ len = 1;
+ next = h->count + 1;
+ while(true) {
+ while(left--) {
+ code |= bitbuf & 1;
+ bitbuf >>= 1;
+ count = *next++;
+ if(code - count < first) {
+ s->bitbuf = bitbuf;
+ s->bitcnt = (s->bitcnt - len) & 7;
+ return h->symbol[index + (code - first)];
+ }
+ index += count;
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ len++;
+ }
+ left = (MAXBITS + 1) - len;
+ if(left == 0) break;
+ if(s->incnt == s->inlen) longjmp(s->env, 1);
+ bitbuf = s->in[s->incnt++];
+ if(left > 8) left = 8;
+ }
+
+ return -10;
+}
+
+inline int construct(huffman *h, short *length, int n) {
+ int symbol, len, left;
+ short offs[MAXBITS + 1];
+
+ for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
+ for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
+ if(h->count[0] == n) return 0;
+
+ left = 1;
+ for(len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= h->count[len];
+ if(left < 0) return left;
+ }
+
+ offs[1] = 0;
+ for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
+
+ for(symbol = 0; symbol < n; symbol++) {
+ if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
+ }
+
+ return left;
+}
+
+inline int codes(state *s, huffman *lencode, huffman *distcode) {
+ int symbol, len;
+ unsigned dist;
+ static const short lens[29] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
+ };
+ static const short lext[29] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
+ };
+ static const short dists[30] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577
+ };
+ static const short dext[30] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13
+ };
+
+ do {
+ symbol = decode(s, lencode);
+ if(symbol < 0) return symbol;
+ if(symbol < 256) {
+ if(s->out != 0) {
+ if(s->outcnt == s->outlen) return 1;
+ s->out[s->outcnt] = symbol;
+ }
+ s->outcnt++;
+ } else if(symbol > 256) {
+ symbol -= 257;
+ if(symbol >= 29) return -10;
+ len = lens[symbol] + bits(s, lext[symbol]);
+
+ symbol = decode(s, distcode);
+ if(symbol < 0) return symbol;
+ dist = dists[symbol] + bits(s, dext[symbol]);
+#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
+ if(dist > s->outcnt) return -11;
+#endif
+
+ if(s->out != 0) {
+ if(s->outcnt + len > s->outlen) return 1;
+ while(len--) {
+ s->out[s->outcnt] =
+#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
+ dist > s->outcnt ? 0 :
+#endif
+ s->out[s->outcnt - dist];
+ s->outcnt++;
+ }
+ } else {
+ s->outcnt += len;
+ }
+ }
+ } while(symbol != 256);
+
+ return 0;
+}
+
+inline int fixed(state *s) {
+ static int virgin = 1;
+ static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
+ static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
+ static huffman lencode, distcode;
+
+ if(virgin) {
+ int symbol = 0;
+ short lengths[FIXLCODES];
+
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+
+ for(; symbol < 144; symbol++) lengths[symbol] = 8;
+ for(; symbol < 256; symbol++) lengths[symbol] = 9;
+ for(; symbol < 280; symbol++) lengths[symbol] = 7;
+ for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
+ construct(&lencode, lengths, FIXLCODES);
+
+ for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
+ construct(&distcode, lengths, MAXDCODES);
+
+ virgin = 0;
+ }
+
+ return codes(s, &lencode, &distcode);
+}
+
+inline int dynamic(state *s) {
+ int nlen, ndist, ncode, index, err;
+ short lengths[MAXCODES];
+ short lencnt[MAXBITS + 1], lensym[MAXLCODES];
+ short distcnt[MAXBITS + 1], distsym[MAXDCODES];
+ huffman lencode, distcode;
+ static const short order[19] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+ };
+
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+
+ nlen = bits(s, 5) + 257;
+ ndist = bits(s, 5) + 1;
+ ncode = bits(s, 4) + 4;
+ if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
+
+ for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
+ for(; index < 19; index++) lengths[order[index]] = 0;
+
+ err = construct(&lencode, lengths, 19);
+ if(err != 0) return -4;
+
+ index = 0;
+ while(index < nlen + ndist) {
+ int symbol, len;
+
+ symbol = decode(s, &lencode);
+ if(symbol < 16) {
+ lengths[index++] = symbol;
+ } else {
+ len = 0;
+ if(symbol == 16) {
+ if(index == 0) return -5;
+ len = lengths[index - 1];
+ symbol = 3 + bits(s, 2);
+ } else if(symbol == 17) {
+ symbol = 3 + bits(s, 3);
+ } else {
+ symbol = 11 + bits(s, 7);
+ }
+ if(index + symbol > nlen + ndist) return -6;
+ while(symbol--) lengths[index++] = len;
+ }
+ }
+
+ if(lengths[256] == 0) return -9;
+
+ err = construct(&lencode, lengths, nlen);
+ if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
+
+ err = construct(&distcode, lengths + nlen, ndist);
+ if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
+
+ return codes(s, &lencode, &distcode);
+}
+
+inline int puff(
+ unsigned char *dest, unsigned long *destlen,
+ unsigned char *source, unsigned long *sourcelen
+) {
+ state s;
+ int last, type, err;
+
+ s.out = dest;
+ s.outlen = *destlen;
+ s.outcnt = 0;
+
+ s.in = source;
+ s.inlen = *sourcelen;
+ s.incnt = 0;
+ s.bitbuf = 0;
+ s.bitcnt = 0;
+
+ if(setjmp(s.env) != 0) {
+ err = 2;
+ } else {
+ do {
+ last = bits(&s, 1);
+ type = bits(&s, 2);
+ err = type == 0 ? stored(&s)
+ : type == 1 ? fixed(&s)
+ : type == 2 ? dynamic(&s)
+ : -1;
+ if(err != 0) break;
+ } while(!last);
+ }
+
+ if(err <= 0) {
+ *destlen = s.outcnt;
+ *sourcelen = s.incnt;
+ }
+
+ return err;
+}
+
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/input.hpp b/snespurify/nall/input.hpp
index 1fd680f4..cd765393 100755
--- a/snespurify/nall/input.hpp
+++ b/snespurify/nall/input.hpp
@@ -110,7 +110,7 @@ struct Keyboard {
break;
}
}
- return string() << "KB" << ID << "::" << KeyboardScancodeName[index];
+ return { "KB", ID, "::", KeyboardScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
@@ -207,7 +207,7 @@ struct Mouse {
break;
}
}
- return string() << "MS" << ID << "::" << MouseScancodeName[index];
+ return { "MS", ID, "::", MouseScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
@@ -330,7 +330,7 @@ struct Joypad {
index = code - (Base + Size * i);
}
}
- return string() << "JP" << ID << "::" << JoypadScancodeName[index];
+ return { "JP", ID, "::", JoypadScancodeName[index] };
}
uint16_t operator[](Scancode code) const { return Base + ID * Size + code; }
diff --git a/snespurify/nall/ips.hpp b/snespurify/nall/ips.hpp
new file mode 100755
index 00000000..87c7de25
--- /dev/null
+++ b/snespurify/nall/ips.hpp
@@ -0,0 +1,110 @@
+#ifndef NALL_IPS_HPP
+#define NALL_IPS_HPP
+
+#include
+#include
+#include
+
+namespace nall {
+
+struct ips {
+ inline bool apply();
+ inline void source(const uint8_t *data, unsigned size);
+ inline void modify(const uint8_t *data, unsigned size);
+ inline bool source(const string &filename);
+ inline bool modify(const string &filename);
+ inline ips();
+ inline ~ips();
+
+ uint8_t *data;
+ unsigned size;
+ const uint8_t *sourceData;
+ unsigned sourceSize;
+ const uint8_t *modifyData;
+ unsigned modifySize;
+};
+
+bool ips::apply() {
+ if(modifySize < 8) return false;
+ if(modifyData[0] != 'P') return false;
+ if(modifyData[1] != 'A') return false;
+ if(modifyData[2] != 'T') return false;
+ if(modifyData[3] != 'C') return false;
+ if(modifyData[4] != 'H') return false;
+
+ if(data) delete[] data;
+ data = new uint8_t[16 * 1024 * 1024 + 65536](); //maximum size of IPS patch + single-tag padding
+ size = sourceSize;
+ memcpy(data, sourceData, sourceSize);
+ unsigned offset = 5;
+
+ while(true) {
+ unsigned address, length;
+
+ if(offset > modifySize - 3) break;
+ address = modifyData[offset++] << 16;
+ address |= modifyData[offset++] << 8;
+ address |= modifyData[offset++] << 0;
+
+ if(address == 0x454f46) { //EOF
+ if(offset == modifySize) return true;
+ if(offset == modifySize - 3) {
+ size = modifyData[offset++] << 16;
+ size |= modifyData[offset++] << 8;
+ size |= modifyData[offset++] << 0;
+ return true;
+ }
+ }
+
+ if(offset > modifySize - 2) break;
+ length = modifyData[offset++] << 8;
+ length |= modifyData[offset++] << 0;
+
+ if(length) { //Copy
+ if(offset > modifySize - length) break;
+ while(length--) data[address++] = modifyData[offset++];
+ } else { //RLE
+ if(offset > modifySize - 3) break;
+ length = modifyData[offset++] << 8;
+ length |= modifyData[offset++] << 0;
+ if(length == 0) break; //illegal
+ while(length--) data[address++] = modifyData[offset];
+ offset++;
+ }
+
+ size = max(size, address);
+ }
+
+ delete[] data;
+ data = 0;
+ return false;
+}
+
+void ips::source(const uint8_t *data, unsigned size) {
+ sourceData = data, sourceSize = size;
+}
+
+void ips::modify(const uint8_t *data, unsigned size) {
+ modifyData = data, modifySize = size;
+}
+
+bool ips::source(const string &filename) {
+ return file::read(filename, sourceData, sourceSize);
+}
+
+bool ips::modify(const string &filename) {
+ return file::read(filename, modifyData, modifySize);
+}
+
+ips::ips() : data(0), sourceData(0), modifyData(0) {
+}
+
+ips::~ips() {
+ if(data) delete[] data;
+ if(sourceData) delete[] sourceData;
+ if(modifyData) delete[] modifyData;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/lzss.hpp b/snespurify/nall/lzss.hpp
index 202bc814..147e1e62 100755
--- a/snespurify/nall/lzss.hpp
+++ b/snespurify/nall/lzss.hpp
@@ -1,81 +1,165 @@
#ifndef NALL_LZSS_HPP
#define NALL_LZSS_HPP
-#include
-#include
+#include
+#include
#include
+#include
namespace nall {
- class lzss {
- public:
- static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) {
- output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9];
- unsigned i = 0, o = 0;
- while(i < inlength) {
- unsigned flagoffset = o++;
- uint8_t flag = 0x00;
+//19:5 pulldown
+//8:1 marker: d7-d0
+//length: { 4 - 35 }, offset: { 1 - 0x80000 }
+//4-byte file size header
+//little-endian encoding
+struct lzss {
+ inline void source(const uint8_t *data, unsigned size);
+ inline bool source(const string &filename);
+ inline unsigned size() const;
+ inline bool compress(const string &filename);
+ inline bool decompress(uint8_t *targetData, unsigned targetSize);
+ inline bool decompress(const string &filename);
- for(unsigned b = 0; b < 8 && i < inlength; b++) {
- unsigned longest = 0, pointer;
- for(unsigned index = 1; index < 4096; index++) {
- unsigned count = 0;
- while(true) {
- if(count >= 15 + 3) break; //verify pattern match is not longer than max length
- if(i + count >= inlength) break; //verify pattern match does not read past end of input
- if(i + count < index) break; //verify read is not before start of input
- if(input[i + count] != input[i + count - index]) break; //verify pattern still matches
- count++;
- }
+protected:
+ struct Node {
+ unsigned offset;
+ Node *next;
+ inline Node() : offset(0), next(0) {}
+ inline ~Node() { if(next) delete next; }
+ } *tree[65536];
- if(count > longest) {
- longest = count;
- pointer = index;
- }
- }
+ filemap sourceFile;
+ const uint8_t *sourceData;
+ unsigned sourceSize;
- if(longest < 3) output[o++] = input[i++];
- else {
- flag |= 1 << b;
- uint16_t x = ((longest - 3) << 12) + pointer;
- output[o++] = x;
- output[o++] = x >> 8;
- i += longest;
- }
+public:
+ inline lzss() : sourceData(0), sourceSize(0) {}
+};
+
+void lzss::source(const uint8_t *data, unsigned size) {
+ sourceData = data;
+ sourceSize = size;
+}
+
+bool lzss::source(const string &filename) {
+ if(sourceFile.open(filename, filemap::mode::read) == false) return false;
+ sourceData = sourceFile.data();
+ sourceSize = sourceFile.size();
+ return true;
+}
+
+unsigned lzss::size() const {
+ unsigned size = 0;
+ if(sourceSize < 4) return size;
+ for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
+ return size;
+}
+
+bool lzss::compress(const string &filename) {
+ file targetFile;
+ if(targetFile.open(filename, file::mode::write) == false) return false;
+
+ for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
+ for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
+
+ uint8_t buffer[25];
+ unsigned sourceOffset = 0;
+
+ while(sourceOffset < sourceSize) {
+ uint8_t mask = 0x00;
+ unsigned bufferOffset = 1;
+
+ for(unsigned iteration = 0; iteration < 8; iteration++) {
+ if(sourceOffset >= sourceSize) break;
+
+ uint16_t symbol = sourceData[sourceOffset + 0];
+ if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
+ Node *node = tree[symbol];
+ unsigned maxLength = 0, maxOffset = 0;
+
+ while(node) {
+ if(node->offset < sourceOffset - 0x80000) {
+ //out-of-range: all subsequent nodes will also be, so free up their memory
+ if(node->next) { delete node->next; node->next = 0; }
+ break;
}
- output[flagoffset] = flag;
+ unsigned length = 0, x = sourceOffset, y = node->offset;
+ while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
+ if(length > maxLength) maxLength = length, maxOffset = node->offset;
+ if(length == 35) break;
+
+ node = node->next;
}
- outlength = o;
- return true;
- }
+ //attach current symbol to top of tree for subsequent searches
+ node = new Node;
+ node->offset = sourceOffset;
+ node->next = tree[symbol];
+ tree[symbol] = node;
- static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) {
- output = new(zeromemory) uint8_t[length];
-
- unsigned i = 0, o = 0;
- while(o < length) {
- uint8_t flag = input[i++];
-
- for(unsigned b = 0; b < 8 && o < length; b++) {
- if(!(flag & (1 << b))) output[o++] = input[i++];
- else {
- uint16_t offset = input[i++];
- offset += input[i++] << 8;
- uint16_t lookuplength = (offset >> 12) + 3;
- offset &= 4095;
- for(unsigned index = 0; index < lookuplength && o + index < length; index++) {
- output[o + index] = output[o + index - offset];
- }
- o += lookuplength;
- }
- }
+ if(maxLength < 4) {
+ buffer[bufferOffset++] = sourceData[sourceOffset++];
+ } else {
+ unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
+ for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
+ mask |= 0x80 >> iteration;
+ sourceOffset += maxLength;
}
-
- return true;
}
- };
+
+ buffer[0] = mask;
+ targetFile.write(buffer, bufferOffset);
+ }
+
+ sourceFile.close();
+ targetFile.close();
+ return true;
+}
+
+bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
+ if(targetSize < size()) return false;
+
+ unsigned sourceOffset = 4, targetOffset = 0;
+ while(sourceOffset < sourceSize) {
+ uint8_t mask = sourceData[sourceOffset++];
+
+ for(unsigned iteration = 0; iteration < 8; iteration++) {
+ if(sourceOffset >= sourceSize) break;
+
+ if((mask & (0x80 >> iteration)) == 0) {
+ targetData[targetOffset++] = sourceData[sourceOffset++];
+ } else {
+ unsigned code = 0;
+ for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
+ unsigned length = (code >> 19) + 4;
+ unsigned offset = targetOffset - 1 - (code & 0x7ffff);
+ while(length--) targetData[targetOffset++] = targetData[offset++];
+ }
+ }
+ }
+}
+
+bool lzss::decompress(const string &filename) {
+ if(sourceSize < 4) return false;
+ unsigned targetSize = size();
+
+ file fp;
+ if(fp.open(filename, file::mode::write) == false) return false;
+ fp.truncate(targetSize);
+ fp.close();
+
+ filemap targetFile;
+ if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
+ uint8_t *targetData = targetFile.data();
+
+ bool result = decompress(targetData, targetSize);
+ sourceFile.close();
+ targetFile.close();
+ return result;
+}
+
}
#endif
diff --git a/snespurify/nall/platform.hpp b/snespurify/nall/platform.hpp
index 72eeec09..8e7bb5fe 100755
--- a/snespurify/nall/platform.hpp
+++ b/snespurify/nall/platform.hpp
@@ -1,7 +1,12 @@
#ifndef NALL_PLATFORM_HPP
#define NALL_PLATFORM_HPP
-#include
+#if defined(_WIN32)
+ //minimum version needed for _wstat64, etc
+ #undef __MSVCRT_VERSION__
+ #define __MSVCRT_VERSION__ 0x0601
+ #include
+#endif
//=========================
//standard platform headers
@@ -18,16 +23,19 @@
#include
#include
+#include
+#include
+
#if defined(_WIN32)
#include
#include
#include
+ #include
#undef interface
#define dllexport __declspec(dllexport)
#else
#include
#include
- #include
#define dllexport
#endif
@@ -53,11 +61,11 @@
#if defined(_WIN32)
#define getcwd _getcwd
#define ftruncate _chsize
- #define putenv _putenv
#define mkdir(n, m) _wmkdir(nall::utf16_t(n))
+ #define putenv _putenv
#define rmdir _rmdir
- #define vsnprintf _vsnprintf
#define usleep(n) Sleep(n / 1000)
+ #define vsnprintf _vsnprintf
#endif
//================
@@ -87,6 +95,7 @@
wchar_t fn[_MAX_PATH] = L"";
_wfullpath(fn, nall::utf16_t(filename), _MAX_PATH);
strcpy(resolvedname, nall::utf8_t(fn));
+ for(unsigned n = 0; resolvedname[n]; n++) if(resolvedname[n] == '\\') resolvedname[n] = '/';
return resolvedname;
}
@@ -94,6 +103,7 @@
wchar_t fp[_MAX_PATH] = L"";
SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp);
strcpy(path, nall::utf8_t(fp));
+ for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
@@ -101,6 +111,7 @@
wchar_t fp[_MAX_PATH] = L"";
_wgetcwd(fp, _MAX_PATH);
strcpy(path, nall::utf8_t(fp));
+ for(unsigned n = 0; path[n]; n++) if(path[n] == '\\') path[n] = '/';
return path;
}
#else
diff --git a/snespurify/nall/png.hpp b/snespurify/nall/png.hpp
new file mode 100755
index 00000000..ba6f05f8
--- /dev/null
+++ b/snespurify/nall/png.hpp
@@ -0,0 +1,423 @@
+#ifndef NALL_PNG_HPP
+#define NALL_PNG_HPP
+
+//PNG image decoder
+//author: byuu
+
+#include
+#include
+
+namespace nall {
+
+struct png {
+ uint32_t *data;
+ unsigned size;
+
+ struct Info {
+ unsigned width;
+ unsigned height;
+ unsigned bitDepth;
+ unsigned colorType;
+ unsigned compressionMethod;
+ unsigned filterType;
+ unsigned interlaceMethod;
+
+ unsigned bytesPerPixel;
+ unsigned pitch;
+
+ uint8_t palette[256][3];
+ } info;
+
+ uint8_t *rawData;
+ unsigned rawSize;
+
+ inline bool decode(const string &filename);
+ inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
+ inline void transform();
+ inline void alphaTransform(uint32_t rgb = 0xffffff);
+ inline png();
+ inline ~png();
+
+protected:
+ enum class FourCC : unsigned {
+ IHDR = 0x49484452,
+ PLTE = 0x504c5445,
+ IDAT = 0x49444154,
+ IEND = 0x49454e44,
+ };
+
+ static const unsigned interlace[7][4];
+ unsigned bitpos;
+
+ inline unsigned inflateSize();
+ inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
+ inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
+ inline unsigned read(const uint8_t *data, unsigned length);
+ inline unsigned decode(const uint8_t *&data);
+ inline unsigned readbits(const uint8_t *&data);
+ inline unsigned scale(unsigned n);
+};
+
+bool png::decode(const string &filename) {
+ uint8_t *data;
+ unsigned size;
+ if(file::read(filename, data, size) == false) return false;
+ bool result = decode(data, size);
+ delete[] data;
+ return result;
+}
+
+bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
+ if(sourceSize < 8) return false;
+ if(read(sourceData + 0, 4) != 0x89504e47) return false;
+ if(read(sourceData + 4, 4) != 0x0d0a1a0a) return false;
+
+ uint8_t *compressedData = 0;
+ unsigned compressedSize = 0;
+
+ unsigned offset = 8;
+ while(offset < sourceSize) {
+ unsigned length = read(sourceData + offset + 0, 4);
+ unsigned fourCC = read(sourceData + offset + 4, 4);
+ unsigned checksum = read(sourceData + offset + 8 + length, 4);
+
+ if(fourCC == (unsigned)FourCC::IHDR) {
+ info.width = read(sourceData + offset + 8, 4);
+ info.height = read(sourceData + offset + 12, 4);
+ info.bitDepth = read(sourceData + offset + 16, 1);
+ info.colorType = read(sourceData + offset + 17, 1);
+ info.compressionMethod = read(sourceData + offset + 18, 1);
+ info.filterType = read(sourceData + offset + 19, 1);
+ info.interlaceMethod = read(sourceData + offset + 20, 1);
+
+ if(info.bitDepth == 0 || info.bitDepth > 16) return false;
+ if(info.bitDepth & (info.bitDepth - 1)) return false; //not a power of two
+ if(info.compressionMethod != 0) return false;
+ if(info.filterType != 0) return false;
+ if(info.interlaceMethod != 0 && info.interlaceMethod != 1) return false;
+
+ switch(info.colorType) {
+ case 0: info.bytesPerPixel = info.bitDepth * 1; break; //L
+ case 2: info.bytesPerPixel = info.bitDepth * 3; break; //R,G,B
+ case 3: info.bytesPerPixel = info.bitDepth * 1; break; //P
+ case 4: info.bytesPerPixel = info.bitDepth * 2; break; //L,A
+ case 6: info.bytesPerPixel = info.bitDepth * 4; break; //R,G,B,A
+ default: return false;
+ }
+
+ if(info.colorType == 2 || info.colorType == 4 || info.colorType == 6)
+ if(info.bitDepth != 8 && info.bitDepth != 16) return false;
+ if(info.colorType == 3 && info.bitDepth == 16) return false;
+
+ info.bytesPerPixel = (info.bytesPerPixel + 7) / 8;
+ info.pitch = (int)info.width * info.bytesPerPixel;
+ }
+
+ if(fourCC == (unsigned)FourCC::PLTE) {
+ if(length % 3) return false;
+ for(unsigned n = 0, p = offset + 8; n < length / 3; n++) {
+ info.palette[n][0] = sourceData[p++];
+ info.palette[n][1] = sourceData[p++];
+ info.palette[n][2] = sourceData[p++];
+ }
+ }
+
+ if(fourCC == (unsigned)FourCC::IDAT) {
+ compressedData = (uint8_t*)realloc(compressedData, compressedSize + length);
+ memcpy(compressedData + compressedSize, sourceData + offset + 8, length);
+ compressedSize += length;
+ }
+
+ if(fourCC == (unsigned)FourCC::IEND) {
+ break;
+ }
+
+ offset += 4 + 4 + length + 4;
+ }
+
+ unsigned interlacedSize = inflateSize();
+ uint8_t *interlacedData = new uint8_t[interlacedSize];
+
+ bool result = inflate(interlacedData, interlacedSize, compressedData + 2, compressedSize - 6);
+ delete[] compressedData;
+
+ if(result == false) {
+ delete[] interlacedData;
+ return false;
+ }
+
+ rawSize = info.width * info.height * info.bytesPerPixel;
+ rawData = new uint8_t[rawSize];
+
+ if(info.interlaceMethod == 0) {
+ if(filter(rawData, interlacedData, info.width, info.height) == false) {
+ delete[] interlacedData;
+ delete[] rawData;
+ rawData = 0;
+ return false;
+ }
+ } else {
+ const uint8_t *passData = interlacedData;
+ for(unsigned pass = 0; pass < 7; pass++) {
+ if(deinterlace(passData, pass) == false) {
+ delete[] interlacedData;
+ delete[] rawData;
+ rawData = 0;
+ return false;
+ }
+ }
+ }
+
+ delete[] interlacedData;
+ return true;
+}
+
+const unsigned png::interlace[7][4] = {
+ //x-distance, y-distance, x-origin, y-origin
+ { 8, 8, 0, 0 },
+ { 8, 8, 4, 0 },
+ { 4, 8, 0, 4 },
+ { 4, 4, 2, 0 },
+ { 2, 4, 0, 2 },
+ { 2, 2, 1, 0 },
+ { 1, 2, 0, 1 },
+};
+
+unsigned png::inflateSize() {
+ if(info.interlaceMethod == 0) {
+ return info.width * info.height * info.bytesPerPixel + info.height;
+ }
+
+ unsigned size = 0;
+ for(unsigned pass = 0; pass < 7; pass++) {
+ unsigned xd = interlace[pass][0], yd = interlace[pass][1];
+ unsigned xo = interlace[pass][2], yo = interlace[pass][3];
+ unsigned width = (info.width + (xd - xo - 1)) / xd;
+ unsigned height = (info.height + (yd - yo - 1)) / yd;
+ if(width == 0 || height == 0) continue;
+ size += width * height * info.bytesPerPixel + height;
+ }
+ return size;
+}
+
+bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
+ unsigned xd = interlace[pass][0], yd = interlace[pass][1];
+ unsigned xo = interlace[pass][2], yo = interlace[pass][3];
+ unsigned width = (info.width + (xd - xo - 1)) / xd;
+ unsigned height = (info.height + (yd - yo - 1)) / yd;
+ if(width == 0 || height == 0) return true;
+
+ unsigned outputSize = width * height * info.bytesPerPixel;
+ uint8_t *outputData = new uint8_t[outputSize];
+ bool result = filter(outputData, inputData, width, height);
+
+ const uint8_t *rd = outputData;
+ for(unsigned y = yo; y < info.height; y += yd) {
+ uint8_t *wr = rawData + y * info.pitch;
+ for(unsigned x = xo; x < info.width; x += xd) {
+ for(unsigned b = 0; b < info.bytesPerPixel; b++) {
+ wr[x * info.bytesPerPixel + b] = *rd++;
+ }
+ }
+ }
+
+ inputData += outputSize + height;
+ delete[] outputData;
+ return result;
+}
+
+bool png::filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height) {
+ uint8_t *wr = outputData;
+ const uint8_t *rd = inputData;
+ int bpp = info.bytesPerPixel, pitch = width * bpp;
+ for(int y = 0; y < height; y++) {
+ uint8_t filter = *rd++;
+
+ switch(filter) {
+ case 0x00: //None
+ for(int x = 0; x < pitch; x++) {
+ wr[x] = rd[x];
+ }
+ break;
+
+ case 0x01: //Subtract
+ for(int x = 0; x < pitch; x++) {
+ wr[x] = rd[x] + (x - bpp < 0 ? 0 : wr[x - bpp]);
+ }
+ break;
+
+ case 0x02: //Above
+ for(int x = 0; x < pitch; x++) {
+ wr[x] = rd[x] + (y - 1 < 0 ? 0 : wr[x - pitch]);
+ }
+ break;
+
+ case 0x03: //Average
+ for(int x = 0; x < pitch; x++) {
+ short a = x - bpp < 0 ? 0 : wr[x - bpp];
+ short b = y - 1 < 0 ? 0 : wr[x - pitch];
+
+ wr[x] = rd[x] + (uint8_t)((a + b) / 2);
+ }
+ break;
+
+ case 0x04: //Paeth
+ for(int x = 0; x < pitch; x++) {
+ short a = x - bpp < 0 ? 0 : wr[x - bpp];
+ short b = y - 1 < 0 ? 0 : wr[x - pitch];
+ short c = x - bpp < 0 || y - 1 < 0 ? 0 : wr[x - pitch - bpp];
+
+ short p = a + b - c;
+ short pa = p > a ? p - a : a - p;
+ short pb = p > b ? p - b : b - p;
+ short pc = p > c ? p - c : c - p;
+
+ uint8_t paeth = (uint8_t)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
+
+ wr[x] = rd[x] + paeth;
+ }
+ break;
+
+ default: //Invalid
+ return false;
+ }
+
+ rd += pitch;
+ wr += pitch;
+ }
+
+ return true;
+}
+
+unsigned png::read(const uint8_t *data, unsigned length) {
+ unsigned result = 0;
+ while(length--) result = (result << 8) | (*data++);
+ return result;
+}
+
+unsigned png::decode(const uint8_t *&data) {
+ unsigned p, r, g, b, a;
+
+ switch(info.colorType) {
+ case 0: //L
+ r = g = b = scale(readbits(data));
+ a = 0xff;
+ break;
+ case 2: //R,G,B
+ r = scale(readbits(data));
+ g = scale(readbits(data));
+ b = scale(readbits(data));
+ a = 0xff;
+ break;
+ case 3: //P
+ p = readbits(data);
+ r = info.palette[p][0];
+ g = info.palette[p][1];
+ b = info.palette[p][2];
+ a = 0xff;
+ break;
+ case 4: //L,A
+ r = g = b = scale(readbits(data));
+ a = scale(readbits(data));
+ break;
+ case 6: //R,G,B,A
+ r = scale(readbits(data));
+ g = scale(readbits(data));
+ b = scale(readbits(data));
+ a = scale(readbits(data));
+ break;
+ }
+
+ return (a << 24) | (r << 16) | (g << 8) | (b << 0);
+}
+
+unsigned png::readbits(const uint8_t *&data) {
+ unsigned result = 0;
+ switch(info.bitDepth) {
+ case 1:
+ result = (*data >> bitpos) & 1;
+ bitpos++;
+ if(bitpos == 8) { data++; bitpos = 0; }
+ break;
+ case 2:
+ result = (*data >> bitpos) & 3;
+ bitpos += 2;
+ if(bitpos == 8) { data++; bitpos = 0; }
+ break;
+ case 4:
+ result = (*data >> bitpos) & 15;
+ bitpos += 4;
+ if(bitpos == 8) { data++; bitpos = 0; }
+ break;
+ case 8:
+ result = *data++;
+ break;
+ case 16:
+ result = (data[0] << 8) | (data[1] << 0);
+ data += 2;
+ break;
+ }
+ return result;
+}
+
+unsigned png::scale(unsigned n) {
+ switch(info.bitDepth) {
+ case 1: return n ? 0xff : 0x00;
+ case 2: return n * 0x55;
+ case 4: return n * 0x11;
+ case 8: return n;
+ case 16: return n >> 8;
+ }
+ return 0;
+}
+
+void png::transform() {
+ if(data) delete[] data;
+ data = new uint32_t[info.width * info.height];
+
+ bitpos = 0;
+ const uint8_t *rd = rawData;
+ for(unsigned y = 0; y < info.height; y++) {
+ uint32_t *wr = data + y * info.width;
+ for(unsigned x = 0; x < info.width; x++) {
+ wr[x] = decode(rd);
+ }
+ }
+}
+
+void png::alphaTransform(uint32_t rgb) {
+ transform();
+
+ uint8_t ir = rgb >> 16;
+ uint8_t ig = rgb >> 8;
+ uint8_t ib = rgb >> 0;
+
+ uint32_t *p = data;
+ for(unsigned y = 0; y < info.height; y++) {
+ for(unsigned x = 0; x < info.width; x++) {
+ uint32_t pixel = *p;
+ uint8_t a = pixel >> 24;
+ uint8_t r = pixel >> 16;
+ uint8_t g = pixel >> 8;
+ uint8_t b = pixel >> 0;
+
+ r = (r * a) + (ir * (255 - a)) >> 8;
+ g = (g * a) + (ig * (255 - a)) >> 8;
+ b = (b * a) + (ib * (255 - a)) >> 8;
+
+ *p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
+ }
+ }
+}
+
+png::png() : data(0), rawData(0) {
+}
+
+png::~png() {
+ if(data) delete[] data;
+ if(rawData) delete[] rawData;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/resource.hpp b/snespurify/nall/resource.hpp
new file mode 100755
index 00000000..f8fd5153
--- /dev/null
+++ b/snespurify/nall/resource.hpp
@@ -0,0 +1,61 @@
+#ifndef NALL_RESOURCE_HPP
+#define NALL_RESOURCE_HPP
+
+#include
+#include
+
+namespace nall {
+
+struct resource {
+ //create resource with "zip -9 resource.zip resource"
+ static bool encode(const char *outputFilename, const char *inputFilename) {
+ file fp;
+ if(fp.open(inputFilename, file::mode::read) == false) return false;
+ unsigned size = fp.size();
+ uint8_t *data = new uint8_t[size];
+ fp.read(data, size);
+ fp.close();
+
+ fp.open(outputFilename, file::mode::write);
+ fp.print("static const uint8_t data[", size, "] = {\n");
+ uint8_t *p = data;
+ while(size) {
+ fp.print(" ");
+ for(unsigned n = 0; n < 32 && size; n++, size--) {
+ fp.print((unsigned)*p++, ",");
+ }
+ fp.print("\n");
+ }
+ fp.print("};\n");
+ fp.close();
+
+ delete[] data;
+ }
+
+ uint8_t *data;
+ unsigned size;
+
+ //extract first file from ZIP archive
+ bool decode(const uint8_t *cdata, unsigned csize) {
+ if(data) delete[] data;
+
+ zip archive;
+ if(archive.open(cdata, csize) == false) return false;
+ if(archive.file.size() == 0) return false;
+ bool result = archive.extract(archive.file[0], data, size);
+ archive.close();
+
+ return result;
+ }
+
+ resource() : data(0), size(0) {
+ }
+
+ ~resource() {
+ if(data) delete[] data;
+ }
+};
+
+}
+
+#endif
diff --git a/snespurify/nall/sha256.hpp b/snespurify/nall/sha256.hpp
index 7f41f04e..c63367a7 100755
--- a/snespurify/nall/sha256.hpp
+++ b/snespurify/nall/sha256.hpp
@@ -3,6 +3,8 @@
//author: vladitx
+#include
+
namespace nall {
#define PTR(t, a) ((t*)(a))
@@ -49,7 +51,7 @@ namespace nall {
uint64_t len;
};
- void sha256_init(sha256_ctx *p) {
+ inline void sha256_init(sha256_ctx *p) {
memset(p, 0, sizeof(sha256_ctx));
memcpy(p->h, T_H, sizeof(T_H));
}
@@ -90,7 +92,7 @@ namespace nall {
p->inlen = 0;
}
- void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
+ inline void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) {
unsigned l;
p->len += len;
@@ -107,7 +109,7 @@ namespace nall {
}
}
- void sha256_final(sha256_ctx *p) {
+ inline void sha256_final(sha256_ctx *p) {
uint64_t len;
p->in[p->inlen++] = 0x80;
@@ -124,7 +126,7 @@ namespace nall {
sha256_block(p);
}
- void sha256_hash(sha256_ctx *p, uint8_t *s) {
+ inline void sha256_hash(sha256_ctx *p, uint8_t *s) {
uint32_t *t = (uint32_t*)s;
for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]);
}
diff --git a/snespurify/nall/snes/cartridge.hpp b/snespurify/nall/snes/cartridge.hpp
index e3c0e0c5..6847ba3a 100755
--- a/snespurify/nall/snes/cartridge.hpp
+++ b/snespurify/nall/snes/cartridge.hpp
@@ -111,422 +111,426 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
string xml = "\n";
if(type == TypeBsx) {
- xml << "";
+ xml.append("");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeSufamiTurbo) {
- xml << "";
+ xml.append("");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
if(type == TypeGameBoy) {
- xml << "\n";
+ xml.append("\n");
if(gameboy_ram_size(data, size) > 0) {
- xml << " \n";
+ xml.append(" \n");
}
- xml << "\n";
+ xml.append("\n");
xmlMemoryMap = xml.transform("'", "\"");
return;
}
- xml << "\n";
+ xml.append(">\n");
if(type == TypeSuperGameBoy1Bios) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(type == TypeSuperGameBoy2Bios) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ } else if(has_cx4) {
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(has_spc7110) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if(has_spc7110rtc) {
- xml << " \n";
+ xml.append(" \n");
}
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == LoROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if(ram_size > 0) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
} else {
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
}
- xml << " \n";
+ xml.append(" \n");
}
} else if(mapper == HiROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if(ram_size > 0) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
- xml << " \n";
+ xml.append(" \n");
} else {
- xml << " \n";
+ xml.append(" \n");
}
- xml << " \n";
+ xml.append(" \n");
}
} else if(mapper == ExLoROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if(ram_size > 0) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
} else if(mapper == ExHiROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if(ram_size > 0) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
- xml << " \n";
+ xml.append(" \n");
} else {
- xml << " \n";
+ xml.append(" \n");
}
- xml << " \n";
+ xml.append(" \n");
}
} else if(mapper == SuperFXROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == SA1ROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == BSCLoROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == BSCHiROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == BSXROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(mapper == STROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_srtc) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_sdd1) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- }
-
- if(has_cx4) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_dsp1) {
- xml << " \n";
+ xml.append(" \n");
if(dsp1_mapper == DSP1LoROM1MB) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(dsp1_mapper == DSP1LoROM2MB) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
} else if(dsp1_mapper == DSP1HiROM) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
- xml << " \n";
+ xml.append(" \n");
}
if(has_dsp2) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_dsp3) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_dsp4) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_obc1) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_st010) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_st011) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
if(has_st018) {
- xml << " \n";
- xml << " \n";
- xml << " \n";
- xml << " \n";
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
+ xml.append(" \n");
}
- xml << "\n";
+ xml.append("\n");
xmlMemoryMap = xml.transform("'", "\"");
}
diff --git a/snespurify/nall/stack.hpp b/snespurify/nall/stack.hpp
new file mode 100755
index 00000000..a4aacfa7
--- /dev/null
+++ b/snespurify/nall/stack.hpp
@@ -0,0 +1,29 @@
+#ifndef NALL_STACK_HPP
+#define NALL_STACK_HPP
+
+#include
+#include
+
+namespace nall {
+ template struct stack : public linear_vector {
+ void push(const T &value) {
+ linear_vector::append(value);
+ }
+
+ T pull() {
+ if(linear_vector::size() == 0) throw;
+ T value = linear_vector::operator[](linear_vector::size() - 1);
+ linear_vector::remove(linear_vector::size() - 1);
+ return value;
+ }
+
+ T& operator()() {
+ if(linear_vector::size() == 0) throw;
+ return linear_vector::operator[](linear_vector::size() - 1);
+ }
+ };
+
+ template struct has_size> { enum { value = true }; };
+}
+
+#endif
diff --git a/snespurify/nall/string.hpp b/snespurify/nall/string.hpp
index 9acc2e9d..91bee596 100755
--- a/snespurify/nall/string.hpp
+++ b/snespurify/nall/string.hpp
@@ -2,7 +2,9 @@
#define NALL_STRING_HPP
#include
+#include
#include
+#include
#include
#include
diff --git a/snespurify/nall/string/base.hpp b/snespurify/nall/string/base.hpp
index f6172c26..ef331515 100755
--- a/snespurify/nall/string/base.hpp
+++ b/snespurify/nall/string/base.hpp
@@ -6,12 +6,14 @@
#include
#include
#include
+#include
#include
-#include
#include
+#include
namespace nall {
class string;
+ class lstring;
template inline const char* to_string(T);
class string {
@@ -20,13 +22,13 @@ namespace nall {
template inline string& assign(Args&&... args);
template inline string& append(Args&&... args);
- inline string& assign_(const char*);
- inline string& append_(const char*);
inline bool readfile(const string&);
- inline string& replace (const char*, const char*);
- inline string& qreplace(const char*, const char*);
+ template inline string& replace(const char*, const char*);
+ template inline string& ireplace(const char*, const char*);
+ template inline string& qreplace(const char*, const char*);
+ template inline string& iqreplace(const char*, const char*);
inline unsigned length() const;
@@ -43,17 +45,18 @@ namespace nall {
inline string& lower();
inline string& upper();
+ inline string& qlower();
+ inline string& qupper();
inline string& transform(const char *before, const char *after);
template inline string& ltrim(const char *key = " ");
template inline string& rtrim(const char *key = " ");
- template inline string& trim (const char *key = " ");
+ template inline string& trim(const char *key = " ", const char *rkey = 0);
inline optional position(const char *key) const;
+ inline optional iposition(const char *key) const;
inline optional qposition(const char *key) const;
-
- template inline string& operator= (T value);
- template inline string& operator<<(T value);
+ inline optional iqposition(const char *key) const;
inline operator const char*() const;
inline char* operator()();
@@ -74,10 +77,16 @@ namespace nall {
inline string(string&&);
inline ~string();
+ //internal functions
+ inline string& assign_(const char*);
+ inline string& append_(const char*);
+
protected:
char *data;
unsigned size;
+ template inline string& ureplace(const char*, const char*);
+
#if defined(QSTRING_H)
public:
inline operator QString() const;
@@ -89,36 +98,43 @@ namespace nall {
template inline lstring& operator<<(T value);
inline optional find(const char*) const;
- template inline void split (const char*, const char*);
- template inline void qsplit(const char*, const char*);
+ template inline lstring& split(const char*, const char*);
+ template inline lstring& isplit(const char*, const char*);
+ template inline lstring& qsplit(const char*, const char*);
+ template inline lstring& iqsplit(const char*, const char*);
lstring();
lstring(std::initializer_list);
+
+ protected:
+ template inline lstring& usplit(const char*, const char*);
};
//compare.hpp
inline char chrlower(char c);
inline char chrupper(char c);
- inline int stricmp(const char *str1, const char *str2);
+ inline int istrcmp(const char *str1, const char *str2);
inline bool wildcard(const char *str, const char *pattern);
inline bool iwildcard(const char *str, const char *pattern);
- inline bool strbegin (const char *str, const char *key);
- inline bool stribegin(const char *str, const char *key);
- inline bool strend (const char *str, const char *key);
- inline bool striend(const char *str, const char *key);
+ inline bool strbegin(const char *str, const char *key);
+ inline bool istrbegin(const char *str, const char *key);
+ inline bool strend(const char *str, const char *key);
+ inline bool istrend(const char *str, const char *key);
//convert.hpp
inline char* strlower(char *str);
inline char* strupper(char *str);
+ inline char* qstrlower(char *str);
+ inline char* qstrupper(char *str);
inline char* strtr(char *dest, const char *before, const char *after);
- inline uintmax_t hex (const char *str);
- inline intmax_t integer(const char *str);
+ inline uintmax_t hex(const char *str);
+ inline intmax_t integer(const char *str);
inline uintmax_t decimal(const char *str);
- inline uintmax_t binary (const char *str);
- inline double fp (const char *str);
+ inline uintmax_t binary(const char *str);
+ inline double fp(const char *str);
//math.hpp
- inline bool strint (const char *str, int &result);
+ inline bool strint(const char *str, int &result);
inline bool strmath(const char *str, int &result);
//platform.hpp
@@ -132,26 +148,31 @@ namespace nall {
//strpos.hpp
inline optional strpos(const char *str, const char *key);
+ inline optional istrpos(const char *str, const char *key);
inline optional qstrpos(const char *str, const char *key);
+ inline optional iqstrpos(const char *str, const char *key);
+ template inline optional ustrpos(const char *str, const char *key);
//trim.hpp
template inline char* ltrim(char *str, const char *key = " ");
template inline char* rtrim(char *str, const char *key = " ");
- template inline char* trim (char *str, const char *key = " ");
+ template inline char* trim(char *str, const char *key = " ", const char *rkey = 0);
//utility.hpp
+ template alwaysinline bool chrequal(char x, char y);
+ template alwaysinline bool quoteskip(T *&p);
+ template alwaysinline bool quotecopy(char *&t, T *&p);
inline unsigned strlcpy(string &dest, const char *src, unsigned length);
inline unsigned strlcat(string &dest, const char *src, unsigned length);
- inline string substr(const char *src, unsigned start = 0, unsigned length = 0);
+ inline string substr(const char *src, unsigned start = 0, unsigned length = ~0u);
+ inline string sha256(const uint8_t *data, unsigned size);
- inline string integer(intmax_t value);
- template inline string linteger(intmax_t value);
- template inline string rinteger(intmax_t value);
- inline string decimal(uintmax_t value);
- template inline string ldecimal(uintmax_t value);
- template inline string rdecimal(uintmax_t value);
- template inline string hex(uintmax_t value);
- template inline string binary(uintmax_t value);
+ template inline string integer(intmax_t value);
+ template inline string linteger(intmax_t value);
+ template inline string decimal(uintmax_t value);
+ template inline string ldecimal(uintmax_t value);
+ template inline string hex(uintmax_t value);
+ template inline string binary(uintmax_t value);
inline unsigned fp(char *str, double value);
inline string fp(double value);
diff --git a/snespurify/nall/string/cast.hpp b/snespurify/nall/string/cast.hpp
index 14f005da..2d010bfa 100755
--- a/snespurify/nall/string/cast.hpp
+++ b/snespurify/nall/string/cast.hpp
@@ -6,16 +6,15 @@ namespace nall {
//this is needed, as C++0x does not support explicit template specialization inside classes
template<> inline const char* to_string (bool v) { return v ? "true" : "false"; }
template<> inline const char* to_string (signed int v) { static char temp[256]; snprintf(temp, 255, "%+d", v); return temp; }
-template<> inline const char* to_string (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
-template<> inline const char* to_string (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
+template<> inline const char* to_string (unsigned int v) { static char temp[256]; snprintf(temp, 255, "%u", v); return temp; }
+template<> inline const char* to_string (intmax_t v) { static char temp[256]; snprintf(temp, 255, "%+lld", (long long)v); return temp; }
+template<> inline const char* to_string (uintmax_t v) { static char temp[256]; snprintf(temp, 255, "%llu", (unsigned long long)v); return temp; }
+template<> inline const char* to_string (double v) { static char temp[256]; snprintf(temp, 255, "%f", v); return temp; }
template<> inline const char* to_string (char *v) { return v; }
template<> inline const char* to_string (const char *v) { return v; }
template<> inline const char* to_string (string v) { return v; }
template<> inline const char* to_string(const string &v) { return v; }
-template string& string::operator= (T value) { return assign(to_string(value)); }
-template string& string::operator<<(T value) { return append(to_string(value)); }
-
template lstring& lstring::operator<<(T value) {
operator[](size()).assign(to_string(value));
return *this;
diff --git a/snespurify/nall/string/compare.hpp b/snespurify/nall/string/compare.hpp
index bce0895b..ad311d74 100755
--- a/snespurify/nall/string/compare.hpp
+++ b/snespurify/nall/string/compare.hpp
@@ -11,7 +11,7 @@ char chrupper(char c) {
return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
}
-int stricmp(const char *str1, const char *str2) {
+int istrcmp(const char *str1, const char *str2) {
while(*str1) {
if(chrlower(*str1) != chrlower(*str2)) break;
str1++, str2++;
@@ -66,7 +66,7 @@ bool strbegin(const char *str, const char *key) {
return (!memcmp(str, key, ksl));
}
-bool stribegin(const char *str, const char *key) {
+bool istrbegin(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
@@ -89,7 +89,7 @@ bool strend(const char *str, const char *key) {
return (!memcmp(str + ssl - ksl, key, ksl));
}
-bool striend(const char *str, const char *key) {
+bool istrend(const char *str, const char *key) {
int ssl = strlen(str), ksl = strlen(key);
if(ksl > ssl) return false;
diff --git a/snespurify/nall/string/convert.hpp b/snespurify/nall/string/convert.hpp
index 603d2e0e..3dd487f6 100755
--- a/snespurify/nall/string/convert.hpp
+++ b/snespurify/nall/string/convert.hpp
@@ -23,6 +23,26 @@ char* strupper(char *str) {
return str;
}
+char* qstrlower(char *s) {
+ if(!s) return 0;
+ bool quoted = false;
+ while(*s) {
+ if(*s == '\"' || *s == '\'') quoted ^= 1;
+ if(quoted == false && *s >= 'A' && *s <= 'Z') *s += 0x20;
+ s++;
+ }
+}
+
+char* qstrupper(char *s) {
+ if(!s) return 0;
+ bool quoted = false;
+ while(*s) {
+ if(*s == '\"' || *s == '\'') quoted ^= 1;
+ if(quoted == false && *s >= 'a' && *s <= 'z') *s -= 0x20;
+ s++;
+ }
+}
+
char* strtr(char *dest, const char *before, const char *after) {
if(!dest || !before || !after) return dest;
int sl = strlen(dest), bsl = strlen(before), asl = strlen(after);
diff --git a/snespurify/nall/string/math.hpp b/snespurify/nall/string/math.hpp
index ea8b99c8..d4bc9d25 100755
--- a/snespurify/nall/string/math.hpp
+++ b/snespurify/nall/string/math.hpp
@@ -3,6 +3,8 @@
namespace nall {
+static function eval_fallback;
+
static int eval_integer(const char *&s) {
if(!*s) throw "unrecognized_integer";
int value = 0, x = *s, y = *(s + 1);
@@ -58,7 +60,7 @@ static int eval_integer(const char *&s) {
}
static int eval(const char *&s, int depth = 0) {
- while(*s == ' ' || *s == '\t') s++; //trim whitespace
+ while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) throw "unrecognized_token";
int value = 0, x = *s, y = *(s + 1);
@@ -74,10 +76,12 @@ static int eval(const char *&s, int depth = 0) {
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
+ else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
+
else throw "unrecognized_token";
while(true) {
- while(*s == ' ' || *s == '\t') s++; //trim whitespace
+ while(*s == ' ' || *s == '\t') s++; //trim whitespace
if(!*s) break;
x = *s, y = *(s + 1);
diff --git a/snespurify/nall/string/replace.hpp b/snespurify/nall/string/replace.hpp
index db405a9b..7c7a09d4 100755
--- a/snespurify/nall/string/replace.hpp
+++ b/snespurify/nall/string/replace.hpp
@@ -3,100 +3,49 @@
namespace nall {
-string& string::replace(const char *key, const char *token) {
- int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
- unsigned int replace_count = 0, size = ssl;
- char *buffer;
+template
+string& string::ureplace(const char *key, const char *token) {
+ if(!key || !*key) return *this;
+ enum : unsigned { limit = Limit ? Limit : ~0u };
- if(ksl <= ssl) {
- if(tsl > ksl) { //the new string may be longer than the old string...
- for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need...
- if(!memcmp(data + i, key, ksl)) {
- replace_count++;
- i += ksl;
- } else i++;
- }
- size = ssl + ((tsl - ksl) * replace_count);
- reserve(size);
+ const char *p = data;
+ unsigned counter = 0, keyLength = 0;
+
+ while(*p) {
+ if(quoteskip(p)) continue;
+ for(unsigned n = 0;; n++) {
+ if(key[n] == 0) { counter++; p += n; keyLength = n; break; }
+ if(!chrequal(key[n], p[n])) { p++; break; }
}
-
- buffer = new char[size + 1];
- for(i = z = 0; i < ssl;) {
- if(i <= ssl - ksl) {
- if(!memcmp(data + i, key, ksl)) {
- memcpy(buffer + z, token, tsl);
- z += tsl;
- i += ksl;
- } else buffer[z++] = data[i++];
- } else buffer[z++] = data[i++];
- }
- buffer[z] = 0;
-
- assign(buffer);
- delete[] buffer;
}
+ if(counter == 0) return *this;
+ if(Limit) counter = min(counter, Limit);
+
+ char *t = data, *base;
+ unsigned tokenLength = strlen(token);
+ if(tokenLength > keyLength) {
+ t = base = strdup(data);
+ reserve((unsigned)(p - data) + ((tokenLength - keyLength) * counter));
+ }
+ char *o = data;
+
+ while(*t && counter) {
+ if(quotecopy(o, t)) continue;
+ for(unsigned n = 0;; n++) {
+ if(key[n] == 0) { counter--; memcpy(o, token, tokenLength); t += keyLength; o += tokenLength; break; }
+ if(!chrequal(key[n], t[n])) { *o++ = *t++; break; }
+ }
+ }
+ do *o++ = *t; while(*t++);
+ if(tokenLength > keyLength) free(base);
return *this;
}
-string& string::qreplace(const char *key, const char *token) {
- int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length();
- unsigned int replace_count = 0, size = ssl;
- uint8_t x;
- char *buffer;
-
- if(ksl <= ssl) {
- if(tsl > ksl) {
- for(i = 0; i <= ssl - ksl;) {
- x = data[i];
- if(x == '\"' || x == '\'') {
- l = i;
- i++;
- while(data[i++] != x) {
- if(i == ssl) {
- i = l;
- break;
- }
- }
- }
- if(!memcmp(data + i, key, ksl)) {
- replace_count++;
- i += ksl;
- } else i++;
- }
- size = ssl + ((tsl - ksl) * replace_count);
- reserve(size);
- }
-
- buffer = new char[size + 1];
- for(i = z = 0; i < ssl;) {
- x = data[i];
- if(x == '\"' || x == '\'') {
- l = i++;
- while(data[i] != x && i < ssl)i++;
- if(i >= ssl)i = l;
- else {
- memcpy(buffer + z, data + l, i - l);
- z += i - l;
- }
- }
- if(i <= ssl - ksl) {
- if(!memcmp(data + i, key, ksl)) {
- memcpy(buffer + z, token, tsl);
- z += tsl;
- i += ksl;
- replace_count++;
- } else buffer[z++] = data[i++];
- } else buffer[z++] = data[i++];
- }
- buffer[z] = 0;
-
- assign(buffer);
- delete[] buffer;
- }
-
- return *this;
-}
+template string &string::replace(const char *key, const char *token) { return ureplace(key, token); }
+template string &string::ireplace(const char *key, const char *token) { return ureplace(key, token); }
+template string &string::qreplace(const char *key, const char *token) { return ureplace(key, token); }
+template string &string::iqreplace(const char *key, const char *token) { return ureplace(key, token); }
};
diff --git a/snespurify/nall/string/split.hpp b/snespurify/nall/string/split.hpp
index 8d3ca877..1644401b 100755
--- a/snespurify/nall/string/split.hpp
+++ b/snespurify/nall/string/split.hpp
@@ -3,56 +3,36 @@
namespace nall {
-template void lstring::split(const char *key, const char *src) {
- unsigned limit = Limit;
+template lstring& lstring::usplit(const char *key, const char *base) {
reset();
+ if(!key || !*key) return *this;
- int ssl = strlen(src), ksl = strlen(key);
- int lp = 0, split_count = 0;
+ const char *p = base;
+ unsigned counter = 0;
- for(int i = 0; i <= ssl - ksl;) {
- if(!memcmp(src + i, key, ksl)) {
- strlcpy(operator[](split_count++), src + lp, i - lp + 1);
- i += ksl;
- lp = i;
- if(!--limit) break;
- } else i++;
- }
-
- operator[](split_count++) = src + lp;
-}
-
-template void lstring::qsplit(const char *key, const char *src) {
- unsigned limit = Limit;
- reset();
-
- int ssl = strlen(src), ksl = strlen(key);
- int lp = 0, split_count = 0;
-
- for(int i = 0; i <= ssl - ksl;) {
- uint8_t x = src[i];
-
- if(x == '\"' || x == '\'') {
- int z = i++; //skip opening quote
- while(i < ssl && src[i] != x) i++;
- if(i >= ssl) i = z; //failed match, rewind i
- else {
- i++; //skip closing quote
- continue; //restart in case next char is also a quote
+ while(*p) {
+ if(Limit) if(counter >= Limit) break;
+ if(quoteskip(p)) continue;
+ for(unsigned n = 0;; n++) {
+ if(key[n] == 0) {
+ strlcpy(operator[](counter++), base, (unsigned)(p - base + 1));
+ p += n;
+ base = p;
+ break;
}
+ if(!chrequal(key[n], p[n])) { p++; break; }
}
-
- if(!memcmp(src + i, key, ksl)) {
- strlcpy(operator[](split_count++), src + lp, i - lp + 1);
- i += ksl;
- lp = i;
- if(!--limit) break;
- } else i++;
}
- operator[](split_count++) = src + lp;
+ operator[](counter) = base;
+ return *this;
}
+template lstring& lstring::split(const char *key, const char *src) { return usplit(key, src); }
+template lstring& lstring::isplit(const char *key, const char *src) { return usplit(key, src); }
+template lstring& lstring::qsplit(const char *key, const char *src) { return usplit(key, src); }
+template lstring& lstring::iqsplit(const char *key, const char *src) { return usplit(key, src); }
+
};
#endif
diff --git a/snespurify/nall/string/strpos.hpp b/snespurify/nall/string/strpos.hpp
index 1907a2f3..3b28923e 100755
--- a/snespurify/nall/string/strpos.hpp
+++ b/snespurify/nall/string/strpos.hpp
@@ -2,40 +2,33 @@
#define NALL_STRING_STRPOS_HPP
//usage example:
-//if(auto pos = strpos(str, key)) print(pos(), "\n");
-//prints position of key within str, only if it is found
+//if(auto position = strpos(str, key)) print(position(), "\n");
+//prints position of key within str; but only if it is found
namespace nall {
-optional strpos(const char *str, const char *key) {
- unsigned ssl = strlen(str), ksl = strlen(key);
- if(ksl > ssl) return { false, 0 };
+template
+optional ustrpos(const char *str, const char *key) {
+ const char *base = str;
- for(unsigned i = 0; i <= ssl - ksl; i++) {
- if(!memcmp(str + i, key, ksl)) return { true, i };
- }
-
- return { false, 0 };
-}
-
-optional qstrpos(const char *str, const char *key) {
- unsigned ssl = strlen(str), ksl = strlen(key);
- if(ksl > ssl) return { false, 0 };
-
- for(unsigned i = 0; i <= ssl - ksl;) {
- uint8_t x = str[i];
- if(x == '\"' || x == '\'') {
- uint8_t z = i++;
- while(str[i] != x && i < ssl) i++;
- if(i >= ssl) i = z;
+ while(*str) {
+ if(quoteskip(str)) continue;
+ for(unsigned n = 0;; n++) {
+ if(key[n] == 0) return { true, (unsigned)(str - base) };
+ if(str[n] == 0) return { false, 0 };
+ if(!chrequal(str[n], key[n])) break;
}
- if(!memcmp(str + i, key, ksl)) return { true, i };
- i++;
+ str++;
}
return { false, 0 };
}
+optional strpos(const char *str, const char *key) { return ustrpos(str, key); }
+optional istrpos(const char *str, const char *key) { return ustrpos(str, key); }
+optional qstrpos(const char *str, const char *key) { return ustrpos(str, key); }
+optional iqstrpos(const char *str, const char *key) { return ustrpos(str, key); }
+
}
#endif
diff --git a/snespurify/nall/string/trim.hpp b/snespurify/nall/string/trim.hpp
index f5355d7d..d1f15ee1 100755
--- a/snespurify/nall/string/trim.hpp
+++ b/snespurify/nall/string/trim.hpp
@@ -29,7 +29,8 @@ template char* rtrim(char *str, const char *key) {
return str;
}
-template char* trim(char *str, const char *key) {
+template char* trim(char *str, const char *key, const char *rkey) {
+ if(rkey) return ltrim(rtrim(str, rkey), key);
return ltrim(rtrim(str, key), key);
}
diff --git a/snespurify/nall/string/utility.hpp b/snespurify/nall/string/utility.hpp
index 8e6c1005..13faaf64 100755
--- a/snespurify/nall/string/utility.hpp
+++ b/snespurify/nall/string/utility.hpp
@@ -3,6 +3,38 @@
namespace nall {
+template
+bool chrequal(char x, char y) {
+ if(Insensitive) return chrlower(x) == chrlower(y);
+ return x == y;
+}
+
+template
+bool quoteskip(T *&p) {
+ if(Quoted == false) return false;
+ if(*p != '\'' && *p != '\"') return false;
+
+ while(*p == '\'' || *p == '\"') {
+ char x = *p++;
+ while(*p && *p++ != x);
+ }
+ return true;
+}
+
+template
+bool quotecopy(char *&t, T *&p) {
+ if(Quoted == false) return false;
+ if(*p != '\'' && *p != '\"') return false;
+
+ while(*p == '\'' || *p == '\"') {
+ char x = *p++;
+ *t++ = x;
+ while(*p && *p != x) *t++ = *p++;
+ *t++ = *p++;
+ }
+ return true;
+}
+
unsigned strlcpy(string &dest, const char *src, unsigned length) {
dest.reserve(length);
return strlcpy(dest(), src, length);
@@ -15,7 +47,7 @@ unsigned strlcat(string &dest, const char *src, unsigned length) {
string substr(const char *src, unsigned start, unsigned length) {
string dest;
- if(length == 0) {
+ if(length == ~0u) {
//copy entire string
dest = src + start;
} else {
@@ -25,35 +57,21 @@ string substr(const char *src, unsigned start, unsigned length) {
return dest;
}
+string sha256(const uint8_t *data, unsigned size) {
+ sha256_ctx sha;
+ uint8_t hash[32];
+ sha256_init(&sha);
+ sha256_chunk(&sha, data, size);
+ sha256_final(&sha);
+ sha256_hash(&sha, hash);
+ string result;
+ foreach(byte, hash) result.append(hex<2>(byte));
+ return result;
+}
+
/* arithmetic <> string */
-string integer(intmax_t value) {
- bool negative = value < 0;
- if(negative) value = abs(value);
-
- char buffer[64];
- unsigned size = 0;
-
- do {
- unsigned n = value % 10;
- buffer[size++] = '0' + n;
- value /= 10;
- } while(value);
- buffer[size++] = negative ? '-' : '+';
- buffer[size] = 0;
-
- char result[size + 1];
- memset(result, '0', size);
- result[size] = 0;
-
- for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
- result[x] = buffer[y];
- }
-
- return (const char*)result;
-}
-
-template string linteger(intmax_t value) {
+template string integer(intmax_t value) {
bool negative = value < 0;
if(negative) value = abs(value);
@@ -70,34 +88,7 @@ template string linteger(intmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
- memset(result, ' ', length);
- result[length] = 0;
-
- for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
- result[x] = buffer[y];
- }
-
- return (const char*)result;
-}
-
-template string rinteger(intmax_t value) {
- bool negative = value < 0;
- if(negative) value = abs(value);
-
- char buffer[64];
- unsigned size = 0;
-
- do {
- unsigned n = value % 10;
- buffer[size++] = '0' + n;
- value /= 10;
- } while(value);
- buffer[size++] = negative ? '-' : '+';
- buffer[size] = 0;
-
- unsigned length = (length_ == 0 ? size : length_);
- char result[length + 1];
- memset(result, ' ', length);
+ memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -107,29 +98,10 @@ template string rinteger(intmax_t value) {
return (const char*)result;
}
-string decimal(uintmax_t value) {
- char buffer[64];
- unsigned size = 0;
-
- do {
- unsigned n = value % 10;
- buffer[size++] = '0' + n;
- value /= 10;
- } while(value);
- buffer[size] = 0;
-
- char result[size + 1];
- memset(result, '0', size);
- result[size] = 0;
-
- for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) {
- result[x] = buffer[y];
- }
-
- return (const char*)result;
-}
-
-template string ldecimal(uintmax_t value) {
+template string linteger(intmax_t value) {
+ bool negative = value < 0;
+ if(negative) value = abs(value);
+
char buffer[64];
unsigned size = 0;
@@ -138,11 +110,12 @@ template string ldecimal(uintmax_t value) {
buffer[size++] = '0' + n;
value /= 10;
} while(value);
+ buffer[size++] = negative ? '-' : '+';
buffer[size] = 0;
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
- memset(result, ' ', length);
+ memset(result, padding, length);
result[length] = 0;
for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
@@ -152,7 +125,7 @@ template string ldecimal(uintmax_t value) {
return (const char*)result;
}
-template string rdecimal(uintmax_t value) {
+template string decimal(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -165,7 +138,7 @@ template string rdecimal(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
- memset(result, ' ', length);
+ memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -175,7 +148,30 @@ template string rdecimal(uintmax_t value) {
return (const char*)result;
}
-template string hex(uintmax_t value) {
+template string ldecimal(uintmax_t value) {
+ char buffer[64];
+ unsigned size = 0;
+
+ do {
+ unsigned n = value % 10;
+ buffer[size++] = '0' + n;
+ value /= 10;
+ } while(value);
+ buffer[size] = 0;
+
+ unsigned length = (length_ == 0 ? size : length_);
+ char result[length + 1];
+ memset(result, padding, length);
+ result[length] = 0;
+
+ for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) {
+ result[x] = buffer[y];
+ }
+
+ return (const char*)result;
+}
+
+template string hex(uintmax_t value) {
char buffer[64];
unsigned size = 0;
@@ -187,7 +183,7 @@ template string hex(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
- memset(result, '0', length);
+ memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
@@ -197,7 +193,7 @@ template string hex(uintmax_t value) {
return (const char*)result;
}
-template string binary(uintmax_t value) {
+template string binary(uintmax_t value) {
char buffer[256];
unsigned size = 0;
@@ -209,7 +205,7 @@ template string binary(uintmax_t value) {
unsigned length = (length_ == 0 ? size : length_);
char result[length + 1];
- memset(result, '0', length);
+ memset(result, padding, length);
result[length] = 0;
for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) {
diff --git a/snespurify/nall/string/wrapper.hpp b/snespurify/nall/string/wrapper.hpp
index eadf0a10..a28c1ced 100755
--- a/snespurify/nall/string/wrapper.hpp
+++ b/snespurify/nall/string/wrapper.hpp
@@ -6,27 +6,31 @@ namespace nall {
unsigned string::length() const { return strlen(data); }
bool string::equals(const char *str) const { return !strcmp(data, str); }
-bool string::iequals(const char *str) const { return !stricmp(data, str); }
+bool string::iequals(const char *str) const { return !istrcmp(data, str); }
bool string::wildcard(const char *str) const { return nall::wildcard(data, str); }
bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); }
bool string::beginswith(const char *str) const { return strbegin(data, str); }
-bool string::ibeginswith(const char *str) const { return stribegin(data, str); }
+bool string::ibeginswith(const char *str) const { return istrbegin(data, str); }
bool string::endswith(const char *str) const { return strend(data, str); }
-bool string::iendswith(const char *str) const { return striend(data, str); }
+bool string::iendswith(const char *str) const { return istrend(data, str); }
string& string::lower() { nall::strlower(data); return *this; }
string& string::upper() { nall::strupper(data); return *this; }
+string& string::qlower() { nall::qstrlower(data); return *this; }
+string& string::qupper() { nall::qstrupper(data); return *this; }
string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; }
template string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; }
template string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; }
-template string& string::trim (const char *key) { nall::trim (data, key); return *this; }
+template string& string::trim(const char *key, const char *rkey) { nall::trim (data, key, rkey); return *this; }
optional string::position(const char *key) const { return strpos(data, key); }
+optional string::iposition(const char *key) const { return istrpos(data, key); }
optional string::qposition(const char *key) const { return qstrpos(data, key); }
+optional string::iqposition(const char *key) const { return iqstrpos(data, key); }
}
diff --git a/snespurify/nall/string/xml.hpp b/snespurify/nall/string/xml.hpp
index 185a89f9..47653786 100755
--- a/snespurify/nall/string/xml.hpp
+++ b/snespurify/nall/string/xml.hpp
@@ -77,7 +77,7 @@ inline string xml_element::parse() const {
if(auto pos = strpos(source, "]]>")) {
if(pos() - 9 > 0) {
string cdata = substr(source, 9, pos() - 9);
- data << cdata;
+ data.append(cdata);
offset += strlen(cdata);
}
source += 9 + offset + 3;
diff --git a/snespurify/nall/utility.hpp b/snespurify/nall/utility.hpp
index 60bda562..1f5699e5 100755
--- a/snespurify/nall/utility.hpp
+++ b/snespurify/nall/utility.hpp
@@ -26,6 +26,7 @@ namespace nall {
public:
inline operator bool() const { return valid; }
inline const T& operator()() const { if(!valid) throw; return value; }
+ inline optional& operator=(const optional &source) { valid = source.valid; value = source.value; return *this; }
inline optional(bool valid, const T &value) : valid(valid), value(value) {}
};
diff --git a/snespurify/nall/vector.hpp b/snespurify/nall/vector.hpp
index c6ef24f2..f98eb375 100755
--- a/snespurify/nall/vector.hpp
+++ b/snespurify/nall/vector.hpp
@@ -94,14 +94,15 @@ namespace nall {
else resize(objectsize - count);
}
- inline T& operator[](unsigned index) {
- if(index >= objectsize) resize(index + 1);
- return pool[index];
+ linear_vector() : pool(0), poolsize(0), objectsize(0) {
}
- inline const T& operator[](unsigned index) const {
- if(index >= objectsize) throw "vector[] out of bounds";
- return pool[index];
+ linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) {
+ for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
+ }
+
+ ~linear_vector() {
+ reset();
}
//copy
@@ -132,17 +133,22 @@ namespace nall {
operator=(std::move(source));
}
- //construction
- linear_vector() : pool(0), poolsize(0), objectsize(0) {
+ //index
+ inline T& operator[](unsigned index) {
+ if(index >= objectsize) resize(index + 1);
+ return pool[index];
}
- linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) {
- for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
+ inline const T& operator[](unsigned index) const {
+ if(index >= objectsize) throw "vector[] out of bounds";
+ return pool[index];
}
- ~linear_vector() {
- reset();
- }
+ //iteration
+ T* begin() { return &pool[0]; }
+ T* end() { return &pool[objectsize]; }
+ const T* begin() const { return &pool[0]; }
+ const T* end() const { return &pool[objectsize]; }
};
//pointer_vector
@@ -222,15 +228,15 @@ namespace nall {
else resize(objectsize - count);
}
- inline T& operator[](unsigned index) {
- if(index >= objectsize) resize(index + 1);
- if(!pool[index]) pool[index] = new T;
- return *pool[index];
+ pointer_vector() : pool(0), poolsize(0), objectsize(0) {
}
- inline const T& operator[](unsigned index) const {
- if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
- return *pool[index];
+ pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) {
+ for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
+ }
+
+ ~pointer_vector() {
+ reset();
}
//copy
@@ -261,17 +267,31 @@ namespace nall {
operator=(std::move(source));
}
- //construction
- pointer_vector() : pool(0), poolsize(0), objectsize(0) {
+ //index
+ inline T& operator[](unsigned index) {
+ if(index >= objectsize) resize(index + 1);
+ if(!pool[index]) pool[index] = new T;
+ return *pool[index];
}
- pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) {
- for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
+ inline const T& operator[](unsigned index) const {
+ if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
+ return *pool[index];
}
- ~pointer_vector() {
- reset();
- }
+ //iteration
+ struct iterator {
+ bool operator!=(const iterator &source) const { return index != source.index; }
+ T& operator*() { return vector.operator[](index); }
+ iterator& operator++() { index++; return *this; }
+ iterator(pointer_vector &vector, unsigned index) : vector(vector), index(index) {}
+ private:
+ pointer_vector &vector;
+ unsigned index;
+ };
+
+ iterator begin() { return iterator(*this, 0); }
+ iterator end() { return iterator(*this, objectsize); }
};
template struct has_size> { enum { value = true }; };
diff --git a/snespurify/nall/windows/detour.hpp b/snespurify/nall/windows/detour.hpp
new file mode 100755
index 00000000..e270f318
--- /dev/null
+++ b/snespurify/nall/windows/detour.hpp
@@ -0,0 +1,192 @@
+#ifndef NALL_WINDOWS_DETOUR_HPP
+#define NALL_WINDOWS_DETOUR_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+#define Copy 0
+#define RelNear 1
+
+struct detour {
+ static bool insert(const string &moduleName, const string &functionName, void *&source, void *target);
+ static bool remove(const string &moduleName, const string &functionName, void *&source);
+
+protected:
+ static unsigned length(const uint8_t *function);
+ static unsigned mirror(uint8_t *target, const uint8_t *source);
+
+ struct opcode {
+ uint16_t prefix;
+ unsigned length;
+ unsigned mode;
+ uint16_t modify;
+ };
+ static opcode opcodes[];
+};
+
+//TODO:
+//* fs:, gs: should force another opcode copy
+//* conditional branches within +5-byte range should fail
+detour::opcode detour::opcodes[] = {
+ { 0x50, 1 }, //push eax
+ { 0x51, 1 }, //push ecx
+ { 0x52, 1 }, //push edx
+ { 0x53, 1 }, //push ebx
+ { 0x54, 1 }, //push esp
+ { 0x55, 1 }, //push ebp
+ { 0x56, 1 }, //push esi
+ { 0x57, 1 }, //push edi
+ { 0x58, 1 }, //pop eax
+ { 0x59, 1 }, //pop ecx
+ { 0x5a, 1 }, //pop edx
+ { 0x5b, 1 }, //pop ebx
+ { 0x5c, 1 }, //pop esp
+ { 0x5d, 1 }, //pop ebp
+ { 0x5e, 1 }, //pop esi
+ { 0x5f, 1 }, //pop edi
+ { 0x64, 1 }, //fs:
+ { 0x65, 1 }, //gs:
+ { 0x68, 5 }, //push dword
+ { 0x6a, 2 }, //push byte
+ { 0x74, 2, RelNear, 0x0f84 }, //je near -> je far
+ { 0x75, 2, RelNear, 0x0f85 }, //jne near -> jne far
+ { 0x89, 2 }, //mov reg,reg
+ { 0x8b, 2 }, //mov reg,reg
+ { 0x90, 1 }, //nop
+ { 0xa1, 5 }, //mov eax,[dword]
+ { 0xeb, 2, RelNear, 0xe9 }, //jmp near -> jmp far
+};
+
+bool detour::insert(const string &moduleName, const string &functionName, void *&source, void *target) {
+ HMODULE module = GetModuleHandleW(utf16_t(moduleName));
+ if(!module) return false;
+
+ uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
+ if(!sourceData) return false;
+
+ unsigned sourceLength = detour::length(sourceData);
+ if(sourceLength < 5) {
+ //unable to clone enough bytes to insert hook
+ #if 1
+ string output = { "detour::insert(", moduleName, "::", functionName, ") failed: " };
+ for(unsigned n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " ");
+ output.rtrim<1>(" ");
+ MessageBoxA(0, output, "nall::detour", MB_OK);
+ #endif
+ return false;
+ }
+
+ uint8_t *mirrorData = new uint8_t[512]();
+ detour::mirror(mirrorData, sourceData);
+
+ DWORD privileges;
+ VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges);
+ VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
+ uintmax_t address = (uintmax_t)target - ((uintmax_t)sourceData + 5);
+ sourceData[0] = 0xe9; //jmp target
+ sourceData[1] = address >> 0;
+ sourceData[2] = address >> 8;
+ sourceData[3] = address >> 16;
+ sourceData[4] = address >> 24;
+ VirtualProtect((void*)sourceData, 256, privileges, &privileges);
+
+ source = (void*)mirrorData;
+ return true;
+}
+
+bool detour::remove(const string &moduleName, const string &functionName, void *&source) {
+ HMODULE module = GetModuleHandleW(utf16_t(moduleName));
+ if(!module) return false;
+
+ uint8_t *sourceData = (uint8_t*)GetProcAddress(module, functionName);
+ if(!sourceData) return false;
+
+ uint8_t *mirrorData = (uint8_t*)source;
+ if(mirrorData == sourceData) return false; //hook was never installed
+
+ unsigned length = detour::length(256 + mirrorData);
+ if(length < 5) return false;
+
+ DWORD privileges;
+ VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
+ for(unsigned n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n];
+ VirtualProtect((void*)sourceData, 256, privileges, &privileges);
+
+ source = (void*)sourceData;
+ delete[] mirrorData;
+ return true;
+}
+
+unsigned detour::length(const uint8_t *function) {
+ unsigned length = 0;
+ while(length < 5) {
+ detour::opcode *opcode = 0;
+ foreach(op, detour::opcodes) {
+ if(function[length] == op.prefix) {
+ opcode = &op;
+ break;
+ }
+ }
+ if(opcode == 0) break;
+ length += opcode->length;
+ }
+ return length;
+}
+
+unsigned detour::mirror(uint8_t *target, const uint8_t *source) {
+ const uint8_t *entryPoint = source;
+ for(unsigned n = 0; n < 256; n++) target[256 + n] = source[n];
+
+ unsigned size = detour::length(source);
+ while(size) {
+ detour::opcode *opcode = 0;
+ foreach(op, detour::opcodes) {
+ if(*source == op.prefix) {
+ opcode = &op;
+ break;
+ }
+ }
+
+ switch(opcode->mode) {
+ case Copy:
+ for(unsigned n = 0; n < opcode->length; n++) *target++ = *source++;
+ break;
+ case RelNear: {
+ source++;
+ uintmax_t sourceAddress = (uintmax_t)source + 1 + (int8_t)*source;
+ *target++ = opcode->modify;
+ if(opcode->modify >> 8) *target++ = opcode->modify >> 8;
+ uintmax_t targetAddress = (uintmax_t)target + 4;
+ uintmax_t address = sourceAddress - targetAddress;
+ *target++ = address >> 0;
+ *target++ = address >> 8;
+ *target++ = address >> 16;
+ *target++ = address >> 24;
+ source += 2;
+ } break;
+ }
+
+ size -= opcode->length;
+ }
+
+ uintmax_t address = (entryPoint + detour::length(entryPoint)) - (target + 5);
+ *target++ = 0xe9; //jmp entryPoint
+ *target++ = address >> 0;
+ *target++ = address >> 8;
+ *target++ = address >> 16;
+ *target++ = address >> 24;
+
+ return source - entryPoint;
+}
+
+#undef Implied
+#undef RelNear
+
+}
+
+#endif
diff --git a/snespurify/nall/windows/launcher.hpp b/snespurify/nall/windows/launcher.hpp
new file mode 100755
index 00000000..914683ec
--- /dev/null
+++ b/snespurify/nall/windows/launcher.hpp
@@ -0,0 +1,94 @@
+#ifndef NALL_WINDOWS_LAUNCHER_HPP
+#define NALL_WINDOWS_LAUNCHER_HPP
+
+namespace nall {
+
+//launch a new process and inject specified DLL into it
+
+bool launch(const char *applicationName, const char *libraryName, uint32_t entryPoint) {
+ //if a launcher does not send at least one message, a wait cursor will appear
+ PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
+ MSG msg;
+ GetMessage(&msg, 0, 0, 0);
+
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+
+ memset(&si, 0, sizeof(STARTUPINFOW));
+ BOOL result = CreateProcessW(
+ utf16_t(applicationName), GetCommandLineW(), NULL, NULL, TRUE,
+ DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, //do not break if application creates its own processes
+ NULL, NULL, &si, &pi
+ );
+ if(result == false) return false;
+
+ uint8_t entryData[1024], entryHook[1024] = {
+ 0x68, 0x00, 0x00, 0x00, 0x00, //push libraryName
+ 0xb8, 0x00, 0x00, 0x00, 0x00, //mov eax,LoadLibraryW
+ 0xff, 0xd0, //call eax
+ 0xcd, 0x03, //int 3
+ };
+
+ entryHook[1] = (uint8_t)((entryPoint + 14) >> 0);
+ entryHook[2] = (uint8_t)((entryPoint + 14) >> 8);
+ entryHook[3] = (uint8_t)((entryPoint + 14) >> 16);
+ entryHook[4] = (uint8_t)((entryPoint + 14) >> 24);
+
+ uint32_t pLoadLibraryW = (uint32_t)GetProcAddress(GetModuleHandleW(L"kernel32"), "LoadLibraryW");
+ entryHook[6] = pLoadLibraryW >> 0;
+ entryHook[7] = pLoadLibraryW >> 8;
+ entryHook[8] = pLoadLibraryW >> 16;
+ entryHook[9] = pLoadLibraryW >> 24;
+
+ utf16_t buffer = utf16_t(libraryName);
+ memcpy(entryHook + 14, buffer, 2 * wcslen(buffer) + 2);
+
+ while(true) {
+ DEBUG_EVENT event;
+ WaitForDebugEvent(&event, INFINITE);
+
+ if(event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
+
+ if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
+ if(event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) {
+ if(event.u.Exception.ExceptionRecord.ExceptionAddress == (void*)(entryPoint + 14 - 1)) {
+ HANDLE hProcess = OpenProcess(0, FALSE, event.dwProcessId);
+ HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, event.dwThreadId);
+
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_FULL;
+ GetThreadContext(hThread, &context);
+
+ WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL);
+ context.Eip = entryPoint;
+ SetThreadContext(hThread, &context);
+
+ CloseHandle(hThread);
+ CloseHandle(hProcess);
+ }
+
+ ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
+ continue;
+ }
+
+ ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
+ continue;
+ }
+
+ if(event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
+ ReadProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL);
+ WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryHook, sizeof entryHook, NULL);
+
+ ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
+ continue;
+ }
+
+ ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE);
+ }
+
+ return true;
+}
+
+}
+
+#endif
diff --git a/snespurify/nall/utf8.hpp b/snespurify/nall/windows/utf8.hpp
similarity index 100%
rename from snespurify/nall/utf8.hpp
rename to snespurify/nall/windows/utf8.hpp
diff --git a/snespurify/nall/zip.hpp b/snespurify/nall/zip.hpp
new file mode 100755
index 00000000..ad9c7506
--- /dev/null
+++ b/snespurify/nall/zip.hpp
@@ -0,0 +1,124 @@
+#ifndef NALL_UNZIP_HPP
+#define NALL_UNZIP_HPP
+
+#include
+#include
+#include
+#include
+
+namespace nall {
+
+struct zip {
+ struct File {
+ string name;
+ const uint8_t *data;
+ unsigned size;
+ unsigned csize;
+ unsigned cmode; //0 = uncompressed, 8 = deflate
+ unsigned crc32;
+ };
+
+ inline bool open(const string &filename) {
+ close();
+ if(fm.open(filename, filemap::mode::read) == false) return false;
+ if(open(fm.data(), fm.size()) == false) {
+ fm.close();
+ return false;
+ }
+ return true;
+ }
+
+ inline bool open(const uint8_t *data, unsigned size) {
+ if(size < 22) return false;
+
+ filedata = data;
+ filesize = size;
+
+ file.reset();
+
+ const uint8_t *footer = data + size - 22;
+ const uint8_t *directory = data + read(footer + 16, 4);
+
+ while(true) {
+ unsigned signature = read(directory + 0, 4);
+ if(signature != 0x02014b50) break;
+
+ File file;
+ file.cmode = read(directory + 10, 2);
+ file.crc32 = read(directory + 16, 4);
+ file.csize = read(directory + 20, 4);
+ file.size = read(directory + 24, 4);
+
+ unsigned namelength = read(directory + 28, 2);
+ unsigned extralength = read(directory + 30, 2);
+ unsigned commentlength = read(directory + 32, 2);
+
+ char *filename = new char[namelength + 1];
+ memcpy(filename, directory + 46, namelength);
+ filename[namelength] = 0;
+ file.name = filename;
+ delete[] filename;
+
+ unsigned offset = read(directory + 42, 4);
+ unsigned offsetNL = read(data + offset + 26, 2);
+ unsigned offsetEL = read(data + offset + 28, 2);
+ file.data = data + offset + 30 + offsetNL + offsetEL;
+
+ directory += 46 + namelength + extralength + commentlength;
+
+ this->file.append(file);
+ }
+
+ return true;
+ }
+
+ inline bool extract(File &file, uint8_t *&data, unsigned &size) {
+ data = 0, size = 0;
+
+ if(file.cmode == 0) {
+ size = file.size;
+ data = new uint8_t[size];
+ memcpy(data, file.data, size);
+ return true;
+ }
+
+ if(file.cmode == 8) {
+ size = file.size;
+ data = new uint8_t[size];
+ if(inflate(data, size, file.data, file.csize) == false) {
+ delete[] data;
+ size = 0;
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ inline void close() {
+ if(fm.open()) fm.close();
+ }
+
+ ~zip() {
+ close();
+ }
+
+protected:
+ filemap fm;
+ const uint8_t *filedata;
+ unsigned filesize;
+
+ unsigned read(const uint8_t *data, unsigned size) {
+ unsigned result = 0, shift = 0;
+ while(size--) { result |= *data++ << shift; shift += 8; }
+ return result;
+ }
+
+public:
+ linear_vector file;
+};
+
+}
+
+#endif
diff --git a/snespurify/phoenix/core/core.cpp b/snespurify/phoenix/core/core.cpp
index ddb868cb..63c152b2 100755
--- a/snespurify/phoenix/core/core.cpp
+++ b/snespurify/phoenix/core/core.cpp
@@ -34,6 +34,10 @@ void Font::setSize(unsigned size) { state.size = size; return p.setSize(size); }
void Font::setUnderline(bool underline) { state.underline = underline; return p.setUnderline(underline); }
Font::Font() : state(*new State), p(*new pFont(*this)) { p.constructor(); }
+void Timer::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
+void Timer::setInterval(unsigned milliseconds) { state.milliseconds = milliseconds; return p.setInterval(milliseconds); }
+Timer::Timer() : state(*new State), p(*new pTimer(*this)) { p.constructor(); }
+
MessageWindow::Response MessageWindow::information(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::information(parent, text, buttons); }
MessageWindow::Response MessageWindow::question(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::question(parent, text, buttons); }
MessageWindow::Response MessageWindow::warning(Window &parent, const string &text, MessageWindow::Buttons buttons) { return pMessageWindow::warning(parent, text, buttons); }
@@ -43,11 +47,12 @@ Window Window::None;
void Window::append(Layout &layout) { state.layout.append(layout); return p.append(layout); }
void Window::append(Menu &menu) { state.menu.append(menu); ((Action&)menu).state.parent = this; return p.append(menu); }
void Window::append(Widget &widget) { state.widget.append(widget); return p.append(widget); }
+Color Window::backgroundColor() { return p.backgroundColor(); }
Geometry Window::frameGeometry() { Geometry geometry = p.geometry(), margin = p.frameMargin(); return { geometry.x - margin.x, geometry.y - margin.y, geometry.width + margin.width, geometry.height + margin.height }; }
Geometry Window::frameMargin() { return p.frameMargin(); }
bool Window::focused() { return p.focused(); }
Geometry Window::geometry() { return p.geometry(); }
-void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { state.backgroundColor = true; state.backgroundColorRed = red; state.backgroundColorGreen = green; state.backgroundColorBlue = blue; return p.setBackgroundColor(red, green, blue); }
+void Window::setBackgroundColor(const Color &color) { state.backgroundColorOverride = true; state.backgroundColor = color; return p.setBackgroundColor(color); }
void Window::setFrameGeometry(const Geometry &geometry) { Geometry margin = p.frameMargin(); return setGeometry({ geometry.x + margin.x, geometry.y + margin.y, geometry.width - margin.width, geometry.height - margin.height }); }
void Window::setFocused() { return p.setFocused(); }
void Window::setFullScreen(bool fullScreen) { state.fullScreen = fullScreen; return p.setFullScreen(fullScreen); }
@@ -89,6 +94,7 @@ RadioItem::RadioItem() : state(*new State), base_from_member(*new p
bool Widget::enabled() { return state.enabled; }
Font& Widget::font() { return p.font(); }
+Geometry Widget::geometry() { return state.geometry; }
Geometry Widget::minimumGeometry() { return p.minimumGeometry(); }
void Widget::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); }
void Widget::setFocused() { return p.setFocused(); }
@@ -124,6 +130,11 @@ void HexEdit::setRows(unsigned rows) { state.rows = rows; return p.setRows(rows)
void HexEdit::update() { return p.update(); }
HexEdit::HexEdit() : state(*new State), base_from_member(*new pHexEdit(*this)), Widget(base_from_member::value), p(base_from_member::value) { p.constructor(); }
+unsigned HorizontalScrollBar::position() { return p.position(); }
+void HorizontalScrollBar::setLength(unsigned length) { state.length = length; return p.setLength(length); }
+void HorizontalScrollBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
+HorizontalScrollBar::HorizontalScrollBar() : state(*new State), base_from_member(*new pHorizontalScrollBar(*this)), Widget(base_from_member::value), p(base_from_member::value) { p.constructor(); }
+
unsigned HorizontalSlider::position() { return p.position(); }
void HorizontalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void HorizontalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
@@ -168,6 +179,11 @@ void TextEdit::setWordWrap(bool wordWrap) { state.wordWrap = wordWrap; return p.
string TextEdit::text() { return p.text(); }
TextEdit::TextEdit() : state(*new State), base_from_member(*new pTextEdit(*this)), Widget(base_from_member::value), p(base_from_member::value) { p.constructor(); }
+unsigned VerticalScrollBar::position() { return p.position(); }
+void VerticalScrollBar::setLength(unsigned length) { state.length = length; return p.setLength(length); }
+void VerticalScrollBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
+VerticalScrollBar::VerticalScrollBar() : state(*new State), base_from_member(*new pVerticalScrollBar(*this)), Widget(base_from_member::value), p(base_from_member::value) { p.constructor(); }
+
unsigned VerticalSlider::position() { return p.position(); }
void VerticalSlider::setLength(unsigned length) { state.length = length; return p.setLength(length); }
void VerticalSlider::setPosition(unsigned position) { state.position = position; return p.setPosition(position); }
diff --git a/snespurify/phoenix/core/core.hpp b/snespurify/phoenix/core/core.hpp
index 2f087b42..c09909ed 100755
--- a/snespurify/phoenix/core/core.hpp
+++ b/snespurify/phoenix/core/core.hpp
@@ -6,6 +6,7 @@ struct Widget;
struct pOS;
struct pFont;
+struct pTimer;
struct pWindow;
struct pAction;
struct pMenu;
@@ -20,6 +21,7 @@ struct pCanvas;
struct pCheckBox;
struct pComboBox;
struct pHexEdit;
+struct pHorizontalScrollBar;
struct pHorizontalSlider;
struct pLabel;
struct pLineEdit;
@@ -27,6 +29,7 @@ struct pListView;
struct pProgressBar;
struct pRadioBox;
struct pTextEdit;
+struct pVerticalScrollBar;
struct pVerticalSlider;
struct pViewport;
@@ -42,6 +45,12 @@ struct Geometry {
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
};
+struct Color {
+ uint8_t red, green, blue, alpha;
+ inline Color() : red(0), green(0), blue(0), alpha(255) {}
+ inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
+};
+
struct Object {
Object();
Object& operator=(const Object&) = delete;
@@ -82,6 +91,18 @@ struct Font : Object {
pFont &p;
};
+struct Timer : Object {
+ nall::function onTimeout;
+
+ void setEnabled(bool enabled = true);
+ void setInterval(unsigned milliseconds);
+
+ Timer();
+ struct State;
+ State &state;
+ pTimer &p;
+};
+
struct MessageWindow : Object {
enum class Buttons : unsigned {
Ok,
@@ -111,11 +132,12 @@ struct Window : Object {
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
+ Color backgroundColor();
Geometry frameGeometry();
Geometry frameMargin();
bool focused();
Geometry geometry();
- void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
+ void setBackgroundColor(const Color &color);
void setFrameGeometry(const Geometry &geometry);
void setFocused();
void setFullScreen(bool fullScreen = true);
@@ -210,6 +232,7 @@ struct Layout : Object {
struct Widget : Object {
bool enabled();
Font& font();
+ Geometry geometry();
Geometry minimumGeometry();
void setEnabled(bool enabled = true);
void setFocused();
@@ -287,6 +310,19 @@ struct HexEdit : private nall::base_from_member, Widget {
pHexEdit &p;
};
+struct HorizontalScrollBar : private nall::base_from_member, Widget {
+ nall::function onChange;
+
+ unsigned position();
+ void setLength(unsigned length);
+ void setPosition(unsigned position);
+
+ HorizontalScrollBar();
+ struct State;
+ State &state;
+ pHorizontalScrollBar &p;
+};
+
struct HorizontalSlider : private nall::base_from_member, Widget {
nall::function onChange;
@@ -393,6 +429,19 @@ struct TextEdit : private nall::base_from_member, Widget {
pTextEdit &p;
};
+struct VerticalScrollBar : private nall::base_from_member, Widget {
+ nall::function onChange;
+
+ unsigned position();
+ void setLength(unsigned length);
+ void setPosition(unsigned position);
+
+ VerticalScrollBar();
+ struct State;
+ State &state;
+ pVerticalScrollBar &p;
+};
+
struct VerticalSlider : private nall::base_from_member, Widget {
nall::function onChange;
diff --git a/snespurify/phoenix/core/state.hpp b/snespurify/phoenix/core/state.hpp
index a4d99bf7..cbe6970c 100755
--- a/snespurify/phoenix/core/state.hpp
+++ b/snespurify/phoenix/core/state.hpp
@@ -13,9 +13,19 @@ struct Font::State {
}
};
+struct Timer::State {
+ bool enabled;
+ unsigned milliseconds;
+
+ State() {
+ enabled = false;
+ milliseconds = 0;
+ }
+};
+
struct Window::State {
- bool backgroundColor;
- unsigned backgroundColorRed, backgroundColorGreen, backgroundColorBlue;
+ bool backgroundColorOverride;
+ Color backgroundColor;
bool fullScreen;
Geometry geometry;
reference_array layout;
@@ -32,10 +42,8 @@ struct Window::State {
Font *widgetFont;
State() {
- backgroundColor = false;
- backgroundColorRed = 0;
- backgroundColorGreen = 0;
- backgroundColorBlue = 0;
+ backgroundColorOverride = false;
+ backgroundColor = { 0, 0, 0, 255 };
fullScreen = false;
geometry = { 128, 128, 256, 256 };
menuFont = 0;
@@ -142,6 +150,16 @@ struct HexEdit::State {
}
};
+struct HorizontalScrollBar::State {
+ unsigned length;
+ unsigned position;
+
+ State() {
+ length = 101;
+ position = 0;
+ }
+};
+
struct HorizontalSlider::State {
unsigned length;
unsigned position;
@@ -213,6 +231,16 @@ struct TextEdit::State {
}
};
+struct VerticalScrollBar::State {
+ unsigned length;
+ unsigned position;
+
+ State() {
+ length = 101;
+ position = 0;
+ }
+};
+
struct VerticalSlider::State {
unsigned length;
unsigned position;
diff --git a/snespurify/phoenix/gtk/font.cpp b/snespurify/phoenix/gtk/font.cpp
index 93bc0303..9f4f840b 100755
--- a/snespurify/phoenix/gtk/font.cpp
+++ b/snespurify/phoenix/gtk/font.cpp
@@ -29,4 +29,6 @@ void pFont::constructor() {
gtkFont = pango_font_description_new();
PangoContext *context = gdk_pango_context_get_for_screen(gdk_screen_get_default());
gtkLayout = pango_layout_new(context);
+ font.setFamily("Sans");
+ font.setSize(8);
}
diff --git a/snespurify/phoenix/gtk/gtk.cpp b/snespurify/phoenix/gtk/gtk.cpp
index c711d7a2..de35e588 100755
--- a/snespurify/phoenix/gtk/gtk.cpp
+++ b/snespurify/phoenix/gtk/gtk.cpp
@@ -2,6 +2,7 @@
#include "settings.cpp"
#include "font.cpp"
+#include "timer.cpp"
#include "message-window.cpp"
#include "window.cpp"
@@ -18,6 +19,7 @@
#include "widget/check-box.cpp"
#include "widget/combo-box.cpp"
#include "widget/hex-edit.cpp"
+#include "widget/horizontal-scroll-bar.cpp"
#include "widget/horizontal-slider.cpp"
#include "widget/label.cpp"
#include "widget/line-edit.cpp"
@@ -25,16 +27,36 @@
#include "widget/progress-bar.cpp"
#include "widget/radio-box.cpp"
#include "widget/text-edit.cpp"
+#include "widget/vertical-scroll-bar.cpp"
#include "widget/vertical-slider.cpp"
#include "widget/viewport.cpp"
Font pOS::defaultFont;
Geometry pOS::availableGeometry() {
- //TODO: is there a GTK+ function for this?
- //should return desktopGeometry() sans panels, toolbars, docks, etc.
- Geometry geometry = desktopGeometry();
- return { geometry.x + 64, geometry.y + 64, geometry.width - 128, geometry.height - 128 };
+ Display *display = XOpenDisplay(0);
+ int screen = DefaultScreen(display);
+
+ static Atom atom = X11None;
+ if(atom == X11None) atom = XInternAtom(display, "_NET_WORKAREA", True);
+
+ int format;
+ unsigned char *data = 0;
+ unsigned long items, after;
+ Atom returnAtom;
+
+ int result = XGetWindowProperty(
+ display, RootWindow(display, screen), atom, 0, 4, False, XA_CARDINAL, &returnAtom, &format, &items, &after, &data
+ );
+
+ XCloseDisplay(display);
+
+ if(result == Success && returnAtom == XA_CARDINAL && format == 32 && items == 4) {
+ unsigned long *workarea = (unsigned long*)data;
+ return { (signed)workarea[0], (signed)workarea[1], (unsigned)workarea[2], (unsigned)workarea[3] };
+ }
+
+ return desktopGeometry();
}
Geometry pOS::desktopGeometry() {
@@ -149,7 +171,7 @@ void pOS::initialize() {
" GtkComboBox::appears-as-list = 1\n"
" GtkTreeView::vertical-separator = 0\n"
"}\n"
- "class \"GtkComboBox\" style \"phoenix-gtk\"\n"
+ //"class \"GtkComboBox\" style \"phoenix-gtk\"\n"
"class \"GtkTreeView\" style \"phoenix-gtk\"\n"
);
}
diff --git a/snespurify/phoenix/gtk/gtk.hpp b/snespurify/phoenix/gtk/gtk.hpp
index f6abacce..bd2f19fd 100755
--- a/snespurify/phoenix/gtk/gtk.hpp
+++ b/snespurify/phoenix/gtk/gtk.hpp
@@ -57,6 +57,16 @@ struct pFont : public pObject {
void constructor();
};
+struct pTimer : public pObject {
+ Timer &timer;
+
+ void setEnabled(bool enabled);
+ void setInterval(unsigned milliseconds);
+
+ pTimer(Timer &timer) : timer(timer) {}
+ void constructor();
+};
+
struct pMessageWindow : public pObject {
static MessageWindow::Response information(Window &parent, const string &text, MessageWindow::Buttons buttons);
static MessageWindow::Response question(Window &parent, const string &text, MessageWindow::Buttons buttons);
@@ -72,14 +82,16 @@ struct pWindow : public pObject {
GtkWidget *statusContainer;
GtkWidget *menu;
GtkWidget *status;
+ GdkEventConfigure lastConfigure;
void append(Layout &layout);
void append(Menu &menu);
void append(Widget &widget);
+ Color backgroundColor();
bool focused();
Geometry frameMargin();
Geometry geometry();
- void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue);
+ void setBackgroundColor(const Color &color);
void setFocused();
void setFullScreen(bool fullScreen);
void setGeometry(const Geometry &geometry);
@@ -192,8 +204,7 @@ struct pButton : public pWidget {
struct pCanvas : public pWidget {
Canvas &canvas;
- uint32_t *bufferRGB;
- uint32_t *bufferBGR;
+ cairo_surface_t *surface;
uint32_t* buffer();
void setGeometry(const Geometry &geometry);
@@ -201,7 +212,6 @@ struct pCanvas : public pWidget {
pCanvas(Canvas &canvas) : pWidget(canvas), canvas(canvas) {}
void constructor();
- void redraw();
};
struct pCheckBox : public pWidget {
@@ -254,6 +264,18 @@ struct pHexEdit : public pWidget {
void updateScroll();
};
+struct pHorizontalScrollBar : public pWidget {
+ HorizontalScrollBar &horizontalScrollBar;
+
+ Geometry minimumGeometry();
+ unsigned position();
+ void setLength(unsigned length);
+ void setPosition(unsigned position);
+
+ pHorizontalScrollBar(HorizontalScrollBar &horizontalScrollBar) : pWidget(horizontalScrollBar), horizontalScrollBar(horizontalScrollBar) {}
+ void constructor();
+};
+
struct pHorizontalSlider : public pWidget {
HorizontalSlider &horizontalSlider;
@@ -358,6 +380,18 @@ struct pTextEdit : public pWidget {
void constructor();
};
+struct pVerticalScrollBar : public pWidget {
+ VerticalScrollBar &verticalScrollBar;
+
+ Geometry minimumGeometry();
+ unsigned position();
+ void setLength(unsigned length);
+ void setPosition(unsigned position);
+
+ pVerticalScrollBar(VerticalScrollBar &verticalScrollBar) : pWidget(verticalScrollBar), verticalScrollBar(verticalScrollBar) {}
+ void constructor();
+};
+
struct pVerticalSlider : public pWidget {
VerticalSlider &verticalSlider;
diff --git a/snespurify/phoenix/gtk/timer.cpp b/snespurify/phoenix/gtk/timer.cpp
new file mode 100755
index 00000000..d04183f8
--- /dev/null
+++ b/snespurify/phoenix/gtk/timer.cpp
@@ -0,0 +1,24 @@
+static guint Timer_trigger(pTimer *self) {
+ //timer may have been disabled prior to triggering, so check state
+ if(self->timer.state.enabled) {
+ if(self->timer.onTimeout) self->timer.onTimeout();
+ }
+ //callback may have disabled timer, so check state again
+ if(self->timer.state.enabled) {
+ g_timeout_add(self->timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)self);
+ }
+ //kill this timer instance (it is spawned above if needed again)
+ return false;
+}
+
+void pTimer::setEnabled(bool enabled) {
+ if(enabled) {
+ g_timeout_add(timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)this);
+ }
+}
+
+void pTimer::setInterval(unsigned milliseconds) {
+}
+
+void pTimer::constructor() {
+}
diff --git a/snespurify/phoenix/gtk/widget/button.cpp b/snespurify/phoenix/gtk/widget/button.cpp
index 360ccedf..f657bb06 100755
--- a/snespurify/phoenix/gtk/widget/button.cpp
+++ b/snespurify/phoenix/gtk/widget/button.cpp
@@ -5,7 +5,7 @@ static void Button_tick(Button *self) {
Geometry pButton::minimumGeometry() {
Font &font = pWidget::font();
Geometry geometry = font.geometry(button.state.text);
- return { 0, 0, geometry.width + 24, geometry.height + 14 };
+ return { 0, 0, geometry.width + 24, geometry.height + 12 };
}
void pButton::setText(const string &text) {
diff --git a/snespurify/phoenix/gtk/widget/canvas.cpp b/snespurify/phoenix/gtk/widget/canvas.cpp
index 2a0d479b..c2261d17 100755
--- a/snespurify/phoenix/gtk/widget/canvas.cpp
+++ b/snespurify/phoenix/gtk/widget/canvas.cpp
@@ -1,17 +1,21 @@
-static void Canvas_expose(pCanvas *self) {
- self->redraw();
+static gboolean Canvas_expose(GtkWidget *widget, GdkEvent *event, pCanvas *self) {
+ cairo_t *context = gdk_cairo_create(gtk_widget_get_window(widget));
+ cairo_set_source_surface(context, self->surface, 0, 0);
+ cairo_paint(context);
+ cairo_destroy(context);
+ return true;
}
uint32_t* pCanvas::buffer() {
- return bufferRGB;
+ return (uint32_t*)cairo_image_surface_get_data(surface);
}
void pCanvas::setGeometry(const Geometry &geometry) {
- delete[] bufferRGB;
- delete[] bufferBGR;
+ if(geometry.width == cairo_image_surface_get_width(surface)
+ && geometry.height == cairo_image_surface_get_height(surface)) return;
- bufferRGB = new uint32_t[geometry.width * geometry.height]();
- bufferBGR = new uint32_t[geometry.width * geometry.height]();
+ cairo_surface_destroy(surface);
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, geometry.width, geometry.height);
pWidget::setGeometry(geometry);
update();
@@ -19,41 +23,16 @@ void pCanvas::setGeometry(const Geometry &geometry) {
void pCanvas::update() {
if(gtk_widget_get_realized(gtkWidget) == false) return;
- GdkRectangle rect;
- rect.x = 0;
- rect.y = 0;
- rect.width = gtkWidget->allocation.width;
- rect.height = gtkWidget->allocation.height;
- gdk_window_invalidate_rect(gtkWidget->window, &rect, true);
+ gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), 0, true);
}
void pCanvas::constructor() {
- bufferRGB = new uint32_t[256 * 256]();
- bufferBGR = new uint32_t[256 * 256]();
-
+ surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 256, 256);
gtkWidget = gtk_drawing_area_new();
GdkColor color;
color.pixel = color.red = color.green = color.blue = 0;
gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, &color);
gtk_widget_set_double_buffered(gtkWidget, false);
gtk_widget_add_events(gtkWidget, GDK_EXPOSURE_MASK);
- g_signal_connect_swapped(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
-}
-
-void pCanvas::redraw() {
- if(gtk_widget_get_realized(gtkWidget) == false) return;
- uint32_t *rgb = bufferRGB, *bgr = bufferBGR;
- for(unsigned y = gtkWidget->allocation.height; y; y--) {
- for(unsigned x = gtkWidget->allocation.width; x; x--) {
- uint32_t pixel = *rgb++;
- *bgr++ = ((pixel << 16) & 0xff0000) | (pixel & 0x00ff00) | ((pixel >> 16) & 0x0000ff);
- }
- }
-
- gdk_draw_rgb_32_image(
- gtkWidget->window,
- gtkWidget->style->fg_gc[GTK_WIDGET_STATE(gtkWidget)],
- 0, 0, gtkWidget->allocation.width, gtkWidget->allocation.height,
- GDK_RGB_DITHER_NONE, (guchar*)bufferBGR, sizeof(uint32_t) * gtkWidget->allocation.width
- );
+ g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this);
}
diff --git a/snespurify/phoenix/gtk/widget/combo-box.cpp b/snespurify/phoenix/gtk/widget/combo-box.cpp
index 0ea1e200..348e95d0 100755
--- a/snespurify/phoenix/gtk/widget/combo-box.cpp
+++ b/snespurify/phoenix/gtk/widget/combo-box.cpp
@@ -13,14 +13,12 @@ Geometry pComboBox::minimumGeometry() {
foreach(item, comboBox.state.text) maximumWidth = max(maximumWidth, font.geometry(item).width);
Geometry geometry = font.geometry(" ");
- return { 0, 0, maximumWidth + 44, geometry.height + 10 };
+ return { 0, 0, maximumWidth + 44, geometry.height + 12 };
}
void pComboBox::reset() {
locked = true;
- for(signed n = itemCounter - 1; n >= 0; n--) {
- gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), n);
- }
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(gtkWidget))));
itemCounter = 0;
locked = false;
}
diff --git a/snespurify/phoenix/gtk/widget/hex-edit.cpp b/snespurify/phoenix/gtk/widget/hex-edit.cpp
index 481ab77d..7aa61a07 100755
--- a/snespurify/phoenix/gtk/widget/hex-edit.cpp
+++ b/snespurify/phoenix/gtk/widget/hex-edit.cpp
@@ -112,17 +112,17 @@ bool pHexEdit::keyPress(unsigned scancode) {
unsigned cursorY = position / lineWidth;
unsigned cursorX = position % lineWidth;
- if(scancode == GDK_Home) {
+ if(scancode == GDK_KEY_Home) {
setCursorPosition(cursorY * lineWidth + 10);
return true;
}
- if(scancode == GDK_End) {
+ if(scancode == GDK_KEY_End) {
setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1));
return true;
}
- if(scancode == GDK_Up) {
+ if(scancode == GDK_KEY_Up) {
if(cursorY != 0) return false;
signed newOffset = hexEdit.state.offset - hexEdit.state.columns;
@@ -133,7 +133,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
- if(scancode == GDK_Down) {
+ if(scancode == GDK_KEY_Down) {
if(cursorY != hexEdit.state.rows - 1) return false;
signed newOffset = hexEdit.state.offset + hexEdit.state.columns;
@@ -144,7 +144,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
- if(scancode == GDK_Page_Up) {
+ if(scancode == GDK_KEY_Page_Up) {
signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows;
if(newOffset >= 0) {
hexEdit.setOffset(newOffset);
@@ -155,7 +155,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
return true;
}
- if(scancode == GDK_Page_Down) {
+ if(scancode == GDK_KEY_Page_Down) {
signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows;
for(unsigned n = 0; n < hexEdit.state.rows; n++) {
if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
diff --git a/snespurify/phoenix/gtk/widget/horizontal-scroll-bar.cpp b/snespurify/phoenix/gtk/widget/horizontal-scroll-bar.cpp
new file mode 100755
index 00000000..f260cb58
--- /dev/null
+++ b/snespurify/phoenix/gtk/widget/horizontal-scroll-bar.cpp
@@ -0,0 +1,29 @@
+static void HorizontalScrollBar_change(HorizontalScrollBar *self) {
+ if(self->state.position == self->position()) return;
+ self->state.position = self->position();
+ if(self->onChange) self->onChange();
+}
+
+Geometry pHorizontalScrollBar::minimumGeometry() {
+ return { 0, 0, 0, 20 };
+}
+
+unsigned pHorizontalScrollBar::position() {
+ return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
+}
+
+void pHorizontalScrollBar::setLength(unsigned length) {
+ length += length == 0;
+ gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
+ gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+}
+
+void pHorizontalScrollBar::setPosition(unsigned position) {
+ gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+void pHorizontalScrollBar::constructor() {
+ gtkWidget = gtk_hscrollbar_new(0);
+ setLength(101);
+ g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScrollBar_change), (gpointer)&horizontalScrollBar);
+}
diff --git a/snespurify/phoenix/gtk/widget/horizontal-slider.cpp b/snespurify/phoenix/gtk/widget/horizontal-slider.cpp
index 89a17a8a..4541b6fd 100755
--- a/snespurify/phoenix/gtk/widget/horizontal-slider.cpp
+++ b/snespurify/phoenix/gtk/widget/horizontal-slider.cpp
@@ -15,6 +15,7 @@ unsigned pHorizontalSlider::position() {
void pHorizontalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
+ gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pHorizontalSlider::setPosition(unsigned position) {
@@ -24,5 +25,6 @@ void pHorizontalSlider::setPosition(unsigned position) {
void pHorizontalSlider::constructor() {
gtkWidget = gtk_hscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
+ setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider);
}
diff --git a/snespurify/phoenix/gtk/widget/line-edit.cpp b/snespurify/phoenix/gtk/widget/line-edit.cpp
index e783b3e2..e630b3f5 100755
--- a/snespurify/phoenix/gtk/widget/line-edit.cpp
+++ b/snespurify/phoenix/gtk/widget/line-edit.cpp
@@ -13,7 +13,7 @@ Geometry pLineEdit::minimumGeometry() {
}
void pLineEdit::setEditable(bool editable) {
- gtk_entry_set_editable(GTK_ENTRY(gtkWidget), editable);
+ gtk_editable_set_editable(GTK_EDITABLE(gtkWidget), editable);
}
void pLineEdit::setText(const string &text) {
diff --git a/snespurify/phoenix/gtk/widget/vertical-scroll-bar.cpp b/snespurify/phoenix/gtk/widget/vertical-scroll-bar.cpp
new file mode 100755
index 00000000..bde3b73f
--- /dev/null
+++ b/snespurify/phoenix/gtk/widget/vertical-scroll-bar.cpp
@@ -0,0 +1,29 @@
+static void VerticalScrollBar_change(VerticalScrollBar *self) {
+ if(self->state.position == self->position()) return;
+ self->state.position = self->position();
+ if(self->onChange) self->onChange();
+}
+
+Geometry pVerticalScrollBar::minimumGeometry() {
+ return { 0, 0, 20, 0 };
+}
+
+unsigned pVerticalScrollBar::position() {
+ return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget));
+}
+
+void pVerticalScrollBar::setLength(unsigned length) {
+ length += length == 0;
+ gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
+ gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
+}
+
+void pVerticalScrollBar::setPosition(unsigned position) {
+ gtk_range_set_value(GTK_RANGE(gtkWidget), position);
+}
+
+void pVerticalScrollBar::constructor() {
+ gtkWidget = gtk_vscrollbar_new(0);
+ setLength(101);
+ g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScrollBar_change), (gpointer)&verticalScrollBar);
+}
diff --git a/snespurify/phoenix/gtk/widget/vertical-slider.cpp b/snespurify/phoenix/gtk/widget/vertical-slider.cpp
index b19632f7..ebe59ee7 100755
--- a/snespurify/phoenix/gtk/widget/vertical-slider.cpp
+++ b/snespurify/phoenix/gtk/widget/vertical-slider.cpp
@@ -15,6 +15,7 @@ unsigned pVerticalSlider::position() {
void pVerticalSlider::setLength(unsigned length) {
length += length == 0;
gtk_range_set_range(GTK_RANGE(gtkWidget), 0, length - 1);
+ gtk_range_set_increments(GTK_RANGE(gtkWidget), 1, length >> 3);
}
void pVerticalSlider::setPosition(unsigned position) {
@@ -24,5 +25,6 @@ void pVerticalSlider::setPosition(unsigned position) {
void pVerticalSlider::constructor() {
gtkWidget = gtk_vscale_new_with_range(0, 100, 1);
gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false);
+ setLength(101);
g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider);
}
diff --git a/snespurify/phoenix/gtk/widget/viewport.cpp b/snespurify/phoenix/gtk/widget/viewport.cpp
index fd3bae57..7683c7f9 100755
--- a/snespurify/phoenix/gtk/widget/viewport.cpp
+++ b/snespurify/phoenix/gtk/widget/viewport.cpp
@@ -1,5 +1,5 @@
uintptr_t pViewport::handle() {
- return GDK_WINDOW_XID(gtkWidget->window);
+ return GDK_WINDOW_XID(gtk_widget_get_window(gtkWidget));
}
void pViewport::constructor() {
diff --git a/snespurify/phoenix/gtk/window.cpp b/snespurify/phoenix/gtk/window.cpp
index 632929dc..85beae54 100755
--- a/snespurify/phoenix/gtk/window.cpp
+++ b/snespurify/phoenix/gtk/window.cpp
@@ -1,70 +1,79 @@
static void Action_setFont(GtkWidget *widget, gpointer font);
static void Widget_setFont(GtkWidget *widget, gpointer font);
-static gint Window_close(Window *window) {
+static gint Window_close(GtkWidget *widget, GdkEvent *event, Window *window) {
if(window->onClose) window->onClose();
window->setVisible(false);
return true;
}
-static gboolean Window_configure(Window *window) {
+static gboolean Window_expose(GtkWidget *widget, GdkEvent *event, Window *window) {
+ cairo_t *context = gdk_cairo_create(widget->window);
+
+ Color color = window->backgroundColor();
+ double red = (double)color.red / 255.0;
+ double green = (double)color.green / 255.0;
+ double blue = (double)color.blue / 255.0;
+ double alpha = (double)color.alpha / 255.0;
+
+ if(gdk_screen_is_composited(gdk_screen_get_default())) {
+ cairo_set_source_rgba(context, red, green, blue, alpha);
+ } else {
+ cairo_set_source_rgb(context, red, green, blue);
+ }
+
+ cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(context);
+ cairo_destroy(context);
+
+ return false;
+}
+
+static gboolean Window_configure(GtkWidget *widget, GdkEvent *event, Window *window) {
if(gtk_widget_get_realized(window->p.widget) == false) return false;
+ GdkWindow *gdkWindow = gtk_widget_get_window(widget);
//update geometry settings
- Display *display = XOpenDisplay(0);
- XWindowAttributes attributes, parentAttributes;
- XGetWindowAttributes(display, GDK_WINDOW_XID(window->p.widget->window), &attributes);
- X11Window rootWindow, parentWindow, *childWindow = 0;
- unsigned int childCount;
- XQueryTree(display, GDK_WINDOW_XID(window->p.widget->window), &rootWindow, &parentWindow, &childWindow, &childCount);
- XGetWindowAttributes(display, parentWindow, &parentAttributes);
- if(childWindow) XFree(childWindow);
- XCloseDisplay(display);
+ GdkRectangle border, client;
+ gdk_window_get_frame_extents(gdkWindow, &border);
+ gdk_window_get_geometry(gdkWindow, 0, 0, &client.width, &client.height, 0);
+ gdk_window_get_origin(gdkWindow, &client.x, &client.y);
- settings.frameGeometryX = attributes.x;
- settings.frameGeometryY = attributes.y;
- settings.frameGeometryWidth = parentAttributes.width - attributes.width;
- settings.frameGeometryHeight = parentAttributes.height - attributes.height;
-
- GtkAllocation menuAllocation, statusAllocation;
- gtk_widget_get_allocation(window->p.menu, &menuAllocation);
- gtk_widget_get_allocation(window->p.status, &statusAllocation);
-
- if(menuAllocation.height > 1) settings.menuGeometryHeight = menuAllocation.height;
- if(statusAllocation.height > 1) settings.statusGeometryHeight = statusAllocation.height;
-
- //calculate current window position
- signed eventX = parentAttributes.x + attributes.x;
- signed eventY = parentAttributes.y + attributes.y + window->p.menuHeight();
- unsigned eventWidth = attributes.width;
- unsigned eventHeight = attributes.height - window->p.menuHeight() - window->p.statusHeight();
+ settings.frameGeometryX = client.x - border.x;
+ settings.frameGeometryY = client.y - border.y;
+ settings.frameGeometryWidth = border.width - client.width;
+ settings.frameGeometryHeight = border.height - client.height;
//move
- if(window->p.locked == false && window->state.fullScreen == false) {
- if(window->state.geometry.x != eventX || window->state.geometry.y != eventY) {
- window->state.geometry.x = eventX;
- window->state.geometry.y = eventY;
+ if(event->configure.x != window->p.lastConfigure.x
+ || event->configure.y != window->p.lastConfigure.y
+ ) {
+ if(window->state.fullScreen == false) {
+ window->state.geometry.x = client.x;
+ window->state.geometry.y = client.y + window->p.menuHeight();
}
+ if(window->p.locked == false && window->onMove) window->onMove();
}
- if(window->onMove) window->onMove();
-
//size
- if(window->p.locked == false && window->state.fullScreen == false) {
- if(window->state.geometry.width != eventWidth || window->state.geometry.height != eventHeight) {
- window->state.geometry.width = eventWidth;
- window->state.geometry.height = eventHeight;
+ if(event->configure.width != window->p.lastConfigure.width
+ || event->configure.height != window->p.lastConfigure.height
+ ) {
+ if(window->state.fullScreen == false) {
+ window->state.geometry.width = client.width;
+ window->state.geometry.height = client.height - window->p.menuHeight() - window->p.statusHeight();
}
+
+ foreach(layout, window->state.layout) {
+ Geometry geometry = window->geometry();
+ geometry.x = geometry.y = 0;
+ layout.setGeometry(geometry);
+ }
+
+ if(window->p.locked == false && window->onSize) window->onSize();
}
- foreach(layout, window->state.layout) {
- Geometry geometry = window->geometry();
- geometry.x = geometry.y = 0;
- layout.setGeometry(geometry);
- }
-
- if(window->onSize) window->onSize();
-
+ window->p.lastConfigure = event->configure;
return false;
}
@@ -77,7 +86,7 @@ void pWindow::append(Layout &layout) {
void pWindow::append(Menu &subMenu) {
if(window.state.menuFont) subMenu.p.setFont(*window.state.menuFont);
- gtk_menu_bar_append(menu, subMenu.p.widget);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), subMenu.p.widget);
gtk_widget_show(subMenu.p.widget);
}
@@ -90,6 +99,12 @@ void pWindow::append(Widget &widget) {
widget.setVisible();
}
+Color pWindow::backgroundColor() {
+ if(window.state.backgroundColorOverride) return window.state.backgroundColor;
+ GdkColor color = widget->style->bg[GTK_STATE_NORMAL];
+ return { (uint8_t)(color.red >> 8), (uint8_t)(color.green >> 8), (uint8_t)(color.blue >> 8), 255 };
+}
+
Geometry pWindow::frameMargin() {
if(window.state.fullScreen) return { 0, menuHeight(), 0, menuHeight() + statusHeight() };
return {
@@ -111,13 +126,13 @@ Geometry pWindow::geometry() {
return window.state.geometry;
}
-void pWindow::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) {
- GdkColor color;
- color.pixel = (red << 16) | (green << 8) | (blue << 0);
- color.red = (red << 8) | (red << 0);
- color.green = (green << 8) | (green << 0);
- color.blue = (blue << 8) | (blue << 0);
- gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &color);
+void pWindow::setBackgroundColor(const Color &color) {
+ GdkColor gdkColor;
+ gdkColor.pixel = (color.red << 16) | (color.green << 8) | (color.blue << 0);
+ gdkColor.red = (color.red << 8) | (color.red << 0);
+ gdkColor.green = (color.green << 8) | (color.green << 0);
+ gdkColor.blue = (color.blue << 8) | (color.blue << 0);
+ gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor);
}
void pWindow::setFocused() {
@@ -129,14 +144,12 @@ void pWindow::setFullScreen(bool fullScreen) {
gtk_window_unfullscreen(GTK_WINDOW(widget));
gtk_window_set_resizable(GTK_WINDOW(widget), window.state.resizable);
gtk_window_set_decorated(GTK_WINDOW(widget), true);
- locked = true;
for(unsigned n = 0; n < 4; n++) {
setGeometry(window.state.geometry);
gtk_widget_set_size_request(widget, -1, -1);
OS::processEvents();
usleep(2000);
}
- locked = false;
} else {
gtk_window_fullscreen(GTK_WINDOW(widget));
gtk_window_set_decorated(GTK_WINDOW(widget), false);
@@ -198,7 +211,15 @@ void pWindow::setWidgetFont(Font &font) {
}
void pWindow::constructor() {
+ memset(&lastConfigure, 0, sizeof(GdkEventConfigure));
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ if(gdk_screen_is_composited(gdk_screen_get_default())) {
+ gtk_widget_set_colormap(widget, gdk_screen_get_rgba_colormap(gdk_screen_get_default()));
+ } else {
+ gtk_widget_set_colormap(widget, gdk_screen_get_rgb_colormap(gdk_screen_get_default()));
+ }
+
gtk_window_set_resizable(GTK_WINDOW(widget), true);
gtk_widget_set_app_paintable(widget, true);
gtk_widget_add_events(widget, GDK_CONFIGURE);
@@ -224,8 +245,9 @@ void pWindow::constructor() {
setTitle("");
setGeometry(window.state.geometry);
- g_signal_connect_swapped(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
- g_signal_connect_swapped(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
+ g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
+ g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window);
+ g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
}
unsigned pWindow::menuHeight() {
diff --git a/snespurify/phoenix/nall/Makefile b/snespurify/phoenix/nall/Makefile
deleted file mode 100755
index 9a93bd23..00000000
--- a/snespurify/phoenix/nall/Makefile
+++ /dev/null
@@ -1,109 +0,0 @@
-# Makefile
-# author: byuu
-# license: public domain
-
-[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
-[0-9] = 0 1 2 3 4 5 6 7 8 9
-[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ?
-[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup])
-[space] :=
-[space] +=
-
-#####
-# platform detection
-#####
-
-ifeq ($(platform),)
- uname := $(shell uname -a)
- ifeq ($(uname),)
- platform := win
- delete = del $(subst /,\,$1)
- else ifneq ($(findstring Darwin,$(uname)),)
- platform := osx
- delete = rm -f $1
- else
- platform := x
- delete = rm -f $1
- endif
-endif
-
-ifeq ($(compiler),)
- ifeq ($(platform),win)
- compiler := gcc
- else ifeq ($(platform),osx)
- compiler := gcc-mp-4.5
- else
- compiler := gcc-4.5
- endif
-endif
-
-ifeq ($(prefix),)
- prefix := /usr/local
-endif
-
-#####
-# function rwildcard(directory, pattern)
-#####
-rwildcard = \
- $(strip \
- $(filter $(if $2,$2,%), \
- $(foreach f, \
- $(wildcard $1*), \
- $(eval t = $(call rwildcard,$f/)) \
- $(if $t,$t,$f) \
- ) \
- ) \
- )
-
-#####
-# function strtr(source, from, to)
-#####
-strtr = \
- $(eval __temp := $1) \
- $(strip \
- $(foreach c, \
- $(join $(addsuffix :,$2),$3), \
- $(eval __temp := \
- $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \
- ) \
- ) \
- $(__temp) \
- )
-
-#####
-# function strupper(source)
-#####
-strupper = $(call strtr,$1,$([a-z]),$([A-Z]))
-
-#####
-# function strlower(source)
-#####
-strlower = $(call strtr,$1,$([A-Z]),$([a-z]))
-
-#####
-# function strlen(source)
-#####
-strlen = \
- $(eval __temp := $(subst $([space]),_,$1)) \
- $(words \
- $(strip \
- $(foreach c, \
- $([all]), \
- $(eval __temp := \
- $(subst $c,$c ,$(__temp)) \
- ) \
- ) \
- $(__temp) \
- ) \
- )
-
-#####
-# function streq(source)
-#####
-streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1)
-
-#####
-# function strne(source)
-#####
-strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,)
diff --git a/snespurify/phoenix/nall/algorithm.hpp b/snespurify/phoenix/nall/algorithm.hpp
deleted file mode 100755
index 037f0bb7..00000000
--- a/snespurify/phoenix/nall/algorithm.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef NALL_ALGORITHM_HPP
-#define NALL_ALGORITHM_HPP
-
-#undef min
-#undef max
-
-namespace nall {
- template T min(const T &t, const U &u) {
- return t < u ? t : u;
- }
-
- template T max(const T &t, const U &u) {
- return t > u ? t : u;
- }
-}
-
-#endif
diff --git a/snespurify/phoenix/nall/any.hpp b/snespurify/phoenix/nall/any.hpp
deleted file mode 100755
index b31cff3c..00000000
--- a/snespurify/phoenix/nall/any.hpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef NALL_ANY_HPP
-#define NALL_ANY_HPP
-
-#include
-#include
-#include
-
-namespace nall {
- class any {
- public:
- bool empty() const { return container; }
- const std::type_info& type() const { return container ? container->type() : typeid(void); }
-
- template any& operator=(const T& value_) {
- typedef typename static_if<
- std::is_array::value,
- typename std::remove_extent::type>::type*,
- T
- >::type auto_t;
-
- if(type() == typeid(auto_t)) {
- static_cast*>(container)->value = (auto_t)value_;
- } else {
- if(container) delete container;
- container = new holder((auto_t)value_);
- }
-
- return *this;
- }
-
- any() : container(0) {}
- template any(const T& value_) : container(0) { operator=(value_); }
-
- private:
- struct placeholder {
- virtual const std::type_info& type() const = 0;
- } *container;
-
- template struct holder : placeholder {
- T value;
- const std::type_info& type() const { return typeid(T); }
- holder(const T& value_) : value(value_) {}
- };
-
- template friend T any_cast(any&);
- template friend T any_cast(const any&);
- template friend T* any_cast(any*);
- template friend const T* any_cast(const any*);
- };
-
- template T any_cast(any &value) {
- typedef typename std::remove_reference::type nonref;
- if(value.type() != typeid(nonref)) throw;
- return static_cast*>(value.container)->value;
- }
-
- template T any_cast(const any &value) {
- typedef const typename std::remove_reference::type nonref;
- if(value.type() != typeid(nonref)) throw;
- return static_cast*>(value.container)->value;
- }
-
- template T* any_cast(any *value) {
- if(!value || value->type() != typeid(T)) return 0;
- return &static_cast*>(value->container)->value;
- }
-
- template const T* any_cast(const any *value) {
- if(!value || value->type() != typeid(T)) return 0;
- return &static_cast