Update to v088r03 release.

byuu says:

static vector<uint8_t> file::read(const string &filename); replaces:
static bool file::read(const string &filename, uint8_t *&data, unsigned
&size); This allows automatic deletion of the underlying data.

Added vectorstream, which is obviously a vector<uint8_t> wrapper for
a data stream.  Plan is for all data accesses inside my emulation cores
to take stream objects, especially MSU1.  This lets you feed the core
anything: memorystream, filestream, zipstream, gzipstream, httpstream,
etc.  There will still be exceptions for link and serial, those need
actual library files on disk. But those aren't official hardware devices
anyway.

So to help with speed a bit, I'm rethinking the video rendering path.

Previous system:
- core outputs system-native samples (SNES = 19-bit LRGB, NES = 9-bit
  emphasis+palette, DMG = 2-bit grayscale, etc.)
- interfaceSystem transforms samples to 30-bit via lookup table inside
  the emulation core
- interfaceSystem masks off overscan areas, if enabled
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI transforms 30-bit video to native display depth (24-bit or
  30-bit), and applies color-adjustments (gamma, etc) at the same time

New system:
- all cores now generate an internal palette, and call
  Interface::videoColor(uint32_t source, uint16_t red, uint16_t green,
  uint16_t blue) to get native display color post-adjusted (gamma, etc
  applied already.)
- all cores output to uint32_t* buffer now (output video.palette[color]
  instead of just color)
- interfaceUI runs filter to produce new target buffer, if enabled
- interfaceUI memcpy()'s buffer to the video card

videoColor() is pretty neat. source is the raw pixel (as per the
old-format, 19-bit SNES, 9-bit NES, etc), and you can create a color
from that if you really want to. Or return that value to get a buffer
just like v088 and below.  red, green, blue are 16-bits per channel,
because why the hell not, right? Just lop off all the bits you don't
want. If you have more bits on your display than that, fuck you :P

The last step is extremely difficult to avoid. Video cards can and do
have pitches that differ from the width of the texture.  Trying to make
the core account for this would be really awful. And even if we did
that, the emulation routine would need to write directly to a video card
RAM buffer.  Some APIs require you to lock the video buffer while
writing, so this would leave the video buffer locked for a long time.
Probably not catastrophic, but still awful.  And lastly, if the
emulation core tried writing directly to the display texture, software
filters would no longer be possible (unless you -really- jump through
hooks and divert to a memory buffer when a filter is enabled, but ...
fuck.)

Anyway, the point of all that work was to eliminate an extra video copy,
and the need for a really painful 30-bit to 24-bit conversion (three
shifts, three masks, three array indexes.) So this basically reverts us,
performance-wise, to where we were pre-30 bit support.

[...]

The downside to this is that we're going to need a filter for each
output depth. Since the array type is uint32_t*, and I don't intend to
support higher or lower depths, we really only need 24+30-bit versions
of each filter.  Kinda shitty, but oh well.
This commit is contained in:
Tim Allen 2012-04-27 22:12:53 +10:00
parent bba597fc6f
commit 616372e96b
68 changed files with 516 additions and 861 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "088.02";
static const char Version[] = "088.03";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
@ -19,6 +19,7 @@ static const char Version[] = "088.02";
#include <nall/utility.hpp>
#include <nall/varint.hpp>
#include <nall/vector.hpp>
#include <nall/stream/memory.hpp>
using namespace nall;
//debugging function hook:
@ -39,8 +40,8 @@ template<typename R, typename... P> struct hook<R (P...)> {
hook(const hook &hook) { callback = hook.callback; }
hook(void *function) { callback = function; }
hook(R (*function)(P...)) { callback = function; }
template<typename C> hook(R (C::*function)(P...), C *object) { callback = { function, object }; }
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = { function, object }; }
template<typename C> hook(R (C::*function)(P...), C *object) { callback = {function, object}; }
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = {function, object}; }
template<typename L> hook(const L& function) { callback = function; }
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }

View File

@ -14,10 +14,10 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
void Cartridge::load(System::Revision revision, const string &markup, const stream &memory) {
romsize = memory.size() ? memory.size() : 32768u;
romdata = allocate<uint8>(romsize, 0xff);
memory.read(romdata, memory.size());
information.markup = markup;
information.mapper = Mapper::Unknown;
@ -49,14 +49,14 @@ void Cartridge::load(System::Revision revision, const string &markup, const uint
information.battery = document["cartridge"]["ram"]["nonvolatile"].data == "true";
switch(information.mapper) { default:
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
case Mapper::MBC0: mapper = &mbc0; break;
case Mapper::MBC1: mapper = &mbc1; break;
case Mapper::MBC2: mapper = &mbc2; break;
case Mapper::MBC3: mapper = &mbc3; break;
case Mapper::MBC5: mapper = &mbc5; break;
case Mapper::MMM01: mapper = &mmm01; break;
case Mapper::HuC1: mapper = &huc1; break;
case Mapper::HuC3: mapper = &huc3; break;
}
ramdata = new uint8_t[ramsize = information.ramsize]();

View File

@ -45,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper;
bool bootrom_enable;
void load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
void load(System::Revision revision, const string &markup, const stream &memory);
void unload();
uint8 rom_read(unsigned addr);

View File

@ -10,7 +10,12 @@ void Interface::lcdScanline() {
void Interface::joypWrite(bool p15, bool p14) {
}
void Interface::videoRefresh(const uint16_t *data) {
uint32_t Interface::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {

View File

@ -2,7 +2,8 @@ struct Interface {
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual void videoRefresh(const uint16_t *data);
virtual uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);

View File

@ -12,8 +12,8 @@ void PPU::cgb_render() {
if(status.ob_enable) cgb_render_ob();
}
uint16 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
uint32 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
interface->lcdScanline();
}

View File

@ -12,8 +12,8 @@ void PPU::dmg_render() {
if(status.ob_enable) dmg_render_ob();
}
uint16 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
uint32 *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
interface->lcdScanline();
}

View File

@ -49,7 +49,7 @@ struct PPU : Thread, MMIO {
uint8 obpi;
} status;
uint16 screen[160 * 144];
uint32 screen[160 * 144];
uint16 line[160];
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
uint8 origin[160];

View File

@ -5,20 +5,34 @@ namespace GameBoy {
Video video;
unsigned Video::palette_dmg(unsigned color) const {
unsigned R = monochrome[color][0] * 1023.0;
unsigned G = monochrome[color][1] * 1023.0;
unsigned B = monochrome[color][2] * 1023.0;
void Video::generate_palette() {
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
}
return (R << 20) + (G << 10) + (B << 0);
Video::Video() {
palette = new unsigned[1 << 15]();
}
Video::~Video() {
delete[] palette;
}
unsigned Video::palette_dmg(unsigned color) const {
unsigned R = monochrome[color][0] * 65535.0;
unsigned G = monochrome[color][1] * 65535.0;
unsigned B = monochrome[color][2] * 65535.0;
return interface->videoColor(color, R, G, B);
}
unsigned Video::palette_sgb(unsigned color) const {
unsigned R = (3 - color) * 341;
unsigned G = (3 - color) * 341;
unsigned B = (3 - color) * 341;
unsigned R = (3 - color) * 21845;
unsigned G = (3 - color) * 21845;
unsigned B = (3 - color) * 21845;
return (R << 20) + (G << 10) + (B << 0);
return interface->videoColor(color, R, G, B);
}
unsigned Video::palette_cgb(unsigned color) const {
@ -34,42 +48,11 @@ unsigned Video::palette_cgb(unsigned color) const {
G = min(960, G);
B = min(960, B);
return (R << 20) + (G << 10) + (B << 0);
}
R = R << 6 | R >> 4;
G = G << 6 | G >> 4;
B = B << 6 | B >> 4;
void Video::generate(Format format) {
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
}
}
Video::Video() {
palette = new unsigned[1 << 15]();
}
Video::~Video() {
delete[] palette;
return interface->videoColor(color, R, G, B);
}
const double Video::monochrome[4][3] = {

View File

@ -1,17 +1,15 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
uint32_t *palette;
void generate_palette();
unsigned palette_dmg(unsigned color) const;
unsigned palette_sgb(unsigned color) const;
unsigned palette_cgb(unsigned color) const;
void generate(Format format);
Video();
~Video();
private:
static const double monochrome[4][3];
uint32_t palette_dmg(unsigned color) const;
uint32_t palette_sgb(unsigned color) const;
uint32_t palette_cgb(unsigned color) const;
};
extern Video video;

View File

@ -4,7 +4,12 @@ namespace GameBoyAdvance {
Interface *interface = nullptr;
void Interface::videoRefresh(const uint16_t *data) {
uint32_t Interface::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data) {
}
void Interface::audioSample(int16_t lsample, int16_t rsample) {

View File

@ -1,5 +1,6 @@
struct Interface {
virtual void videoRefresh(const uint16_t *data);
virtual uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t lsample, int16_t rsample);
virtual bool inputPoll(unsigned id);

View File

@ -153,7 +153,7 @@ void PPU::frame() {
}
PPU::PPU() {
output = new uint16[240 * 160];
output = new uint32[240 * 160];
blur = new uint16[240 * 160];
regs.bg[0].id = BG0;

View File

@ -3,7 +3,7 @@ struct PPU : Thread, MMIO {
uint16 pram[512];
#include "registers.hpp"
#include "state.hpp"
uint16 *output;
uint32 *output;
uint16 *blur;
static void Enter();

View File

@ -1,14 +1,14 @@
void PPU::render_forceblank() {
uint16 *line = output + regs.vcounter * 240;
uint32 *line = output + regs.vcounter * 240;
uint16 *last = blur + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) {
line[x] = (0x7fff + last[x] - ((0x7fff ^ last[x]) & 0x0421)) >> 1;
line[x] = video.palette[(0x7fff + last[x] - ((0x7fff ^ last[x]) & 0x0421)) >> 1];
last[x] = 0x7fff;
}
}
void PPU::render_screen() {
uint16 *line = output + regs.vcounter * 240;
uint32 *line = output + regs.vcounter * 240;
uint16 *last = blur + regs.vcounter * 240;
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
@ -59,7 +59,7 @@ void PPU::render_screen() {
}
//output pixel; blend with previous pixel to simulate GBA LCD blur
line[x] = (color + last[x] - ((color ^ last[x]) & 0x0421)) >> 1;
line[x] = video.palette[(color + last[x] - ((color ^ last[x]) & 0x0421)) >> 1];
last[x] = color;
}
}

View File

@ -4,40 +4,17 @@ namespace GameBoyAdvance {
Video video;
unsigned Video::color(unsigned color) const {
uint5 b = color >> 10;
uint5 g = color >> 5;
uint5 r = color >> 0;
void Video::generate_palette() {
for(unsigned color = 0; color < (1 << 15); color++) {
uint5 b = color >> 10;
uint5 g = color >> 5;
uint5 r = color >> 0;
uint10 R = (r << 5) | (r << 0);
uint10 G = (g << 5) | (g << 0);
uint10 B = (b << 5) | (b << 0);
uint16 R = r << 11 | r << 6 | r << 1 | r >> 4;
uint16 G = g << 11 | g << 6 | g << 1 | g >> 4;
uint16 B = b << 11 | b << 6 | b << 1 | b >> 4;
return (R << 20) | (G << 10) | (B << 0);
}
void Video::generate(Format format) {
for(unsigned n = 0; n < (1 << 15); n++) palette[n] = color(n);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 15); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
palette[color] = interface->videoColor(color, R, G, B);
}
}

View File

@ -1,9 +1,7 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
void generate_palette();
unsigned color(unsigned color) const;
void generate(Format format);
Video();
~Video();
};

View File

@ -1,159 +0,0 @@
#ifndef NALL_ARRAY_HPP
#define NALL_ARRAY_HPP
#include <stdlib.h>
#include <algorithm>
#include <initializer_list>
#include <utility>
#include <nall/algorithm.hpp>
#include <nall/bit.hpp>
#include <nall/sort.hpp>
#include <nall/type_traits.hpp>
#include <nall/utility.hpp>
namespace nall {
template<typename T> struct array {
struct exception_out_of_bounds{};
protected:
T *pool;
unsigned poolsize, objectsize;
public:
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0;
objectsize = 0;
}
void reserve(unsigned newsize) {
if(newsize == poolsize) return;
pool = (T*)realloc(pool, newsize * sizeof(T));
poolsize = newsize;
objectsize = min(objectsize, newsize);
}
void resize(unsigned newsize) {
if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2
objectsize = newsize;
}
T* get(unsigned minsize = 0) {
if(minsize > objectsize) resize(minsize);
return pool;
}
void append(const T data) {
operator()(objectsize) = data;
}
void append(const T data[], unsigned length) {
for(unsigned n = 0; n < length; n++) operator()(objectsize) = data[n];
}
void remove() {
if(size() > 0) resize(size - 1); //remove last element only
}
void remove(unsigned index, unsigned count = 1) {
for(unsigned i = index; count + i < objectsize; i++) {
pool[i] = pool[count + i];
}
if(count + index >= objectsize) resize(index); //every element >= index was removed
else resize(objectsize - count);
}
void sort() {
nall::sort(pool, objectsize);
}
template<typename Comparator> void sort(const Comparator &lessthan) {
nall::sort(pool, objectsize, lessthan);
}
optional<unsigned> find(const T data) {
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
return { false, 0u };
}
void clear() {
memset(pool, 0, objectsize * sizeof(T));
}
array() : pool(nullptr), poolsize(0), objectsize(0) {
}
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
for(auto &data : list) append(data);
}
~array() {
reset();
}
//copy
array& operator=(const array &source) {
if(pool) free(pool);
objectsize = source.objectsize;
poolsize = source.poolsize;
pool = (T*)malloc(sizeof(T) * poolsize); //allocate entire pool size,
memcpy(pool, source.pool, sizeof(T) * objectsize); //... but only copy used pool objects
return *this;
}
array(const array &source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(source);
}
//move
array& operator=(array &&source) {
if(pool) free(pool);
pool = source.pool;
poolsize = source.poolsize;
objectsize = source.objectsize;
source.pool = nullptr;
source.reset();
return *this;
}
array(array &&source) : pool(nullptr), poolsize(0), objectsize(0) {
operator=(std::move(source));
}
//access
inline T& operator[](unsigned position) {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline const T& operator[](unsigned position) const {
if(position >= objectsize) throw exception_out_of_bounds();
return pool[position];
}
inline T& operator()(unsigned position) {
if(position >= objectsize) resize(position + 1);
return pool[position];
}
inline const T& operator()(unsigned position, const T& data) {
if(position >= objectsize) return data;
return pool[position];
}
//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]; }
};
}
#endif

View File

@ -1,6 +1,8 @@
#ifndef NALL_BIT_HPP
#define NALL_BIT_HPP
#include <nall/stdint.hpp>
namespace nall {
template<unsigned bits>
inline uintmax_t uclamp(const uintmax_t x) {

View File

@ -1,79 +0,0 @@
#ifndef NALL_BITARRAY_HPP
#define NALL_BITARRAY_HPP
#include <nall/stdint.hpp>
//statically-sized bit array
//no bounds-checking on read/write
//packed into uint8_t array (8 bits per byte)
namespace nall {
struct bitarray {
uint8_t *pool;
unsigned poolsize;
uint8_t* data() { return pool; }
const uint8_t* data() const { return pool; }
unsigned size() const { return poolsize; }
unsigned bytesize() const { return (poolsize >> 3) + ((poolsize & 7) > 0); }
void reset() {
if(pool) free(pool);
pool = nullptr;
poolsize = 0u;
}
void resize(unsigned allocsize) {
if(allocsize == poolsize) return;
pool = (uint8_t*)realloc(pool, allocsize);
poolsize = allocsize;
}
bool operator[](unsigned offset) const {
return pool[offset >> 3] & (0x80 >> (offset & 7));
}
void set() {
memset(pool, 0xff, (poolsize >> 3) + ((poolsize & 7) > 0));
}
void set(unsigned offset) {
pool[offset >> 3] |= 0x80 >> (offset & 7);
}
void clear() {
memset(pool, 0, (poolsize >> 3) + ((poolsize & 7) > 0));
}
void clear(unsigned offset) {
pool[offset >> 3] &=~0x80 >> (offset & 7);
}
void set(unsigned offset, bool data) {
data ? set(offset) : clear(offset);
}
struct bit {
bitarray &array;
unsigned offset;
operator bool() const { return const_cast<const bitarray&>(array)[offset]; }
bit& operator=(bool data) { array.set(offset, data); return *this; }
bit& operator=(const bit& data) { return operator=((bool)data); }
bit(bitarray &array, unsigned offset) : array(array), offset(offset) {}
};
bit operator[](unsigned offset) {
return bit(*this, offset);
}
bitarray() : pool(nullptr), poolsize(0u) {}
bitarray(unsigned allocsize) {
pool = (uint8_t*)malloc((allocsize >> 3) + ((allocsize & 7) > 0));
poolsize = allocsize;
}
};
}
#endif

View File

@ -6,6 +6,7 @@
#include <nall/string.hpp>
#include <nall/utility.hpp>
#include <nall/windows/utf8.hpp>
#include <nall/stream/memory.hpp>
namespace nall {
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
@ -16,25 +17,19 @@ namespace nall {
#endif
}
class file {
public:
struct file {
enum class mode : unsigned { read, write, readwrite, writeread };
enum class index : unsigned { absolute, relative };
enum class time : unsigned { create, modify, access };
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
data = 0;
static vector<uint8_t> read(const string &filename) {
vector<uint8_t> memory;
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);
if(fp.open(filename, mode::read)) {
memory.resize(fp.size());
fp.read(memory.data(), memory.size());
}
return memory;
}
static bool write(const string &filename, const uint8_t *data, unsigned size) {

View File

@ -19,12 +19,10 @@ struct 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;
if(auto memory = file::read(filename)) {
return decompress(memory.data(), memory.size());
}
return false;
}
bool gzip::decompress(const uint8_t *data, unsigned size) {

View File

@ -11,8 +11,6 @@ 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();
@ -88,14 +86,6 @@ void ips::modify(const uint8_t *data, unsigned size) {
modifyData = data, modifySize = size;
}
bool ips::source(const string &filename) {
return file::read(filename, sourceData, sourceSize);
}
bool ips::modify(const string &filename) {
return file::read(filename, modifyData, modifySize);
}
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
}

View File

@ -58,12 +58,10 @@ protected:
};
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;
if(auto memory = file::read(filename)) {
return decode(memory.data(), memory.size());
}
return false;
}
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {

View File

@ -1,4 +1,5 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_AUTO_HPP
#define NALL_STREAM_AUTO_HPP
namespace nall {

View File

@ -1,21 +1,24 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_FILE_HPP
#define NALL_STREAM_FILE_HPP
#include <nall/file.hpp>
namespace nall {
struct filestream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return false; }
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return false; }
inline unsigned size() const { return pfile.size(); }
inline unsigned offset() const { return pfile.offset(); }
inline void seek(unsigned offset) const { pfile.seek(offset); }
unsigned size() const { return pfile.size(); }
unsigned offset() const { return pfile.offset(); }
void seek(unsigned offset) const { pfile.seek(offset); }
inline uint8_t read() const { return pfile.read(); }
inline void write(uint8_t data) const { pfile.write(data); }
uint8_t read() const { return pfile.read(); }
void write(uint8_t data) const { pfile.write(data); }
inline filestream(const string &filename) {
filestream(const string &filename) {
pfile.open(filename, file::mode::readwrite);
pwritable = pfile.open();
if(!pwritable) pfile.open(filename, file::mode::read);

View File

@ -1,9 +1,12 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_GZIP_HPP
#define NALL_STREAM_GZIP_HPP
#include <nall/gzip.hpp>
namespace nall {
struct gzipstream : memorystream {
inline gzipstream(const stream &stream) {
gzipstream(const stream &stream) {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
@ -18,7 +21,7 @@ struct gzipstream : memorystream {
memcpy(pdata, archive.data, psize);
}
inline ~gzipstream() {
~gzipstream() {
if(pdata) delete[] pdata;
}
};

View File

@ -1,24 +1,27 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_HTTP_HPP
#define NALL_STREAM_HTTP_HPP
#include <nall/http.hpp>
namespace nall {
struct httpstream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return true; }
inline bool randomaccess() const { return true; }
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return true; }
bool randomaccess() const { return true; }
inline unsigned size() const { return psize; }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
unsigned size() const { return psize; }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
httpstream(const string &url, unsigned port) : pdata(nullptr), psize(0), poffset(0) {
string uri = url;
uri.ltrim<1>("http://");
lstring part = uri.split<1>("/");
@ -29,7 +32,7 @@ struct httpstream : stream {
connection.download(part[1], pdata, psize);
}
inline ~httpstream() {
~httpstream() {
if(pdata) delete[] pdata;
}

View File

@ -1,31 +1,35 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_MEMORY_HPP
#define NALL_STREAM_MEMORY_HPP
#include <nall/stream/stream.hpp>
namespace nall {
struct memorystream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return true; }
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return true; }
inline unsigned size() const { return psize; }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
uint8_t *data() const { return pdata; }
unsigned size() const { return psize; }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
memorystream() : pdata(nullptr), psize(0), poffset(0), pwritable(true) {}
inline memorystream(uint8_t *data, unsigned size) {
memorystream(uint8_t *data, unsigned size) {
pdata = data, psize = size, poffset = 0;
pwritable = true;
}
inline memorystream(const uint8_t *data, unsigned size) {
memorystream(const uint8_t *data, unsigned size) {
pdata = (uint8_t*)data, psize = size, poffset = 0;
pwritable = false;
}

View File

@ -1,24 +1,27 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_MMAP_HPP
#define NALL_STREAM_MMAP_HPP
#include <nall/filemap.hpp>
namespace nall {
struct mmapstream : stream {
inline bool seekable() const { return true; }
inline bool readable() const { return true; }
inline bool writable() const { return pwritable; }
inline bool randomaccess() const { return false; }
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return false; }
inline unsigned size() const { return pmmap.size(); }
inline unsigned offset() const { return poffset; }
inline void seek(unsigned offset) const { poffset = offset; }
unsigned size() const { return pmmap.size(); }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
inline uint8_t read() const { return pdata[poffset++]; }
inline void write(uint8_t data) const { pdata[poffset++] = data; }
uint8_t read() const { return pdata[poffset++]; }
void write(uint8_t data) const { pdata[poffset++] = data; }
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
uint8_t read(unsigned offset) const { return pdata[offset]; }
void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
inline mmapstream(const string &filename) {
mmapstream(const string &filename) {
pmmap.open(filename, filemap::mode::readwrite);
pwritable = pmmap.open();
if(!pwritable) pmmap.open(filename, filemap::mode::read);

View File

@ -9,6 +9,7 @@ struct stream {
virtual bool writable() const = 0;
virtual bool randomaccess() const = 0;
virtual uint8_t* data() const { return nullptr; }
virtual unsigned size() const = 0;
virtual unsigned offset() const = 0;
virtual void seek(unsigned offset) const = 0;
@ -16,44 +17,45 @@ struct stream {
virtual uint8_t read() const = 0;
virtual void write(uint8_t data) const = 0;
inline virtual uint8_t read(unsigned) const { return 0; }
inline virtual void write(unsigned, uint8_t) const {}
virtual uint8_t read(unsigned) const { return 0; }
virtual void write(unsigned, uint8_t) const {}
inline bool end() const {
operator bool() const {
return size();
}
bool empty() const {
return size() == 0;
}
bool end() const {
return offset() >= size();
}
inline void copy(uint8_t *&data, unsigned &length) const {
seek(0);
length = size();
data = new uint8_t[length];
for(unsigned n = 0; n < length; n++) data[n] = read();
}
inline uintmax_t readl(unsigned length = 1) const {
uintmax_t readl(unsigned length = 1) const {
uintmax_t data = 0, shift = 0;
while(length--) { data |= read() << shift; shift += 8; }
return data;
}
inline uintmax_t readm(unsigned length = 1) const {
uintmax_t readm(unsigned length = 1) const {
uintmax_t data = 0;
while(length--) data = (data << 8) | read();
return data;
}
inline void read(uint8_t *data, unsigned length) const {
void read(uint8_t *data, unsigned length) const {
while(length--) *data++ = read();
}
inline void writel(uintmax_t data, unsigned length = 1) const {
void writel(uintmax_t data, unsigned length = 1) const {
while(length--) {
write(data);
data >>= 8;
}
}
inline void writem(uintmax_t data, unsigned length = 1) const {
void writem(uintmax_t data, unsigned length = 1) const {
uintmax_t shift = 8 * length;
while(length--) {
shift -= 8;
@ -61,26 +63,26 @@ struct stream {
}
}
inline void write(const uint8_t *data, unsigned length) const {
void write(const uint8_t *data, unsigned length) const {
while(length--) write(*data++);
}
struct byte {
inline operator uint8_t() const { return s.read(offset); }
inline byte& operator=(uint8_t data) { s.write(offset, data); }
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
operator uint8_t() const { return s.read(offset); }
byte& operator=(uint8_t data) { s.write(offset, data); }
byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
private:
const stream &s;
const unsigned offset;
};
inline byte operator[](unsigned offset) const {
byte operator[](unsigned offset) const {
return byte(*this, offset);
}
inline stream() {}
inline virtual ~stream() {}
stream() {}
virtual ~stream() {}
stream(const stream&) = delete;
stream& operator=(const stream&) = delete;
};

36
bsnes/nall/stream/vector.hpp Executable file
View File

@ -0,0 +1,36 @@
#ifndef NALL_STREAM_VECTOR_HPP
#define NALL_STREAM_VECTOR_HPP
#include <nall/stream/stream.hpp>
#include <nall/vector.hpp>
namespace nall {
struct vectorstream : stream {
bool seekable() const { return true; }
bool readable() const { return true; }
bool writable() const { return pwritable; }
bool randomaccess() const { return true; }
uint8_t* data() const { return memory.data(); }
unsigned size() const { return memory.size(); }
unsigned offset() const { return poffset; }
void seek(unsigned offset) const { poffset = offset; }
uint8_t read() const { return memory[poffset++]; }
void write(uint8_t data) const { memory[poffset++] = data; }
uint8_t read(unsigned offset) const { return memory[offset]; }
void write(unsigned offset, uint8_t data) const { memory[offset] = data; }
vectorstream(vector<uint8_t> &memory) : memory(memory), poffset(0), pwritable(true) {}
vectorstream(const vector<uint8_t> &memory) : memory((vector<uint8_t>&)memory), poffset(0), pwritable(false) {}
protected:
vector<uint8_t> &memory;
mutable unsigned poffset, pwritable;
};
}
#endif

View File

@ -1,9 +1,12 @@
#ifdef NALL_STREAM_INTERNAL_HPP
#ifndef NALL_STREAM_ZIP_HPP
#define NALL_STREAM_ZIP_HPP
#include <nall/zip.hpp>
namespace nall {
struct zipstream : memorystream {
inline zipstream(const stream &stream, const string &filter = "*") {
zipstream(const stream &stream, const string &filter = "*") {
unsigned size = stream.size();
uint8_t *data = new uint8_t[size];
stream.read(data, size);
@ -20,7 +23,7 @@ struct zipstream : memorystream {
}
}
inline ~zipstream() {
~zipstream() {
if(pdata) delete[] pdata;
}
};

View File

@ -9,7 +9,6 @@
#include <algorithm>
#include <initializer_list>
#include <nall/array.hpp>
#include <nall/atoi.hpp>
#include <nall/function.hpp>
#include <nall/platform.hpp>

View File

@ -21,7 +21,10 @@ namespace nall {
unsigned objectsize;
public:
operator bool() const { return pool; }
T* data() { return pool; }
bool empty() const { return pool == nullptr; }
unsigned size() const { return objectsize; }
unsigned capacity() const { return poolsize; }

View File

@ -4,7 +4,12 @@ namespace Famicom {
Interface *interface = nullptr;
void Interface::videoRefresh(const uint16_t *data) {
uint32_t Interface::videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data) {
}
void Interface::audioSample(const int16_t sample) {
@ -18,4 +23,8 @@ void Interface::message(const string &text) {
print(text, "\n");
}
void Interface::loadCartridge(const string &markup, const stream &memory) {
cartridge.load(markup, memory.data(), memory.size());
}
}

View File

@ -1,9 +1,11 @@
struct Interface {
virtual void videoRefresh(const uint16_t *data);
virtual uint32_t videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data);
virtual void audioSample(int16_t sample);
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
virtual void message(const string &text);
void loadCartridge(const string &markup, const stream &memory);
};
extern Interface *interface;

View File

@ -282,7 +282,7 @@ void PPU::scrolly_increment() {
//
void PPU::raster_pixel() {
uint16 *output = buffer + status.ly * 256;
uint32 *output = buffer + status.ly * 256;
unsigned mask = 0x8000 >> (status.xaddr + (status.lx & 7));
unsigned palette = 0, object_palette = 0;
@ -325,7 +325,7 @@ void PPU::raster_pixel() {
}
if(raster_enable() == false) palette = 0;
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
output[status.lx] = video.palette[(status.emphasis << 6) | cgram_read(palette)];
}
void PPU::raster_sprite() {

View File

@ -98,7 +98,7 @@ struct PPU : Thread {
} oam[8], soam[8];
} raster;
uint16 buffer[256 * 262];
uint32 buffer[256 * 262];
uint8 ciram[2048];
uint8 cgram[32];
uint8 oam[256];

View File

@ -5,7 +5,19 @@ namespace Famicom {
Video video;
unsigned Video::palette30(
void Video::generate_palette() {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8);
}
Video::Video() {
palette = new unsigned[1 << 9];
}
Video::~Video() {
delete[] palette;
}
uint32_t Video::generate_color(
unsigned n, double saturation, double hue,
double contrast, double brightness, double gamma
) {
@ -46,43 +58,11 @@ unsigned Video::palette30(
q *= saturation;
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
unsigned r = 1023.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
unsigned g = 1023.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
unsigned b = 1023.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
return (uclamp<10>(r) << 20) + (uclamp<10>(g) << 10) + (uclamp<10>(b) << 0);
}
unsigned r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
void Video::generate(Format format) {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = palette30(n, 2.0, 0.0, 1.0, 1.0, 1.8);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 9); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
}
}
Video::Video() {
palette = new unsigned[1 << 9];
}
Video::~Video() {
delete[] palette;
return interface->videoColor(n, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
}
}

View File

@ -1,11 +1,12 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
void generate_palette();
unsigned palette30(unsigned, double, double, double, double, double);
void generate(Format format);
Video();
~Video();
private:
uint32_t generate_color(unsigned, double, double, double, double, double);
};
extern Video video;

View File

@ -7,9 +7,9 @@
#ifndef RUBY_H
#define RUBY_H
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bit.hpp>
#include <nall/input.hpp>
#include <nall/intrinsics.hpp>

View File

@ -70,6 +70,7 @@ void ICD2::reset() {
pulselock = true;
GameBoy::interface = this;
GameBoy::video.generate_palette();
GameBoy::system.init();
GameBoy::system.power();
}

View File

@ -7,7 +7,7 @@ void ICD2::lcdScanline() {
}
unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::ppu.status.ly & 7) * 160);
memcpy(lcd.buffer + offset, GameBoy::ppu.screen + GameBoy::ppu.status.ly * 160, 160 * sizeof(uint16));
memcpy(lcd.buffer + offset, GameBoy::ppu.screen + GameBoy::ppu.status.ly * 160, 160 * sizeof(uint32));
}
void ICD2::joypWrite(bool p15, bool p14) {
@ -80,7 +80,11 @@ void ICD2::joypWrite(bool p15, bool p14) {
packetlock = true;
}
void ICD2::videoRefresh(const uint16_t *data) {
uint32_t ICD2::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
return source;
}
void ICD2::videoRefresh(const uint32_t *data) {
}
void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {

View File

@ -1,6 +1,7 @@
void lcdScanline();
void joypWrite(bool p15, bool p14);
void videoRefresh(const uint16_t *data);
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t center, int16_t left, int16_t right);
bool inputPoll(unsigned id);

View File

@ -1,7 +1,7 @@
#ifdef ICD2_CPP
//convert linear pixel data { 0x00, 0x55, 0xaa, 0xff } to 2bpp planar tiledata
void ICD2::render(const uint16 *source) {
//convert linear pixel data to 2bpp planar tiledata
void ICD2::render(const uint32 *source) {
memset(lcd.output, 0x00, 320 * sizeof(uint16));
for(unsigned y = 0; y < 8; y++) {

View File

@ -1,4 +1,4 @@
void render(const uint16 *source);
void render(const uint32 *source);
uint8 r6000_ly; //SGB BIOS' cache of LY
uint8 r6000_row; //SGB BIOS' cache of ROW
@ -13,7 +13,7 @@ unsigned r7800; //VRAM offset
uint8 mlt_req; //number of active joypads
struct LCD {
uint16 buffer[4 * 160 * 8]; //four tile rows of linear video data
uint32 buffer[4 * 160 * 8]; //four tile rows of linear video data
uint16 output[320]; //one tile row of 2bpp video data
unsigned row; //active ICD2 rendering tile row
} lcd;

View File

@ -4,6 +4,11 @@ namespace SuperFamicom {
Interface *interface = nullptr;
uint32_t Interface::videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue) {
red >>= 8, green >>= 8, blue >>= 8;
return red << 16 | green << 8 | blue << 0;
}
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
}

View File

@ -1,4 +1,5 @@
struct Interface {
virtual uint32_t videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue);
virtual void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
virtual void audioSample(int16_t lsample, int16_t rsample);
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);

View File

@ -150,7 +150,7 @@ uint32 PPU::Screen::get_pixel(bool swap) {
//========
if(self.regs.display_disable) return 0;
return (self.regs.display_brightness << 15) | output;
return video.palette[self.regs.display_brightness << 15 | output];
}
uint16 PPU::Screen::addsub(unsigned x, unsigned y, bool halve) {

View File

@ -2,48 +2,25 @@
Video video;
unsigned Video::palette30(unsigned color) {
unsigned l = (color >> 15) & 15;
unsigned b = (color >> 10) & 31;
unsigned g = (color >> 5) & 31;
unsigned r = (color >> 0) & 31;
void Video::generate_palette() {
for(unsigned color = 0; color < (1 << 19); color++) {
unsigned l = (color >> 15) & 15;
unsigned b = (color >> 10) & 31;
unsigned g = (color >> 5) & 31;
unsigned r = (color >> 0) & 31;
double L = (1.0 + l) / 16.0;
if(l == 0) L *= 0.5;
unsigned R = L * ((r << 5) + (r << 0));
unsigned G = L * ((g << 5) + (g << 0));
unsigned B = L * ((b << 5) + (b << 0));
double L = (1.0 + l) / 16.0;
if(l == 0) L *= 0.5;
unsigned R = L * (r << 11 | r << 6 | r << 1 | r >> 4);
unsigned G = L * (g << 11 | g << 6 | g << 1 | g >> 4);
unsigned B = L * (b << 11 | b << 6 | b << 1 | b >> 4);
return (R << 20) + (G << 10) + (B << 0);
}
void Video::generate(Format format) {
for(unsigned n = 0; n < (1 << 19); n++) palette[n] = palette30(n);
if(format == Format::RGB24) {
for(unsigned n = 0; n < (1 << 19); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
}
}
if(format == Format::RGB16) {
for(unsigned n = 0; n < (1 << 19); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
}
}
if(format == Format::RGB15) {
for(unsigned n = 0; n < (1 << 19); n++) {
unsigned color = palette[n];
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
}
palette[color] = interface->videoColor(color, R, G, B);
}
}
Video::Video() {
palette = new unsigned[1 << 19];
palette = new unsigned[1 << 19]();
}
Video::~Video() {

View File

@ -1,9 +1,6 @@
struct Video {
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
unsigned *palette;
unsigned palette30(unsigned color);
void generate(Format format);
void generate_palette();
Video();
~Video();

View File

@ -16,6 +16,8 @@ namespace GBA = GameBoyAdvance;
#include <nall/filemap.hpp>
#include <nall/input.hpp>
#include <nall/bps/patch.hpp>
#include <nall/stream/memory.hpp>
#include <nall/stream/vector.hpp>
#include <nall/nes/cartridge.hpp>
#include <nall/snes/cartridge.hpp>
#include <nall/gb/cartridge.hpp>

View File

@ -12,18 +12,50 @@ bool InterfaceCore::loadFirmware(string filename, string keyname, uint8_t *targe
string firmware = key["firmware"].data;
string hash = key["sha256"].data;
uint8_t *data;
unsigned size;
if(file::read({dir(filename),firmware}, data, size) == true) {
if(nall::sha256(data, size) == hash) {
memcpy(targetdata, data, min(targetsize, size));
if(auto memory = file::read({dir(filename),firmware})) {
if(nall::sha256(memory.data(), memory.size()) == hash) {
memcpy(targetdata, memory.data(), min(targetsize, memory.size()));
result = true;
} else {
MessageWindow::information(Window::None, {"Warning: Firmware SHA256 sum is incorrect:\n\n", dir(filename), firmware});
}
delete[] data;
}
}
return result;
}
/* 5-bit -> 8-bit
static const uint8_t gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};*/
uint32_t InterfaceCore::color(uint16_t r, uint16_t g, uint16_t b) {
auto gamma = [](uint16_t n) {
double exponent = 1.0 + (double)config->video.gamma * 0.01;
return n < 32768 ? 32767 * pow(((double)n / 32767), exponent) : n;
};
auto contrast = [](uint16_t n) {
double contrast = config->video.contrast * 0.01;
signed result = n * contrast;
return max(0, min(65535, result));
};
auto brightness = [](uint16_t n) {
signed brightness = (config->video.brightness - 100) * 256;
signed result = n + brightness;
return max(0, min(65535, result));
};
r = brightness(contrast(gamma(r)));
g = brightness(contrast(gamma(g)));
b = brightness(contrast(gamma(b)));
if(application->depth == 30) { r >>= 6, g >>= 6, b >>= 6; return r << 20 | g << 10 | b << 0; }
if(application->depth == 24) { r >>= 8, g >>= 8, b >>= 8; return r << 16 | g << 8 | b << 0; }
return 0u;
}

View File

@ -18,28 +18,26 @@ bool InterfaceGB::cartridgeLoaded() {
bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &filename) {
interface->unloadCartridge();
uint8_t *data;
unsigned size;
vector<uint8_t> memory;
if(filename.endswith("/")) {
if(file::read({ filename, "program.rom" }, data, size) == false) return false;
interface->base = { true, filename };
memory = file::read({filename, "program.rom"});
interface->base = {true, filename};
} else {
if(file::read(filename, data, size) == false) return false;
interface->base = { false, nall::basename(filename) };
memory = file::read(filename);
interface->base = {false, nall::basename(filename)};
}
if(memory.empty()) return false;
interface->game = interface->base;
interface->cartridgeTitle = interface->base.title();
interface->applyPatch(interface->base, data, size);
interface->applyPatch(interface->base, memory);
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = GameBoyCartridge(data, size).markup;
if(markup.empty()) markup = GameBoyCartridge(memory.data(), memory.size()).markup;
GB::cartridge.load(revision, markup, data, size);
GB::cartridge.load(revision, markup, vectorstream{memory});
GB::system.power();
delete[] data;
if(GB::cartridge.ramsize) {
filemap fp;
@ -49,7 +47,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
}
GB::interface = this;
GB::video.generate(GB::Video::Format::RGB30);
GB::video.generate_palette();
interface->loadCartridge(::Interface::Mode::GB);
return true;
}
@ -103,19 +101,12 @@ void InterfaceGB::setCheats(const lstring &list) {
//
void InterfaceGB::videoRefresh(const uint16_t *data) {
static uint32_t output[160 * 144];
uint32_t InterfaceGB::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
for(unsigned y = 0; y < 144; y++) {
const uint16_t *sp = data + y * 160;
uint32_t *dp = output + y * 160;
for(unsigned x = 0; x < 160; x++) {
uint16_t color = *sp++;
*dp++ = GB::video.palette[color];
}
}
interface->videoRefresh(output, 160 * sizeof(uint32_t), 160, 144);
void InterfaceGB::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 160 * sizeof(uint32_t), 160, 144);
}
void InterfaceGB::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {

View File

@ -16,7 +16,8 @@ struct InterfaceGB : InterfaceCore, GB::Interface {
void setCheats(const lstring &list = lstring{});
void videoRefresh(const uint16_t *data);
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
bool inputPoll(unsigned id);
};

View File

@ -16,28 +16,26 @@ bool InterfaceGBA::cartridgeLoaded() {
bool InterfaceGBA::loadCartridge(const string &filename) {
interface->unloadCartridge();
uint8_t *data;
unsigned size;
vector<uint8_t> memory;
if(filename.endswith("/")) {
if(file::read({filename, "program.rom"}, data, size) == false) return false;
memory = file::read({filename, "program.rom"});
interface->base = {true, filename};
} else {
if(file::read(filename, data, size) == false) return false;
memory = file::read(filename);
interface->base = {false, nall::basename(filename)};
}
if(memory.empty()) return false;
interface->game = interface->base;
interface->cartridgeTitle = interface->base.title();
interface->applyPatch(interface->base, data, size);
interface->applyPatch(interface->base, memory);
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = GameBoyAdvanceCartridge(data, size).markup;
if(markup.empty()) markup = GameBoyAdvanceCartridge(memory.data(), memory.size()).markup;
GBA::cartridge.load(markup, data, size);
GBA::cartridge.load(markup, memory.data(), memory.size());
GBA::system.power();
delete[] data;
if(GBA::cartridge.ram_size()) {
filemap fp;
@ -46,7 +44,7 @@ bool InterfaceGBA::loadCartridge(const string &filename) {
}
}
GBA::video.generate(GBA::Video::Format::RGB30);
GBA::video.generate_palette();
interface->loadCartridge(::Interface::Mode::GBA);
return true;
}
@ -86,19 +84,12 @@ void InterfaceGBA::setCheats(const lstring &list) {
//
void InterfaceGBA::videoRefresh(const uint16_t *data) {
static uint32_t output[240 * 160];
uint32_t InterfaceGBA::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
for(unsigned y = 0; y < 160; y++) {
const uint16_t *sp = data + y * 240;
uint32_t *dp = output + y * 240;
for(unsigned x = 0; x < 240; x++) {
uint16_t color = *sp++;
*dp++ = GBA::video.palette[color];
}
}
interface->videoRefresh(output, 240 * sizeof(uint32_t), 240, 160);
void InterfaceGBA::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 240 * sizeof(uint32_t), 240, 160);
}
void InterfaceGBA::audioSample(int16_t lsample, int16_t rsample) {

View File

@ -16,7 +16,8 @@ struct InterfaceGBA : InterfaceCore, GBA::Interface {
void setCheats(const lstring &list = lstring{});
void videoRefresh(const uint16_t *data);
uint32_t videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t lsample, int16_t rsample);
bool inputPoll(unsigned id);
};

View File

@ -1,6 +1,5 @@
#include "../base.hpp"
#include "core.cpp"
#include "palette.cpp"
#include "nes/nes.cpp"
#include "snes/snes.cpp"
#include "gb/gb.cpp"
@ -169,13 +168,12 @@ bool Interface::saveState(unsigned slot) {
bool Interface::loadState(unsigned slot) {
string filename = game.filename({ "state-", slot, ".bst" }, { "-", slot, ".bst" });
uint8_t *data;
unsigned size;
if(file::read(filename, data, size) == false) {
auto memory = file::read(filename);
if(memory.empty()) {
utility->showMessage(string{ "Slot ", slot, " save file not found" });
return false;
}
serializer s(data, size);
serializer s(memory.data(), memory.size());
bool result = unserialize(s);
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
return result;
@ -185,6 +183,15 @@ void Interface::setCheatCodes(const lstring &list) {
if(core) return core->setCheats(list);
}
void Interface::updatePalette() {
switch(mode()) {
case Mode::NES: return NES::video.generate_palette();
case Mode::SNES: return SNES::video.generate_palette();
case Mode::GB: return GB::video.generate_palette();
case Mode::GBA: return GBA::video.generate_palette();
}
}
string Interface::sha256() {
switch(mode()) {
case Mode::NES: return NES::cartridge.sha256();
@ -205,13 +212,13 @@ Interface::Interface() : core(nullptr) {
//internal
bool Interface::applyPatch(CartridgePath &filepath, uint8_t *&data, unsigned &size) {
bool Interface::applyPatch(CartridgePath &filepath, vector<uint8_t> &memory) {
string patchname = filepath.filename("patch.bps", ".bps");
if(file::exists(patchname) == false) return false;
bpspatch bps;
bps.modify(patchname);
bps.source(data, size);
bps.source(memory.data(), memory.size());
unsigned targetSize = bps.size();
uint8_t *targetData = new uint8_t[targetSize];
bps.target(targetData, targetSize);
@ -220,9 +227,9 @@ bool Interface::applyPatch(CartridgePath &filepath, uint8_t *&data, unsigned &si
return false;
}
delete[] data;
data = targetData;
size = targetSize;
memory.resize(targetSize);
memcpy(memory.data(), targetData, targetSize);
delete[] targetData;
return true;
}
@ -230,7 +237,7 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
uint32_t *output;
unsigned outputPitch;
if(filter.opened()) {
if(application->depth == 30 && filter.opened()) {
filter.render(input, inputPitch, width, height);
input = filter.data;
inputPitch = filter.pitch;
@ -242,11 +249,21 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
inputPitch >>= 2, outputPitch >>= 2;
for(unsigned y = 0; y < height; y++) {
const uint32_t *sp = input + y * inputPitch;
uint32_t *dp = output + y * outputPitch;
for(unsigned x = 0; x < width; x++) {
uint32_t color = *sp++;
*dp++ = palette((color >> 20) & 1023, (color >> 10) & 1023, (color >> 0) & 1023);
memcpy(output + y * outputPitch, input + y * inputPitch, width * sizeof(uint32_t));
}
if(config->video.maskOverscan && (mode() == Mode::NES || mode() == Mode::SNES)) {
unsigned h = config->video.maskOverscanHorizontal;
unsigned v = config->video.maskOverscanVertical;
if(h) for(unsigned y = 0; y < height; y++) {
memset(output + y * outputPitch, 0, h * sizeof(uint32_t));
memset(output + y * outputPitch + (width - h), 0, h * sizeof(uint32_t));
}
if(v) for(unsigned y = 0; y < v; y++) {
memset(output + y * outputPitch, 0, width * sizeof(uint32_t));
memset(output + (height - 1 - y) * outputPitch, 0, width * sizeof(uint32_t));
}
}

View File

@ -1,7 +1,6 @@
#include "palette.hpp"
struct InterfaceCore {
bool loadFirmware(string filename, string keyname, uint8_t *targetdata, unsigned targetsize);
uint32_t color(uint16_t red, uint16_t green, uint16_t blue);
virtual string markup() = 0;
virtual bool cartridgeLoaded() = 0;
@ -69,11 +68,12 @@ struct Interface : property<Interface> {
bool saveState(unsigned slot);
bool loadState(unsigned slot);
void setCheatCodes(const lstring &list = lstring{});
void updatePalette();
string sha256();
Interface();
bool applyPatch(CartridgePath &filepath, uint8_t *&data, unsigned &size);
bool applyPatch(CartridgePath &filepath, vector<uint8_t> &memory);
void videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height);
CartridgePath base; //base cartridge connected to emulated system

View File

@ -29,28 +29,26 @@ bool InterfaceNES::cartridgeLoaded() {
bool InterfaceNES::loadCartridge(const string &filename) {
interface->unloadCartridge();
uint8_t *data;
unsigned size;
vector<uint8_t> memory;
if(filename.endswith("/")) {
if(file::read({filename, "program.rom"}, data, size) == false) return false;
memory = file::read({filename, "program.rom"});
interface->base = {true, filename};
} else {
file::read(filename, data, size);
memory = file::read(filename);
interface->base = {false, nall::basename(filename)};
}
if(memory.empty()) return false;
interface->game = interface->base;
interface->cartridgeTitle = interface->base.title();
interface->applyPatch(interface->base, data, size);
interface->applyPatch(interface->base, memory);
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = FamicomCartridge(data, size).markup;
if(markup.empty()) markup = FamicomCartridge(memory.data(), memory.size()).markup;
NES::cartridge.load(markup, data, size);
NES::cartridge.load(markup, memory.data(), memory.size());
NES::system.power();
delete[] data;
if(NES::cartridge.ram_size()) {
filemap fp;
@ -60,7 +58,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
}
interface->loadCartridge(::Interface::Mode::NES);
NES::video.generate(NES::Video::Format::RGB30);
NES::video.generate_palette();
return true;
}
@ -116,34 +114,12 @@ void InterfaceNES::setCheats(const lstring &list) {
//
void InterfaceNES::videoRefresh(const uint16_t *data) {
static uint32_t output[256 * 240];
uint32_t InterfaceNES::videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
for(unsigned y = 0; y < 240; y++) {
const uint16_t *sp = data + y * 256;
uint32_t *dp = output + y * 256;
for(unsigned x = 0; x < 256; x++) {
uint32_t color = *sp++;
*dp++ = NES::video.palette[color];
}
}
if(config->video.maskOverscan) {
unsigned osw = config->video.maskOverscanHorizontal;
unsigned osh = config->video.maskOverscanVertical;
for(unsigned y = 0; y < 240; y++) {
uint32_t *dp = output + y * 256;
if(y < osh || y >= 240 - osh) {
memset(dp, 0, 256 * sizeof(uint32_t));
} else {
memset(dp + 0, 0, osw * sizeof(uint32_t));
memset(dp + 256 - osw, 0, osw * sizeof(uint32_t));
}
}
}
interface->videoRefresh(output, 256 * sizeof(uint32_t), 256, 240);
void InterfaceNES::videoRefresh(const uint32_t *data) {
interface->videoRefresh(data, 256 * sizeof(uint32_t), 256, 240);
}
void InterfaceNES::audioSample(int16_t sample) {

View File

@ -18,7 +18,8 @@ struct InterfaceNES : InterfaceCore, NES::Interface {
void setCheats(const lstring &list = lstring{});
void videoRefresh(const uint16_t *data);
uint32_t videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data);
void audioSample(int16_t sample);
int16_t inputPoll(bool port, unsigned device, unsigned id);
};

View File

@ -1,50 +0,0 @@
Palette palette;
unsigned Palette::operator()(unsigned r, unsigned g, unsigned b) const {
return red[r] + green[g] + blue[b];
}
/* 5-bit -> 8-bit
const uint8_t Palette::gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};
*/
void Palette::update() {
double exponent = 1.0 + (double)config->video.gamma * 0.01;
for(unsigned n = 0; n < 1024; n++) {
unsigned result = (n < 512 ? 511 * pow(((double)n / 511), exponent) : n);
color[n] = result;
}
double contrast = config->video.contrast * 0.01;
for(unsigned n = 0; n < 1024; n++) {
signed result = color[n] * contrast;
color[n] = max(0, min(1023, result));
}
signed brightness = (config->video.brightness - 100) * 4;
for(unsigned n = 0; n < 1024; n++) {
signed result = color[n] + brightness;
color[n] = max(0, min(1023, result));
}
if(application->depth == 30) {
for(unsigned n = 0; n < 1024; n++) {
red[n] = color[n] << 20;
green[n] = color[n] << 10;
blue[n] = color[n] << 0;
}
}
if(application->depth == 24) {
for(unsigned n = 0; n < 1024; n++) {
red[n] = (color[n] >> 2) << 16;
green[n] = (color[n] >> 2) << 8;
blue[n] = (color[n] >> 2) << 0;
}
}
}

View File

@ -1,10 +0,0 @@
struct Palette {
alwaysinline unsigned operator()(unsigned r, unsigned g, unsigned b) const;
void update();
private:
uint32_t color[1024];
uint32_t red[1024], green[1024], blue[1024];
};
extern Palette palette;

View File

@ -37,178 +37,156 @@ bool InterfaceSNES::cartridgeLoaded() {
return SNES::cartridge.loaded();
}
bool InterfaceSNES::loadCartridge(const string &filename, CartridgePath &cartridge, uint8_t *&data, unsigned &size) {
data = nullptr, size = 0u;
vector<uint8_t> InterfaceSNES::loadCartridge(const string &filename, CartridgePath &cartridge) {
vector<uint8_t> memory;
auto backup = cartridge;
string suffix;
if(filename.endswith("/")) {
cartridge = { true, filename };
cartridge = {true, filename};
} else {
suffix = { ".", extension(filename) };
cartridge = { false, nall::basename(filename) };
suffix = {".", extension(filename)};
cartridge = {false, nall::basename(filename)};
}
if(file::read(cartridge.filename("program.rom", suffix), data, size) == false) {
cartridge = backup;
return false;
}
interface->applyPatch(cartridge, data, size);
return true;
memory = file::read(cartridge.filename("program.rom", suffix));
interface->applyPatch(cartridge, memory);
if(memory.empty()) cartridge = backup;
return memory;
}
bool InterfaceSNES::loadCartridge(string basename) {
interface->unloadCartridge();
uint8_t *data;
unsigned size;
if(loadCartridge(basename, interface->base, data, size) == false) return false;
auto memory = loadCartridge(basename, interface->base);
if(memory.empty()) return false;
interface->game = interface->base;
interface->cartridgeTitle = interface->base.title();
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SuperFamicomCartridge(data, size).markup;
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
SNES::cartridge.rom.copy(data, size);
SNES::cartridge.rom.copy(memory.data(), memory.size());
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
SNES::system.power();
delete[] data;
loadMemory();
interface->loadCartridge(::Interface::Mode::SNES);
SNES::video.generate(SNES::Video::Format::RGB30);
SNES::video.generate_palette();
return true;
}
bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slotname) {
interface->unloadCartridge();
uint8_t *data[2];
unsigned size[2];
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
auto memory = loadCartridge(basename, interface->base);
if(memory.empty()) return false;
auto memoryBS = loadCartridge(slotname, interface->slot[0]);
interface->game = !data[1] ? interface->base : interface->slot[0]; //TODO: subfolder for folders; concatenation for files
interface->game = memoryBS.empty() ? interface->base : interface->slot[0]; //TODO: subfolder for folders; concatenation for files
interface->cartridgeTitle = interface->base.title();
if(data[1]) interface->cartridgeTitle.append(" + ", interface->slot[0].title());
if(memoryBS) interface->cartridgeTitle.append(" + ", interface->slot[0].title());
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
SNES::cartridge.rom.copy(memory.data(), memory.size());
if(memoryBS) SNES::bsxflash.memory.copy(memoryBS.data(), memoryBS.size());
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, markup);
SNES::system.power();
delete[] data[0];
if(data[1]) delete[] data[1];
loadMemory();
interface->loadCartridge(::Interface::Mode::SNES);
SNES::video.generate(SNES::Video::Format::RGB30);
SNES::video.generate_palette();
return true;
}
bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
interface->unloadCartridge();
uint8_t *data[2];
unsigned size[2];
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
auto memory = loadCartridge(basename, interface->base);
if(memory.empty()) return false;
auto memoryBS = loadCartridge(slotname, interface->slot[0]);
interface->game = !data[1] ? interface->base : interface->slot[0];
interface->game = memoryBS.empty() ? interface->base : interface->slot[0];
interface->cartridgeTitle = interface->base.title();
if(data[1]) interface->cartridgeTitle = interface->slot[0].title();
if(memoryBS) interface->cartridgeTitle = interface->slot[0].title();
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
SNES::cartridge.rom.copy(memory.data(), memory.size());
if(memoryBS) SNES::bsxflash.memory.copy(memoryBS.data(), memoryBS.size());
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, markup);
SNES::system.power();
delete[] data[0];
if(data[1]) delete[] data[1];
loadMemory();
interface->loadCartridge(::Interface::Mode::SNES);
SNES::video.generate(SNES::Video::Format::RGB30);
SNES::video.generate_palette();
return true;
}
bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname, string slotBname) {
interface->unloadCartridge();
uint8_t *data[3];
unsigned size[3];
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
loadCartridge(slotAname, interface->slot[0], data[1], size[1]);
loadCartridge(slotBname, interface->slot[1], data[2], size[2]);
auto memory = loadCartridge(basename, interface->base);
if(memory.empty()) return false;
auto memorySTA = loadCartridge(slotAname, interface->slot[0]);
auto memorySTB = loadCartridge(slotBname, interface->slot[1]);
interface->game = !data[1] ? interface->base : interface->slot[0]; //TODO: subfolder for folders; concatenation for files
interface->game = memorySTA.empty() ? interface->base : interface->slot[0]; //TODO: subfolder for folders; concatenation for files
interface->cartridgeTitle = interface->base.title();
if( data[1] && !data[2]) interface->cartridgeTitle = interface->slot[0].title();
if(!data[1] && data[2]) interface->cartridgeTitle = interface->slot[1].title();
if( data[1] && data[2]) interface->cartridgeTitle = {
if( memorySTA && !memorySTB) interface->cartridgeTitle = interface->slot[0].title();
if(!memorySTA && memorySTB) interface->cartridgeTitle = interface->slot[1].title();
if( memorySTA && memorySTB) interface->cartridgeTitle = {
interface->slot[0].title(), " + ", interface->slot[1].title()
};
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
if(data[2]) SNES::sufamiturbo.slotB.rom.copy(data[1], size[1]);
SNES::cartridge.rom.copy(memory.data(), memory.size());
if(memorySTA) SNES::sufamiturbo.slotA.rom.copy(memory.data(), memory.size());
if(memorySTB) SNES::sufamiturbo.slotB.rom.copy(memory.data(), memory.size());
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, markup);
SNES::system.power();
delete[] data[0];
if(data[1]) delete[] data[1];
if(data[2]) delete[] data[2];
loadMemory();
interface->loadCartridge(::Interface::Mode::SNES);
SNES::video.generate(SNES::Video::Format::RGB30);
SNES::video.generate_palette();
return true;
}
bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname) {
interface->unloadCartridge();
uint8_t *data[2];
unsigned size[2];
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
auto memory = loadCartridge(basename, interface->base);
if(memory.empty()) return false;
auto memoryGB = loadCartridge(slotname, interface->slot[0]);
interface->game = !data[1] ? interface->base : interface->slot[0];
interface->game = memoryGB.empty() ? interface->base : interface->slot[0];
interface->cartridgeTitle = interface->base.title();
if(data[1]) interface->cartridgeTitle = interface->slot[0].title();
if(memoryGB) interface->cartridgeTitle = interface->slot[0].title();
string markup;
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
if(markup.empty()) markup = SuperFamicomCartridge(data[0], size[0]).markup;
if(markup.empty()) markup = SuperFamicomCartridge(memory.data(), memory.size()).markup;
string gbMarkup;
gbMarkup.readfile(interface->slot[0].filename("manifest.xml", ".xml"));
if(gbMarkup.empty()) gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
if(gbMarkup.empty()) gbMarkup = GameBoyCartridge(memoryGB.data(), memoryGB.size()).markup;
SNES::cartridge.rom.copy(data[0], size[0]);
GB::cartridge.load(GB::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
SNES::cartridge.rom.copy(memory.data(), memory.size());
GB::cartridge.load(GB::System::Revision::SuperGameBoy, gbMarkup, vectorstream{memoryGB});
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
SNES::system.power();
delete[] data[0];
if(data[1]) delete[] data[1];
loadMemory();
interface->loadCartridge(::Interface::Mode::SNES);
SNES::video.generate(SNES::Video::Format::RGB30);
SNES::video.generate_palette();
return true;
}
@ -253,21 +231,15 @@ void InterfaceSNES::loadMemory() {
string filename = memoryName(memory);
if(filename.empty()) continue;
uint8_t *data;
unsigned size;
if(file::read(filename, data, size)) {
memcpy(memory.data, data, min(memory.size, size));
delete[] data;
if(auto read = file::read(filename)) {
memcpy(memory.data, read.data(), min(memory.size, read.size()));
}
}
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
if(GB::cartridge.ramsize) {
uint8_t *data;
unsigned size;
if(file::read(interface->slot[0].filename("save.ram", ".sav"), data, size)) {
memcpy(GB::cartridge.ramdata, data, min(GB::cartridge.ramsize, size));
delete[] data;
if(auto read = file::read(interface->slot[0].filename("save.ram", ".sav"))) {
memcpy(GB::cartridge.ramdata, read.data(), min(GB::cartridge.ramsize, read.size()));
}
}
}
@ -334,41 +306,20 @@ void InterfaceSNES::setCheats(const lstring &list) {
//
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
static uint32_t output[512 * 480];
uint32_t InterfaceSNES::videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue) {
return color(red, green, blue);
}
unsigned width = 256 << hires;
unsigned height = 240 << interlace;
unsigned pitch = 1024 >> interlace;
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
unsigned width = 256 << hires;
unsigned height = 240 << interlace;
unsigned pitch = 1024 >> interlace;
//skip first line; as it is always blank (by SNES design)
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
for(unsigned y = 0; y < height; y++) {
const uint32_t *sp = data + y * pitch;
uint32_t *dp = output + y * 512;
for(unsigned x = 0; x < width; x++) {
*dp++ = SNES::video.palette[*sp++];
}
}
if(config->video.maskOverscan) {
unsigned osw = config->video.maskOverscanHorizontal << hires;
unsigned osh = config->video.maskOverscanVertical << interlace;
for(unsigned y = 0; y < height; y++) {
uint32_t *dp = output + y * 512;
if(y < osh || y >= height - osh) {
memset(dp, 0, width * sizeof(uint32_t));
} else {
memset(dp + 0, 0, osw * sizeof(uint32_t));
memset(dp + width - osw, 0, osw * sizeof(uint32_t));
}
}
}
interface->videoRefresh(output, 512 * sizeof(uint32_t), width, height);
interface->videoRefresh(data, pitch * sizeof(uint32_t), width, height);
}
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {

View File

@ -6,7 +6,7 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
void setController(bool port, unsigned device);
bool cartridgeLoaded();
bool loadCartridge(const string &filename, CartridgePath &cartridge, uint8_t *&data, unsigned &size);
vector<uint8_t> loadCartridge(const string &filename, CartridgePath &cartridge);
bool loadCartridge(string basename);
bool loadSatellaviewSlottedCartridge(string basename, string slotname);
bool loadSatellaviewCartridge(string basename, string slotname);
@ -27,6 +27,7 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
void setCheats(const lstring &list = lstring{});
uint32_t videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id);

View File

@ -88,7 +88,6 @@ Application::Application(int argc, char **argv) {
video.driver("None");
video.init();
}
palette.update();
utility->bindVideoFilter();
utility->bindVideoShader();

View File

@ -103,5 +103,5 @@ void VideoSettings::synchronize() {
overscanHorizontal.value.setText({ config->video.maskOverscanHorizontal, "px" });
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
palette.update();
interface->updatePalette();
}