Update to v093r13 release.

byuu says:

This WIP removes nall/input.hpp entirely, and implements the new
universal cheat format for FC/SFC/GB/GBC/SGB.

GBA is going to be tricky since there's some consternation around
byte/word/dword overrides.

It's also not immediately obvious to me how to implement the code search
in logarithmic time, due to the optional compare value.

Lastly, the cheat values inside cheats.bml seem to be broken for the
SFC. Likely there's a bug somewhere in the conversion process. Obviously
I'll have to fix that before v094.

I received no feedback on the universal cheat format. If nobody adds
anything before v094, then I don't want to hear any complaining about
the formatting :P
This commit is contained in:
Tim Allen 2014-01-13 20:35:46 +11:00
parent 2b81b630cb
commit fe85679321
36 changed files with 29012 additions and 27489 deletions

View File

@ -39,7 +39,7 @@ ifeq ($(platform),windows)
else
link += -mwindows
endif
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32 -ldxguid
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(platform),macosx)
flags += -march=native

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "093.12";
static const char Version[] = "093.13";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";
@ -33,6 +33,7 @@ namespace Emulator {
#include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/set.hpp>
#include <nall/sha256.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>

View File

@ -4,85 +4,25 @@ namespace Famicom {
Cheat cheat;
bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) {
static bool initialize = false;
static uint8 mapProActionReplay[256], mapGameGenie[256];
void Cheat::reset() {
codes.reset();
}
if(initialize == false) {
initialize = true;
void Cheat::append(unsigned addr, unsigned data) {
codes.append({addr, Unused, data});
}
for(auto& n : mapProActionReplay) n = ~0;
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
codes.append({addr, comp, data});
}
for(auto& n : mapGameGenie) n = ~0;
mapGameGenie['A'] = 0; mapGameGenie['P'] = 1; mapGameGenie['Z'] = 2; mapGameGenie['L'] = 3;
mapGameGenie['G'] = 4; mapGameGenie['I'] = 5; mapGameGenie['T'] = 6; mapGameGenie['Y'] = 7;
mapGameGenie['E'] = 8; mapGameGenie['O'] = 9; mapGameGenie['X'] = 10; mapGameGenie['U'] = 11;
mapGameGenie['K'] = 12; mapGameGenie['S'] = 13; mapGameGenie['V'] = 14; mapGameGenie['N'] = 15;
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
for(auto& code : codes) {
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
return {true, code.data};
}
string code = code_;
code.upper();
unsigned length = code.length(), bits = 0;
if(code.match("????:??")) {
code = {substr(code, 0, 4), substr(code, 5, 2)};
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 8) & 0xffff;
data = (bits >> 0) & 0xff;
comp = ~0;
return true;
}
if(code.match("????:??:??")) {
code = {substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2)};
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 16) & 0xffff;
data = (bits >> 8) & 0xff;
comp = (bits >> 0) & 0xff;
return true;
}
if(length == 6) {
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
unsigned addrTable[] = {10, 9, 8, 7, 2, 1, 0, 19, 14, 13, 12, 11, 6, 5, 4};
unsigned dataTable[] = {23, 18, 17, 16, 3, 22, 21, 20};
addr = 0x8000, data = 0x00, comp = ~0;
for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0;
for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0;
return true;
}
if(length == 8) {
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
unsigned addrTable[] = {18, 17, 16, 15, 10, 9, 8, 27, 22, 21, 20, 19, 14, 13, 12};
unsigned dataTable[] = {31, 26, 25, 24, 3, 30, 29, 28};
unsigned compTable[] = {7, 2, 1, 0, 11, 6, 5, 4};
addr = 0x8000, data = 0x00, comp = 0x00;
for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0;
for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0;
for(unsigned n = 0; n < 8; n++) comp |= bits & (1 << compTable[n]) ? 0x80 >> n : 0;
return true;
}
return false;
}
void Cheat::synchronize() {
for(auto& n : override) n = false;
for(unsigned n = 0; n < size(); n++) {
override[operator[](n).addr] = true;
}
}
}

View File

@ -1,14 +1,17 @@
struct CheatCode {
struct Cheat {
struct Code {
unsigned addr;
unsigned data;
unsigned comp;
};
unsigned data;
};
vector<Code> codes;
enum : unsigned { Unused = ~0u };
struct Cheat : public vector<CheatCode> {
static bool decode(string code, unsigned& addr, unsigned& data, unsigned& comp);
void synchronize();
bool override[65536];
alwaysinline bool enable() const { return codes.size() > 0; }
void reset();
void append(unsigned addr, unsigned data);
void append(unsigned addr, unsigned comp, unsigned data);
optional<unsigned> find(unsigned addr, unsigned comp);
};
extern Cheat cheat;

View File

@ -105,14 +105,14 @@ bool Interface::unserialize(serializer& s) {
void Interface::cheatSet(const lstring& list) {
cheat.reset();
for(auto& code : list) {
lstring codelist = code.split("+");
for(auto& part : codelist) {
unsigned addr, data, comp;
if(Cheat::decode(part, addr, data, comp)) cheat.append({addr, data, comp});
for(auto& codeset : list) {
lstring codes = codeset.split("+");
for(auto& code : codes) {
lstring part = code.split("/");
if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1]));
if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
}
}
cheat.synchronize();
}
void Interface::paletteUpdate(PaletteMode mode) {

View File

@ -17,15 +17,8 @@ uint8 Bus::read(uint16 addr) {
else if(addr <= 0x3fff) data = ppu.read(addr);
else if(addr <= 0x4017) data = cpu.read(addr);
if(cheat.override[addr]) {
for(unsigned n = 0; n < cheat.size(); n++) {
if(cheat[n].addr == addr) {
if(cheat[n].comp > 255 || cheat[n].comp == data) {
data = cheat[n].data;
break;
}
}
}
if(cheat.enable()) {
if(auto result = cheat.find(addr, data)) return result();
}
return data;

View File

@ -68,8 +68,8 @@ void APU::Wave::power() {
frequency = 0;
counter = 0;
//random_lfsr r;
for(auto& n : pattern) n = 0;
LinearFeedbackShiftRegisterGenerator r;
for(auto& n : pattern) n = r() & 15;
output = 0;
length = 0;

View File

@ -4,88 +4,25 @@ namespace GameBoy {
Cheat cheat;
bool Cheat::decode(string code_, unsigned& addr, unsigned& data, unsigned& comp) {
static bool initialize = false;
static uint8 mapProActionReplay[256], mapGameGenie[256];
void Cheat::reset() {
codes.reset();
}
if(initialize == false) {
initialize = true;
void Cheat::append(unsigned addr, unsigned data) {
codes.append({addr, Unused, data});
}
for(auto& n : mapProActionReplay) n = ~0;
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
codes.append({addr, comp, data});
}
for(auto& n : mapGameGenie) n = ~0;
mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3;
mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7;
mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11;
mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15;
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
for(auto& code : codes) {
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
return {true, code.data};
}
string code = code_;
code.upper();
unsigned length = code.length(), bits = 0;
if(code.match("????:??")) {
code = {substr(code, 0, 4), substr(code, 5, 2)};
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 8) & 0xffff;
data = (bits >> 0) & 0xff;
comp = ~0;
return true;
}
if(code.match("????:??:??")) {
code = {substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2)};
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
bits = hex(code);
addr = (bits >> 16) & 0xffff;
data = (bits >> 8) & 0xff;
comp = (bits >> 0) & 0xff;
return true;
}
if(code.match("???" "-" "???")) {
code = {substr(code, 0, 3), substr(code, 4, 3)};
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
addr = (bits >> 0) & 0xffff;
data = (bits >> 16) & 0xff;
comp = ~0;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
return true;
}
if(code.match("???" "-" "???" "-" "???")) {
code = {substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1)};
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
addr = (bits >> 8) & 0xffff;
data = (bits >> 24) & 0xff;
comp = (bits >> 0) & 0xff;
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba;
return true;
}
return false;
}
void Cheat::synchronize() {
for(auto& n : override) n = false;
for(unsigned n = 0; n < size(); n++) {
override[operator[](n).addr] = true;
}
}
}

View File

@ -1,14 +1,17 @@
struct CheatCode {
struct Cheat {
struct Code {
unsigned addr;
unsigned data;
unsigned comp;
};
unsigned data;
};
vector<Code> codes;
enum : unsigned { Unused = ~0u };
struct Cheat : public vector<CheatCode> {
static bool decode(string code, unsigned& addr, unsigned& data, unsigned& comp);
void synchronize();
bool override[65536];
alwaysinline bool enable() const { return codes.size() > 0; }
void reset();
void append(unsigned addr, unsigned data);
void append(unsigned addr, unsigned comp, unsigned data);
optional<unsigned> find(unsigned addr, unsigned comp);
};
extern Cheat cheat;

View File

@ -121,14 +121,14 @@ bool Interface::unserialize(serializer& s) {
void Interface::cheatSet(const lstring& list) {
cheat.reset();
for(auto& code : list) {
lstring codelist = code.split("+");
for(auto& part : codelist) {
unsigned addr, data, comp;
if(Cheat::decode(part, addr, data, comp)) cheat.append({addr, data, comp});
for(auto& codeset : list) {
lstring codes = codeset.split("+");
for(auto& code : codes) {
lstring part = code.split("/");
if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1]));
if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
}
}
cheat.synchronize();
}
void Interface::paletteUpdate(PaletteMode mode) {

View File

@ -44,15 +44,8 @@ Memory::~Memory() {
uint8 Bus::read(uint16 addr) {
uint8 data = mmio[addr]->mmio_read(addr);
if(cheat.override[addr]) {
for(unsigned n = 0; n < cheat.size(); n++) {
if(cheat[n].addr == addr) {
if(cheat[n].comp > 255 || cheat[n].comp == data) {
data = cheat[n].data;
break;
}
}
}
if(cheat.enable()) {
if(auto result = cheat.find(addr, data)) return result();
}
return data;

View File

@ -1,767 +1,22 @@
#ifndef NALL_IMAGE_HPP
#define NALL_IMAGE_HPP
#include <algorithm>
#include <nall/bmp.hpp>
#include <nall/filemap.hpp>
#include <nall/interpolation.hpp>
#include <nall/png.hpp>
#include <nall/stdint.hpp>
#include <algorithm>
namespace nall {
struct image {
uint8_t* data = nullptr;
unsigned width = 0;
unsigned height = 0;
unsigned pitch = 0;
unsigned size = 0;
bool endian = 0; //0 = lsb, 1 = msb
unsigned depth = 32;
unsigned stride = 4;
struct Channel {
uint64_t mask;
unsigned depth;
unsigned shift;
inline bool operator==(const Channel& source) {
return mask == source.mask && depth == source.depth && shift == source.shift;
}
inline bool operator!=(const Channel& source) {
return !operator==(source);
}
};
enum class blend : unsigned {
add,
sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha)
sourceColor, //color = sourceColor
targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha)
targetColor, //color = targetColor
};
Channel alpha = {255u << 24, 8u, 24u};
Channel red = {255u << 16, 8u, 16u};
Channel green = {255u << 8, 8u, 8u};
Channel blue = {255u << 0, 8u, 0u};
typedef double (*interpolation)(double, double, double, double, double);
static inline unsigned bitDepth(uint64_t color);
static inline unsigned bitShift(uint64_t color);
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
inline bool operator==(const image& source);
inline bool operator!=(const image& source);
inline image& operator=(const image& source);
inline image& operator=(image&& source);
inline image(const image& source);
inline image(image&& source);
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image(const string& filename);
inline image(const uint8_t* data, unsigned size);
inline image();
inline ~image();
inline uint64_t read(const uint8_t* data) const;
inline void write(uint8_t* data, uint64_t value) const;
inline void free();
inline bool empty() const;
inline void allocate(unsigned width, unsigned height);
inline bool crop(unsigned x, unsigned y, unsigned width, unsigned height);
inline void impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height);
inline void fill(uint64_t color = 0);
inline void gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d);
inline void horizontalGradient(uint64_t a, uint64_t b);
inline void verticalGradient(uint64_t a, uint64_t b);
inline bool load(const string& filename);
//inline bool loadBMP(const uint8_t* data, unsigned size);
inline bool loadPNG(const uint8_t* data, unsigned size);
inline void scale(unsigned width, unsigned height, bool linear = true);
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline void alphaBlend(uint64_t alphaColor);
protected:
inline uint8_t* allocate(unsigned width, unsigned height, unsigned stride);
alwaysinline uint64_t interpolate1D(int64_t a, int64_t b, uint32_t x);
alwaysinline uint64_t interpolate2D(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y);
inline void scaleLinearWidth(unsigned width);
inline void scaleLinearHeight(unsigned height);
inline void scaleLinear(unsigned width, unsigned height);
inline void scaleNearest(unsigned width, unsigned height);
inline bool loadBMP(const string& filename);
inline bool loadPNG(const string& filename);
};
//static
unsigned image::bitDepth(uint64_t color) {
unsigned depth = 0;
if(color) while((color & 1) == 0) color >>= 1;
while((color & 1) == 1) { color >>= 1; depth++; }
return depth;
}
unsigned image::bitShift(uint64_t color) {
unsigned shift = 0;
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
return shift;
}
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
if(sourceDepth == 0 || targetDepth == 0) return 0;
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;
}
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
return color;
}
//public
bool image::operator==(const image& source) {
if(width != source.width) return false;
if(height != source.height) return false;
if(pitch != source.pitch) return false;
if(endian != source.endian) return false;
if(stride != source.stride) return false;
if(alpha != source.alpha) return false;
if(red != source.red) return false;
if(green != source.green) return false;
if(blue != source.blue) return false;
return memcmp(data, source.data, width * height * stride) == 0;
}
bool image::operator!=(const image& source) {
return !operator==(source);
}
image& image::operator=(const image& source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
size = source.size;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = allocate(width, height, stride);
memcpy(data, source.data, source.size);
return *this;
}
image& image::operator=(image&& source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
size = source.size;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = source.data;
source.data = nullptr;
return *this;
}
image::image(const image& source) {
operator=(source);
}
image::image(image&& source) {
operator=(std::forward<image>(source));
}
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) {
this->endian = endian;
this->depth = depth;
this->stride = (depth / 8) + ((depth & 7) > 0);
alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)};
red = {redMask, bitDepth(redMask), bitShift(redMask )};
green = {greenMask, bitDepth(greenMask), bitShift(greenMask)};
blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )};
}
image::image(const string& filename) {
load(filename);
}
image::image(const uint8_t* data, unsigned size) {
loadPNG(data, size);
}
image::image() {
}
image::~image() {
free();
}
uint64_t image::read(const uint8_t* data) const {
uint64_t result = 0;
if(endian == 0) {
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
} else {
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
}
return result;
}
void image::write(uint8_t* data, uint64_t value) const {
if(endian == 0) {
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
} else {
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
}
}
void image::free() {
if(data) delete[] data;
data = nullptr;
}
bool image::empty() const {
if(data == nullptr) return true;
if(width == 0 || height == 0) return true;
return false;
}
void image::allocate(unsigned width, unsigned height) {
if(data != nullptr && this->width == width && this->height == height) return;
free();
data = allocate(width, height, stride);
pitch = width * stride;
size = height * pitch;
this->width = width;
this->height = height;
}
void image::fill(uint64_t color) {
uint8_t* dp = data;
for(unsigned n = 0; n < width * height; n++) {
write(dp, color);
dp += stride;
}
}
void image::gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) {
//create gradient by scaling 2x2 image using linear interpolation
//replace data with gradient data to prevent extra copy
delete[] data;
nall::image gradient;
gradient.endian = endian, gradient.depth = depth, gradient.stride = stride;
gradient.alpha = alpha, gradient.red = red, gradient.green = green, gradient.blue = blue;
gradient.allocate(2, 2);
uint8_t* dp = gradient.data;
gradient.write(dp, a); dp += stride;
gradient.write(dp, b); dp += stride;
gradient.write(dp, c); dp += stride;
gradient.write(dp, d); dp += stride;
gradient.scale(width, height);
data = gradient.data;
gradient.data = nullptr;
}
void image::horizontalGradient(uint64_t a, uint64_t b) {
gradient(a, b, a, b);
}
void image::verticalGradient(uint64_t a, uint64_t b) {
gradient(a, a, b, b);
}
bool image::load(const string& filename) {
if(loadBMP(filename) == true) return true;
if(loadPNG(filename) == true) return true;
return false;
}
bool image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) {
if(outputX + outputWidth > width) return false;
if(outputY + outputHeight > height) return false;
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
const uint8_t* sp = data + pitch * (outputY + y) + stride * outputX;
uint8_t* dp = outputData + outputPitch * y;
for(unsigned x = 0; x < outputWidth; x++) {
write(dp, read(sp));
sp += stride;
dp += stride;
}
}
delete[] data;
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = width * pitch;
return true;
}
void image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) {
source.transform(endian, depth, alpha.mask, red.mask, green.mask, blue.mask);
for(unsigned y = 0; y < sourceHeight; y++) {
const uint8_t* sp = source.data + source.pitch * (sourceY + y) + source.stride * sourceX;
uint8_t* dp = data + pitch * (targetY + y) + stride * targetX;
for(unsigned x = 0; x < sourceWidth; x++) {
uint64_t sourceColor = source.read(sp);
uint64_t targetColor = read(dp);
int64_t sa = (sourceColor & alpha.mask) >> alpha.shift;
int64_t sr = (sourceColor & red.mask ) >> red.shift;
int64_t sg = (sourceColor & green.mask) >> green.shift;
int64_t sb = (sourceColor & blue.mask ) >> blue.shift;
int64_t da = (targetColor & alpha.mask) >> alpha.shift;
int64_t dr = (targetColor & red.mask ) >> red.shift;
int64_t dg = (targetColor & green.mask) >> green.shift;
int64_t db = (targetColor & blue.mask ) >> blue.shift;
uint64_t a, r, g, b;
switch(mode) {
case blend::add:
a = max(sa, da);
r = min(red.mask >> red.shift, ((sr * sa) >> alpha.depth) + ((dr * da) >> alpha.depth));
g = min(green.mask >> green.shift, ((sg * sa) >> alpha.depth) + ((dg * da) >> alpha.depth));
b = min(blue.mask >> blue.shift, ((sb * sa) >> alpha.depth) + ((db * da) >> alpha.depth));
break;
case blend::sourceAlpha:
a = max(sa, da);
r = dr + (((sr - dr) * sa) >> alpha.depth);
g = dg + (((sg - dg) * sa) >> alpha.depth);
b = db + (((sb - db) * sa) >> alpha.depth);
break;
case blend::sourceColor:
a = sa;
r = sr;
g = sg;
b = sb;
break;
case blend::targetAlpha:
a = max(sa, da);
r = sr + (((dr - sr) * da) >> alpha.depth);
g = sg + (((dg - sg) * da) >> alpha.depth);
b = sb + (((db - sb) * da) >> alpha.depth);
break;
case blend::targetColor:
a = da;
r = dr;
g = dg;
b = db;
break;
}
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
sp += source.stride;
dp += stride;
}
}
}
void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) {
if(width == outputWidth && height == outputHeight) return; //no scaling necessary
if(linear == false) return scaleNearest(outputWidth, outputHeight);
if(width == outputWidth ) return scaleLinearHeight(outputHeight);
if(height == outputHeight) return scaleLinearWidth(outputWidth);
//find fastest scaling method, based on number of interpolation operations required
//magnification usually benefits from two-pass linear interpolation
//minification usually benefits from one-pass bilinear interpolation
unsigned d1wh = ((width * outputWidth ) + (outputWidth * outputHeight)) * 1;
unsigned d1hw = ((height * outputHeight) + (outputWidth * outputHeight)) * 1;
unsigned d2wh = (outputWidth * outputHeight) * 3;
if(d1wh <= d1hw && d1wh <= d2wh) return scaleLinearWidth(outputWidth), scaleLinearHeight(outputHeight);
if(d1hw <= d2wh) return scaleLinearHeight(outputHeight), scaleLinearWidth(outputWidth);
return scaleLinear(outputWidth, outputHeight);
}
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
if(endian == outputEndian && depth == outputDepth && alpha.mask == outputAlphaMask && red.mask == outputRedMask && green.mask == outputGreenMask && blue.mask == outputBlueMask) return;
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
output.allocate(width, height);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
const uint8_t* sp = data + pitch * y;
uint8_t* dp = output.data + output.pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(sp);
sp += stride;
uint64_t a = (color & alpha.mask) >> alpha.shift;
uint64_t r = (color & red.mask) >> red.shift;
uint64_t g = (color & green.mask) >> green.shift;
uint64_t b = (color & blue.mask) >> blue.shift;
a = normalize(a, alpha.depth, output.alpha.depth);
r = normalize(r, red.depth, output.red.depth);
g = normalize(g, green.depth, output.green.depth);
b = normalize(b, blue.depth, output.blue.depth);
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
dp += output.stride;
}
}
operator=(std::move(output));
}
void image::alphaBlend(uint64_t alphaColor) {
uint64_t alphaR = (alphaColor & red.mask ) >> red.shift;
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
uint64_t alphaB = (alphaColor & blue.mask ) >> blue.shift;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t* dp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(dp);
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
uint64_t colorR = (color & red.mask ) >> red.shift;
uint64_t colorG = (color & green.mask) >> green.shift;
uint64_t colorB = (color & blue.mask ) >> blue.shift;
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
colorA = (1 << alpha.depth) - 1;
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
dp += stride;
}
}
}
//protected
uint8_t* image::allocate(unsigned width, unsigned height, unsigned stride) {
//allocate 1x1 larger than requested; so that linear interpolation does not require bounds-checking
unsigned size = width * height * stride;
unsigned padding = width * stride + stride;
uint8_t* data = new uint8_t[size + padding];
memset(data + size, 0x00, padding);
return data;
}
//fixed-point reduction of: a * (1 - x) + b * x
uint64_t image::interpolate1D(int64_t a, int64_t b, uint32_t x) {
return a + (((b - a) * x) >> 32); //a + (b - a) * x
}
//fixed-point reduction of: a * (1 - x) * (1 - y) + b * x * (1 - y) + c * (1 - x) * y + d * x * y
uint64_t image::interpolate2D(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) {
a = a + (((b - a) * x) >> 32); //a + (b - a) * x
c = c + (((d - c) * x) >> 32); //c + (d - c) * x
return a + (((c - a) * y) >> 32); //a + (c - a) * y
}
void image::scaleLinearWidth(unsigned outputWidth) {
uint8_t* outputData = allocate(outputWidth, height, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * y;
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
uint64_t b = read(sp + stride);
sp += stride;
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, xfraction);
uint64_t R = interpolate1D((a & red.mask ) >> red.shift , (b & red.mask ) >> red.shift, xfraction);
uint64_t G = interpolate1D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, xfraction);
uint64_t B = interpolate1D((a & blue.mask ) >> blue.shift , (b & blue.mask ) >> blue.shift, xfraction);
write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift));
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
b = read(sp);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
pitch = outputPitch;
size = height * pitch;
}
void image::scaleLinearHeight(unsigned outputHeight) {
uint8_t* outputData = allocate(width, outputHeight, stride);
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
#pragma omp parallel for
for(unsigned x = 0; x < width; x++) {
uint64_t yfraction = 0;
const uint8_t* sp = data + stride * x;
uint8_t* dp = outputData + stride * x;
uint64_t a = read(sp);
uint64_t b = read(sp + pitch);
sp += pitch;
unsigned y = 0;
while(true) {
while(yfraction < 0x100000000 && y++ < outputHeight) {
uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, yfraction);
uint64_t R = interpolate1D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, yfraction);
uint64_t G = interpolate1D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, yfraction);
uint64_t B = interpolate1D((a & blue.mask ) >> blue.shift, (b & blue.mask ) >> blue.shift, yfraction);
write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift));
dp += pitch;
yfraction += ystride;
}
if(y >= outputHeight) break;
sp += pitch;
a = b;
b = read(sp);
yfraction -= 0x100000000;
}
}
free();
data = outputData;
height = outputHeight;
size = height * pitch;
}
void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
uint64_t yfraction = ystride * y;
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * (yfraction >> 32);
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
uint64_t b = read(sp + stride);
uint64_t c = read(sp + pitch);
uint64_t d = read(sp + pitch + stride);
sp += stride;
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
uint64_t A = interpolate2D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, (c & alpha.mask) >> alpha.shift, (d & alpha.mask) >> alpha.shift, xfraction, yfraction);
uint64_t R = interpolate2D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, (c & red.mask ) >> red.shift, (d & red.mask ) >> red.shift, xfraction, yfraction);
uint64_t G = interpolate2D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, (c & green.mask) >> green.shift, (d & green.mask) >> green.shift, xfraction, yfraction);
uint64_t B = interpolate2D((a & blue.mask ) >> blue.shift, (b & blue.mask ) >> blue.shift, (c & blue.mask ) >> blue.shift, (d & blue.mask ) >> blue.shift, xfraction, yfraction);
write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift));
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
c = d;
b = read(sp);
d = read(sp + pitch);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = height * pitch;
}
void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)width << 32) / outputWidth;
uint64_t ystride = ((uint64_t)height << 32) / outputHeight;
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
uint64_t yfraction = ystride * y;
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * (yfraction >> 32);
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
write(dp, a);
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = read(sp);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = height * pitch;
}
bool image::loadBMP(const string& filename) {
uint32_t* outputData;
unsigned outputWidth, outputHeight;
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
allocate(outputWidth, outputHeight);
const uint32_t* sp = outputData;
uint8_t* dp = data;
for(unsigned y = 0; y < outputHeight; y++) {
for(unsigned x = 0; x < outputWidth; x++) {
uint32_t color = *sp++;
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
dp += stride;
}
}
delete[] outputData;
return true;
}
bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) {
png source;
if(source.decode(pngData, pngSize) == false) return false;
allocate(source.info.width, source.info.height);
const uint8_t* sp = source.data;
uint8_t* dp = data;
auto decode = [&]() -> uint64_t {
uint64_t p, r, g, b, a;
switch(source.info.colorType) {
case 0: //L
r = g = b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 2: //R,G,B
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 3: //P
p = source.readbits(sp);
r = source.info.palette[p][0];
g = source.info.palette[p][1];
b = source.info.palette[p][2];
a = (1 << source.info.bitDepth) - 1;
break;
case 4: //L,A
r = g = b = source.readbits(sp);
a = source.readbits(sp);
break;
case 6: //R,G,B,A
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = source.readbits(sp);
break;
}
a = normalize(a, source.info.bitDepth, alpha.depth);
r = normalize(r, source.info.bitDepth, red.depth);
g = normalize(g, source.info.bitDepth, green.depth);
b = normalize(b, source.info.bitDepth, blue.depth);
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
};
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
write(dp, decode());
dp += stride;
}
}
return true;
}
bool image::loadPNG(const string& filename) {
if(!file::exists(filename)) return false;
auto buffer = file::read(filename);
return loadPNG(buffer.data(), buffer.size());
}
}
#include <nall/image/base.hpp>
#include <nall/image/static.hpp>
#include <nall/image/core.hpp>
#include <nall/image/load.hpp>
#include <nall/image/interpolation.hpp>
#include <nall/image/fill.hpp>
#include <nall/image/scale.hpp>
#include <nall/image/blend.hpp>
#include <nall/image/utility.hpp>
#endif

121
nall/image/base.hpp Normal file
View File

@ -0,0 +1,121 @@
#ifndef NALL_IMAGE_BASE_HPP
#define NALL_IMAGE_BASE_HPP
namespace nall {
struct image {
uint8_t* data = nullptr;
unsigned width = 0;
unsigned height = 0;
unsigned pitch = 0;
unsigned size = 0;
bool endian = 0; //0 = lsb, 1 = msb
unsigned depth = 32;
unsigned stride = 4;
struct channel {
uint64_t mask;
unsigned depth;
unsigned shift;
inline bool operator==(const channel& source) {
return mask == source.mask && depth == source.depth && shift == source.shift;
}
inline bool operator!=(const channel& source) {
return !operator==(source);
}
};
channel alpha = {255u << 24, 8u, 24u};
channel red = {255u << 16, 8u, 16u};
channel green = {255u << 8, 8u, 8u};
channel blue = {255u << 0, 8u, 0u};
enum class blend : unsigned {
add,
sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha)
sourceColor, //color = sourceColor
targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha)
targetColor, //color = targetColor
};
//static.hpp
static inline unsigned bitDepth(uint64_t color);
static inline unsigned bitShift(uint64_t color);
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
//core.hpp
inline bool operator==(const image& source);
inline bool operator!=(const image& source);
inline image& operator=(const image& source);
inline image& operator=(image&& source);
inline image(const image& source);
inline image(image&& source);
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image(const string& filename);
inline image(const uint8_t* data, unsigned size);
inline image();
inline ~image();
inline uint64_t read(const uint8_t* data) const;
inline void write(uint8_t* data, uint64_t value) const;
inline void free();
inline bool empty() const;
inline bool load(const string& filename);
inline void allocate(unsigned width, unsigned height);
//fill.hpp
inline void fill(uint64_t color = 0);
inline void linearGradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d);
inline void horizontalGradient(uint64_t a, uint64_t b);
inline void verticalGradient(uint64_t a, uint64_t b);
inline void radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY);
inline void radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY);
inline void radialGradient(uint64_t a, uint64_t b);
//scale.hpp
inline void scale(unsigned width, unsigned height, bool linear = true);
//blend.hpp
inline void impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height);
//utility.hpp
inline bool crop(unsigned x, unsigned y, unsigned width, unsigned height);
inline void alphaBlend(uint64_t alphaColor);
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
protected:
//core.hpp
inline uint8_t* allocate(unsigned width, unsigned height, unsigned stride);
//scale.hpp
inline void scaleLinearWidth(unsigned width);
inline void scaleLinearHeight(unsigned height);
inline void scaleLinear(unsigned width, unsigned height);
inline void scaleNearest(unsigned width, unsigned height);
//load.hpp
inline bool loadBMP(const string& filename);
inline bool loadPNG(const string& filename);
inline bool loadPNG(const uint8_t* data, unsigned size);
//interpolation.hpp
alwaysinline void isplit(uint64_t* component, uint64_t color);
alwaysinline uint64_t imerge(const uint64_t* component);
alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, double x);
alwaysinline uint64_t interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y);
alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, uint32_t x);
alwaysinline uint64_t interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y);
inline uint64_t interpolate4f(uint64_t a, uint64_t b, double x);
inline uint64_t interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y);
inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint32_t x);
inline uint64_t interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y);
};
}
#endif

74
nall/image/blend.hpp Normal file
View File

@ -0,0 +1,74 @@
#ifndef NALL_IMAGE_BLEND_HPP
#define NALL_IMAGE_BLEND_HPP
namespace nall {
void image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) {
source.transform(endian, depth, alpha.mask, red.mask, green.mask, blue.mask);
for(unsigned y = 0; y < sourceHeight; y++) {
const uint8_t* sp = source.data + source.pitch * (sourceY + y) + source.stride * sourceX;
uint8_t* dp = data + pitch * (targetY + y) + stride * targetX;
for(unsigned x = 0; x < sourceWidth; x++) {
uint64_t sourceColor = source.read(sp);
uint64_t targetColor = read(dp);
int64_t sa = (sourceColor & alpha.mask) >> alpha.shift;
int64_t sr = (sourceColor & red.mask ) >> red.shift;
int64_t sg = (sourceColor & green.mask) >> green.shift;
int64_t sb = (sourceColor & blue.mask ) >> blue.shift;
int64_t da = (targetColor & alpha.mask) >> alpha.shift;
int64_t dr = (targetColor & red.mask ) >> red.shift;
int64_t dg = (targetColor & green.mask) >> green.shift;
int64_t db = (targetColor & blue.mask ) >> blue.shift;
uint64_t a, r, g, b;
switch(mode) {
case blend::add:
a = max(sa, da);
r = min(red.mask >> red.shift, ((sr * sa) >> alpha.depth) + ((dr * da) >> alpha.depth));
g = min(green.mask >> green.shift, ((sg * sa) >> alpha.depth) + ((dg * da) >> alpha.depth));
b = min(blue.mask >> blue.shift, ((sb * sa) >> alpha.depth) + ((db * da) >> alpha.depth));
break;
case blend::sourceAlpha:
a = max(sa, da);
r = dr + (((sr - dr) * sa) >> alpha.depth);
g = dg + (((sg - dg) * sa) >> alpha.depth);
b = db + (((sb - db) * sa) >> alpha.depth);
break;
case blend::sourceColor:
a = sa;
r = sr;
g = sg;
b = sb;
break;
case blend::targetAlpha:
a = max(sa, da);
r = sr + (((dr - sr) * da) >> alpha.depth);
g = sg + (((dg - sg) * da) >> alpha.depth);
b = sb + (((db - sb) * da) >> alpha.depth);
break;
case blend::targetColor:
a = da;
r = dr;
g = dg;
b = db;
break;
}
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
sp += source.stride;
dp += stride;
}
}
}
}
#endif

164
nall/image/core.hpp Normal file
View File

@ -0,0 +1,164 @@
#ifndef NALL_IMAGE_CORE_HPP
#define NALL_IMAGE_CORE_HPP
namespace nall {
bool image::operator==(const image& source) {
if(width != source.width) return false;
if(height != source.height) return false;
if(pitch != source.pitch) return false;
if(endian != source.endian) return false;
if(stride != source.stride) return false;
if(alpha != source.alpha) return false;
if(red != source.red) return false;
if(green != source.green) return false;
if(blue != source.blue) return false;
return memcmp(data, source.data, width * height * stride) == 0;
}
bool image::operator!=(const image& source) {
return !operator==(source);
}
image& image::operator=(const image& source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
size = source.size;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = allocate(width, height, stride);
memcpy(data, source.data, source.size);
return *this;
}
image& image::operator=(image&& source) {
free();
width = source.width;
height = source.height;
pitch = source.pitch;
size = source.size;
endian = source.endian;
stride = source.stride;
alpha = source.alpha;
red = source.red;
green = source.green;
blue = source.blue;
data = source.data;
source.data = nullptr;
return *this;
}
image::image(const image& source) {
operator=(source);
}
image::image(image&& source) {
operator=(std::forward<image>(source));
}
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) {
this->endian = endian;
this->depth = depth;
this->stride = (depth / 8) + ((depth & 7) > 0);
alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)};
red = {redMask, bitDepth(redMask), bitShift(redMask )};
green = {greenMask, bitDepth(greenMask), bitShift(greenMask)};
blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )};
}
image::image(const string& filename) {
load(filename);
}
image::image(const uint8_t* data, unsigned size) {
loadPNG(data, size);
}
image::image() {
}
image::~image() {
free();
}
uint64_t image::read(const uint8_t* data) const {
uint64_t result = 0;
if(endian == 0) {
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
} else {
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
}
return result;
}
void image::write(uint8_t* data, uint64_t value) const {
if(endian == 0) {
for(signed n = 0; n < stride; n++) {
data[n] = value;
value >>= 8;
}
} else {
for(signed n = stride - 1; n >= 0; n--) {
data[n] = value;
value >>= 8;
}
}
}
void image::free() {
if(data) delete[] data;
data = nullptr;
}
bool image::empty() const {
if(data == nullptr) return true;
if(width == 0 || height == 0) return true;
return false;
}
bool image::load(const string& filename) {
if(loadBMP(filename) == true) return true;
if(loadPNG(filename) == true) return true;
return false;
}
void image::allocate(unsigned width, unsigned height) {
if(data != nullptr && this->width == width && this->height == height) return;
free();
data = allocate(width, height, stride);
pitch = width * stride;
size = height * pitch;
this->width = width;
this->height = height;
}
uint8_t* image::allocate(unsigned width, unsigned height, unsigned stride) {
//allocate 1x1 larger than requested; so that linear interpolation does not require bounds-checking
unsigned size = width * height * stride;
unsigned padding = width * stride + stride;
uint8_t* data = new uint8_t[size + padding];
memset(data + size, 0x00, padding);
return data;
}
}
#endif

62
nall/image/fill.hpp Normal file
View File

@ -0,0 +1,62 @@
#ifndef NALL_IMAGE_FILL_HPP
#define NALL_IMAGE_FILL_HPP
namespace nall {
void image::fill(uint64_t color) {
uint8_t* dp = data;
for(unsigned y = 0; y < height; y++) {
uint8_t* dp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
write(dp, color);
dp += stride;
}
}
}
void image::linearGradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) {
for(unsigned y = 0; y < height; y++) {
uint8_t* dp = data + pitch * y;
double muY = (double)y / (double)height;
for(unsigned x = 0; x < width; x++) {
double muX = (double)x / (double)width;
write(dp, interpolate4f(a, b, c, d, muX, muY));
dp += stride;
}
}
}
void image::horizontalGradient(uint64_t a, uint64_t b) {
linearGradient(a, b, a, b);
}
void image::verticalGradient(uint64_t a, uint64_t b) {
linearGradient(a, a, b, b);
}
void image::radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) {
if(radiusX < 1) radiusX = width >> 1;
if(radiusY < 1) radiusY = height >> 1;
for(signed y = 0; y < height; y++) {
uint8_t* dp = data + pitch * y;
double py = max(-radiusY, min(+radiusY, y - centerY)) * 1.0 / radiusY;
for(signed x = 0; x < width; x++) {
double px = max(-radiusX, min(+radiusX, x - centerX)) * 1.0 / radiusX;
double mu = min(1.0, sqrt(px * px + py * py));
write(dp, interpolate4f(a, b, mu));
dp += stride;
}
}
}
void image::radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY) {
radialGradient(a, b, radiusX, radiusY, width >> 1, height >> 1);
}
void image::radialGradient(uint64_t a, uint64_t b) {
radialGradient(a, b, width >> 1, height >> 1, width >> 1, height >> 1);
}
}
#endif

View File

@ -0,0 +1,65 @@
#ifndef NALL_IMAGE_INTERPOLATION_HPP
#define NALL_IMAGE_INTERPOLATION_HPP
namespace nall {
void image::isplit(uint64_t* c, uint64_t color) {
c[0] = (color & alpha.mask) >> alpha.shift;
c[1] = (color & red.mask ) >> red.shift;
c[2] = (color & green.mask) >> green.shift;
c[3] = (color & blue.mask ) >> blue.shift;
}
uint64_t image::imerge(const uint64_t* c) {
return c[0] << alpha.shift | c[1] << red.shift | c[2] << green.shift | c[3] << blue.shift;
}
uint64_t image::interpolate1f(uint64_t a, uint64_t b, double x) {
return a * (1.0 - x) + b * x;
}
uint64_t image::interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) {
return a * (1.0 - x) * (1.0 - y) + b * x * (1.0 - y) + c * (1.0 - x) * y + d * x * y;
}
uint64_t image::interpolate1i(int64_t a, int64_t b, uint32_t x) {
return a + (((b - a) * x) >> 32); //a + (b - a) * x
}
uint64_t image::interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) {
a = a + (((b - a) * x) >> 32); //a + (b - a) * x
c = c + (((d - c) * x) >> 32); //c + (d - c) * x
return a + (((c - a) * y) >> 32); //a + (c - a) * y
}
uint64_t image::interpolate4f(uint64_t a, uint64_t b, double x) {
uint64_t o[4], pa[4], pb[4];
isplit(pa, a), isplit(pb, b);
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], x);
return imerge(o);
}
uint64_t image::interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) {
uint64_t o[4], pa[4], pb[4], pc[4], pd[4];
isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d);
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1f(pa[n], pb[n], pc[n], pd[n], x, y);
return imerge(o);
}
uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint32_t x) {
uint64_t o[4], pa[4], pb[4];
isplit(pa, a), isplit(pb, b);
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], x);
return imerge(o);
}
uint64_t image::interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) {
uint64_t o[4], pa[4], pb[4], pc[4], pd[4];
isplit(pa, a), isplit(pb, b), isplit(pc, c), isplit(pd, d);
for(unsigned n = 0; n < 4; n++) o[n] = interpolate1i(pa[n], pb[n], pc[n], pd[n], x, y);
return imerge(o);
}
}
#endif

98
nall/image/load.hpp Normal file
View File

@ -0,0 +1,98 @@
#ifndef NALL_IMAGE_LOAD_HPP
#define NALL_IMAGE_LOAD_HPP
namespace nall {
bool image::loadBMP(const string& filename) {
uint32_t* outputData;
unsigned outputWidth, outputHeight;
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
allocate(outputWidth, outputHeight);
const uint32_t* sp = outputData;
uint8_t* dp = data;
for(unsigned y = 0; y < outputHeight; y++) {
for(unsigned x = 0; x < outputWidth; x++) {
uint32_t color = *sp++;
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift));
dp += stride;
}
}
delete[] outputData;
return true;
}
bool image::loadPNG(const string& filename) {
if(!file::exists(filename)) return false;
auto buffer = file::read(filename);
return loadPNG(buffer.data(), buffer.size());
}
bool image::loadPNG(const uint8_t* pngData, unsigned pngSize) {
png source;
if(source.decode(pngData, pngSize) == false) return false;
allocate(source.info.width, source.info.height);
const uint8_t* sp = source.data;
uint8_t* dp = data;
auto decode = [&]() -> uint64_t {
uint64_t p, r, g, b, a;
switch(source.info.colorType) {
case 0: //L
r = g = b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 2: //R,G,B
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = (1 << source.info.bitDepth) - 1;
break;
case 3: //P
p = source.readbits(sp);
r = source.info.palette[p][0];
g = source.info.palette[p][1];
b = source.info.palette[p][2];
a = (1 << source.info.bitDepth) - 1;
break;
case 4: //L,A
r = g = b = source.readbits(sp);
a = source.readbits(sp);
break;
case 6: //R,G,B,A
r = source.readbits(sp);
g = source.readbits(sp);
b = source.readbits(sp);
a = source.readbits(sp);
break;
}
a = normalize(a, source.info.bitDepth, alpha.depth);
r = normalize(r, source.info.bitDepth, red.depth);
g = normalize(g, source.info.bitDepth, green.depth);
b = normalize(b, source.info.bitDepth, blue.depth);
return (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift);
};
for(unsigned y = 0; y < height; y++) {
for(unsigned x = 0; x < width; x++) {
write(dp, decode());
dp += stride;
}
}
return true;
}
}
#endif

190
nall/image/scale.hpp Normal file
View File

@ -0,0 +1,190 @@
#ifndef NALL_IMAGE_SCALE_HPP
#define NALL_IMAGE_SCALE_HPP
namespace nall {
void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) {
if(width == outputWidth && height == outputHeight) return; //no scaling necessary
if(linear == false) return scaleNearest(outputWidth, outputHeight);
if(width == outputWidth ) return scaleLinearHeight(outputHeight);
if(height == outputHeight) return scaleLinearWidth(outputWidth);
//find fastest scaling method, based on number of interpolation operations required
//magnification usually benefits from two-pass linear interpolation
//minification usually benefits from one-pass bilinear interpolation
unsigned d1wh = ((width * outputWidth ) + (outputWidth * outputHeight)) * 1;
unsigned d1hw = ((height * outputHeight) + (outputWidth * outputHeight)) * 1;
unsigned d2wh = (outputWidth * outputHeight) * 3;
if(d1wh <= d1hw && d1wh <= d2wh) return scaleLinearWidth(outputWidth), scaleLinearHeight(outputHeight);
if(d1hw <= d2wh) return scaleLinearHeight(outputHeight), scaleLinearWidth(outputWidth);
return scaleLinear(outputWidth, outputHeight);
}
void image::scaleLinearWidth(unsigned outputWidth) {
uint8_t* outputData = allocate(outputWidth, height, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * y;
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
uint64_t b = read(sp + stride);
sp += stride;
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
write(dp, interpolate4i(a, b, xfraction));
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
b = read(sp);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
pitch = outputPitch;
size = height * pitch;
}
void image::scaleLinearHeight(unsigned outputHeight) {
uint8_t* outputData = allocate(width, outputHeight, stride);
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
#pragma omp parallel for
for(unsigned x = 0; x < width; x++) {
uint64_t yfraction = 0;
const uint8_t* sp = data + stride * x;
uint8_t* dp = outputData + stride * x;
uint64_t a = read(sp);
uint64_t b = read(sp + pitch);
sp += pitch;
unsigned y = 0;
while(true) {
while(yfraction < 0x100000000 && y++ < outputHeight) {
write(dp, interpolate4i(a, b, yfraction));
dp += pitch;
yfraction += ystride;
}
if(y >= outputHeight) break;
sp += pitch;
a = b;
b = read(sp);
yfraction -= 0x100000000;
}
}
free();
data = outputData;
height = outputHeight;
size = height * pitch;
}
void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) {
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1);
uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1);
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
uint64_t yfraction = ystride * y;
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * (yfraction >> 32);
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
uint64_t b = read(sp + stride);
uint64_t c = read(sp + pitch);
uint64_t d = read(sp + pitch + stride);
sp += stride;
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
write(dp, interpolate4i(a, b, c, d, xfraction, yfraction));
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = b;
c = d;
b = read(sp);
d = read(sp + pitch);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = height * pitch;
}
void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) {
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
uint64_t xstride = ((uint64_t)width << 32) / outputWidth;
uint64_t ystride = ((uint64_t)height << 32) / outputHeight;
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
uint64_t yfraction = ystride * y;
uint64_t xfraction = 0;
const uint8_t* sp = data + pitch * (yfraction >> 32);
uint8_t* dp = outputData + outputPitch * y;
uint64_t a = read(sp);
unsigned x = 0;
while(true) {
while(xfraction < 0x100000000 && x++ < outputWidth) {
write(dp, a);
dp += stride;
xfraction += xstride;
}
if(x >= outputWidth) break;
sp += stride;
a = read(sp);
xfraction -= 0x100000000;
}
}
free();
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = height * pitch;
}
}
#endif

31
nall/image/static.hpp Normal file
View File

@ -0,0 +1,31 @@
#ifndef NALL_IMAGE_STATIC_HPP
#define NALL_IMAGE_STATIC_HPP
namespace nall {
unsigned image::bitDepth(uint64_t color) {
unsigned depth = 0;
if(color) while((color & 1) == 0) color >>= 1;
while((color & 1) == 1) { color >>= 1; depth++; }
return depth;
}
unsigned image::bitShift(uint64_t color) {
unsigned shift = 0;
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
return shift;
}
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
if(sourceDepth == 0 || targetDepth == 0) return 0;
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;
}
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
return color;
}
}
#endif

95
nall/image/utility.hpp Normal file
View File

@ -0,0 +1,95 @@
#ifndef NALL_IMAGE_UTILITY_HPP
#define NALL_IMAGE_UTILITY_HPP
namespace nall {
bool image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) {
if(outputX + outputWidth > width) return false;
if(outputY + outputHeight > height) return false;
uint8_t* outputData = allocate(outputWidth, outputHeight, stride);
unsigned outputPitch = outputWidth * stride;
#pragma omp parallel for
for(unsigned y = 0; y < outputHeight; y++) {
const uint8_t* sp = data + pitch * (outputY + y) + stride * outputX;
uint8_t* dp = outputData + outputPitch * y;
for(unsigned x = 0; x < outputWidth; x++) {
write(dp, read(sp));
sp += stride;
dp += stride;
}
}
delete[] data;
data = outputData;
width = outputWidth;
height = outputHeight;
pitch = outputPitch;
size = width * pitch;
return true;
}
void image::alphaBlend(uint64_t alphaColor) {
uint64_t alphaR = (alphaColor & red.mask ) >> red.shift;
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
uint64_t alphaB = (alphaColor & blue.mask ) >> blue.shift;
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
uint8_t* dp = data + pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(dp);
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
uint64_t colorR = (color & red.mask ) >> red.shift;
uint64_t colorG = (color & green.mask) >> green.shift;
uint64_t colorB = (color & blue.mask ) >> blue.shift;
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
colorA = (1 << alpha.depth) - 1;
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
write(dp, (colorA << alpha.shift) | (colorR << red.shift) | (colorG << green.shift) | (colorB << blue.shift));
dp += stride;
}
}
}
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
if(endian == outputEndian && depth == outputDepth && alpha.mask == outputAlphaMask && red.mask == outputRedMask && green.mask == outputGreenMask && blue.mask == outputBlueMask) return;
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
output.allocate(width, height);
#pragma omp parallel for
for(unsigned y = 0; y < height; y++) {
const uint8_t* sp = data + pitch * y;
uint8_t* dp = output.data + output.pitch * y;
for(unsigned x = 0; x < width; x++) {
uint64_t color = read(sp);
sp += stride;
uint64_t a = (color & alpha.mask) >> alpha.shift;
uint64_t r = (color & red.mask) >> red.shift;
uint64_t g = (color & green.mask) >> green.shift;
uint64_t b = (color & blue.mask) >> blue.shift;
a = normalize(a, alpha.depth, output.alpha.depth);
r = normalize(r, red.depth, output.red.depth);
g = normalize(g, green.depth, output.green.depth);
b = normalize(b, blue.depth, output.blue.depth);
output.write(dp, (a << output.alpha.shift) | (r << output.red.shift) | (g << output.green.shift) | (b << output.blue.shift));
dp += output.stride;
}
}
operator=(std::move(output));
}
}
#endif

View File

@ -174,7 +174,7 @@ void pCanvas::rasterize() {
if(canvas.state.mode == Canvas::Mode::Gradient) {
nall::image image;
image.allocate(width, height);
image.gradient(
image.linearGradient(
canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
);
memcpy(target, image.data, image.size);

View File

@ -131,7 +131,7 @@ void pCanvas::rasterize() {
if(canvas.state.mode == Canvas::Mode::Gradient) {
nall::image image;
image.allocate(width, height);
image.gradient(
image.linearGradient(
canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
);
memcpy(buffer, image.data, image.size);

View File

@ -67,7 +67,7 @@ void pCanvas::rasterize() {
if(canvas.state.mode == Canvas::Mode::Gradient) {
nall::image image;
image.allocate(width, height);
image.gradient(
image.linearGradient(
canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
);
memcpy(surface->bits(), image.data, image.size);

View File

@ -168,7 +168,7 @@ void pCanvas::rasterize() {
if(canvas.state.mode == Canvas::Mode::Gradient) {
nall::image image;
image.allocate(width, height);
image.gradient(
image.linearGradient(
canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb()
);
memcpy(surface, image.data, image.size);

View File

@ -66,8 +66,8 @@ struct pVideoGLX : OpenGL {
if(depth > DefaultDepth(display, screen)) return false;
switch(depth) {
case 24: format = GL_RGBA8; break;
case 30: format = GL_RGB10_A2; break;
case 24: inputFormat = GL_RGBA8; break;
case 30: inputFormat = GL_RGB10_A2; break;
default: return false;
}

View File

@ -4,7 +4,7 @@ void OpenGL::shader(const char* pathname) {
settings.reset();
format = GL_RGBA8;
format = inputFormat;
filter = GL_LINEAR;
wrap = GL_CLAMP_TO_BORDER;
absoluteWidth = 0, absoluteHeight = 0;

View File

@ -66,6 +66,7 @@ struct OpenGLProgram : OpenGLSurface {
struct OpenGL : OpenGLProgram {
vector<OpenGLProgram> programs;
vector<OpenGLTexture> history;
GLuint inputFormat = GL_RGBA8;
unsigned outputWidth = 0;
unsigned outputHeight = 0;
struct Setting {

View File

@ -5,106 +5,28 @@ namespace SuperFamicom {
Cheat cheat;
bool Cheat::enabled() const {
return system_enabled;
void Cheat::reset() {
codes.reset();
}
void Cheat::enable(bool state) {
system_enabled = state;
cheat_enabled = system_enabled && code_enabled;
void Cheat::append(unsigned addr, unsigned data) {
codes.append({addr, Unused, data});
}
void Cheat::synchronize() {
memset(override, 0x00, 16 * 1024 * 1024);
code_enabled = size() > 0;
void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
codes.append({addr, comp, data});
}
for(unsigned i = 0; i < size(); i++) {
const CheatCode &code = operator[](i);
optional<unsigned> Cheat::find(unsigned addr, unsigned comp) {
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
unsigned addr = mirror(code.addr);
override[addr] = true;
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
unsigned mirroraddr;
for(unsigned x = 0; x <= 0x3f; x++) {
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
override[mirroraddr] = true;
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
override[mirroraddr] = true;
for(auto& code : codes) {
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
return {true, code.data};
}
}
}
cheat_enabled = system_enabled && code_enabled;
}
uint8 Cheat::read(unsigned addr) const {
addr = mirror(addr);
for(unsigned i = 0; i < size(); i++) {
const CheatCode& code = operator[](i);
if(addr == mirror(code.addr)) {
return code.data;
}
}
return 0x00;
}
void Cheat::init() {
memset(override, 0x00, 16 * 1024 * 1024);
}
Cheat::Cheat() {
override = new uint8[16 * 1024 * 1024];
system_enabled = true;
}
Cheat::~Cheat() {
delete[] override;
}
bool Cheat::decode(string code, unsigned& addr, unsigned& data) {
string t = code;
t.lower();
#define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f'))
if(t.match("??????:??")) {
//Direct
t = {substr(t, 0, 6), substr(t, 7, 2)};
for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input
unsigned r = hex(t);
addr = r >> 8;
data = r & 0xff;
return true;
}
if(t.match(R"(????-????)")) {
//Game Genie
t = {substr(t, 0, 4), substr(t, 5, 4)};
for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input
t.transform("df4709156bc8a23e", "0123456789abcdef");
unsigned r = hex(t);
static unsigned bits[] = {13, 12, 11, 10, 5, 4, 3, 2, 23, 22, 21, 20, 1, 0, 15, 14, 19, 18, 17, 16, 9, 8, 7, 6};
addr = 0;
for(unsigned n = 0; n < 24; n++) addr |= r & (1 << bits[n]) ? 0x800000 >> n : 0;
data = r >> 24;
return true;
} else {
return false;
}
#undef ischr
}
unsigned Cheat::mirror(unsigned addr) const {
//$00-3f|80-bf:0000-1fff -> $7e:0000-1fff
if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff));
return addr;
}
}

View File

@ -1,27 +1,17 @@
struct CheatCode {
struct Cheat {
struct Code {
unsigned addr;
unsigned comp;
unsigned data;
};
};
vector<Code> codes;
enum : unsigned { Unused = ~0u };
struct Cheat : public vector<CheatCode> {
uint8* override;
bool enabled() const;
void enable(bool);
void synchronize();
uint8 read(unsigned) const;
void init();
Cheat();
~Cheat();
static bool decode(string, unsigned&, unsigned&);
private:
bool system_enabled;
bool code_enabled;
bool cheat_enabled;
unsigned mirror(unsigned) const;
alwaysinline bool enable() const { return codes.size() > 0; }
void reset();
void append(unsigned addr, unsigned data);
void append(unsigned addr, unsigned comp, unsigned data);
optional<unsigned> find(unsigned addr, unsigned comp);
};
extern Cheat cheat;

View File

@ -310,32 +310,31 @@ bool Interface::unserialize(serializer& s) {
}
void Interface::cheatSet(const lstring& list) {
cheat.reset();
//Super Game Boy
if(cartridge.has_gb_slot()) {
GameBoy::cheat.reset();
for(auto& code : list) {
lstring codelist = code.split("+");
for(auto& part : codelist) {
unsigned addr, data, comp;
part.trim();
if(GameBoy::Cheat::decode(part, addr, data, comp)) GameBoy::cheat.append({addr, data, comp});
for(auto& codeset : list) {
lstring codes = codeset.split("+");
for(auto& code : codes) {
lstring part = code.split("/");
if(part.size() == 2) GameBoy::cheat.append(hex(part[0]), hex(part[1]));
if(part.size() == 3) GameBoy::cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
}
}
GameBoy::cheat.synchronize();
return;
}
//Super Famicom, Broadcast Satellaview, Sufami Turbo
cheat.reset();
for(auto& code : list) {
lstring codelist = code.split("+");
for(auto& part : codelist) {
unsigned addr, data;
part.trim();
if(Cheat::decode(part, addr, data)) cheat.append({addr, data});
for(auto& codeset : list) {
lstring codes = codeset.split("+");
for(auto& code : codes) {
lstring part = code.split("/");
if(part.size() == 2) cheat.append(hex(part[0]), hex(part[1]));
if(part.size() == 3) cheat.append(hex(part[0]), hex(part[1]), hex(part[2]));
}
}
cheat.synchronize();
}
void Interface::paletteUpdate(PaletteMode mode) {

View File

@ -84,8 +84,13 @@ unsigned Bus::reduce(unsigned addr, unsigned mask) {
}
uint8 Bus::read(unsigned addr) {
if(cheat.override[addr]) return cheat.read(addr);
return reader[lookup[addr]](target[addr]);
uint8 data = reader[lookup[addr]](target[addr]);
if(cheat.enable()) {
if(auto result = cheat.find(addr, data)) return result();
}
return data;
}
void Bus::write(unsigned addr, uint8 data) {

View File

@ -140,7 +140,6 @@ void System::load() {
if(cartridge.has_st_slots()) sufamiturboA.load(), sufamiturboB.load();
serialize_init();
cheat.init();
}
void System::unload() {

View File

@ -25,7 +25,7 @@ else ifeq ($(platform),macosx)
else ifeq ($(platform),linux)
ruby := video.glx video.xv video.xshm video.sdl
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.udev
ruby += input.udev input.sdl input.x
else ifeq ($(platform),bsd)
ruby := video.glx
ruby += audio.openal audio.oss

View File

@ -4,7 +4,6 @@
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/dsp.hpp>
#include <nall/hid.hpp>
#include <nall/invoke.hpp>
#include <nall/map.hpp>
#include <nall/stream/file.hpp>