mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-03 06:02:52 +02:00
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:
@@ -1,7 +1,7 @@
|
|||||||
#ifndef BASE_HPP
|
#ifndef BASE_HPP
|
||||||
#define BASE_HPP
|
#define BASE_HPP
|
||||||
|
|
||||||
static const char Version[] = "088.02";
|
static const char Version[] = "088.03";
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
@@ -19,6 +19,7 @@ static const char Version[] = "088.02";
|
|||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
#include <nall/varint.hpp>
|
#include <nall/varint.hpp>
|
||||||
#include <nall/vector.hpp>
|
#include <nall/vector.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
using namespace nall;
|
using namespace nall;
|
||||||
|
|
||||||
//debugging function hook:
|
//debugging function hook:
|
||||||
@@ -39,8 +40,8 @@ template<typename R, typename... P> struct hook<R (P...)> {
|
|||||||
hook(const hook &hook) { callback = hook.callback; }
|
hook(const hook &hook) { callback = hook.callback; }
|
||||||
hook(void *function) { callback = function; }
|
hook(void *function) { callback = function; }
|
||||||
hook(R (*function)(P...)) { 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...), 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...) const, C *object) { callback = {function, object}; }
|
||||||
template<typename L> hook(const L& function) { callback = function; }
|
template<typename L> hook(const L& function) { callback = function; }
|
||||||
|
|
||||||
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }
|
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }
|
||||||
|
@@ -14,10 +14,10 @@ namespace GameBoy {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
|
|
||||||
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
|
void Cartridge::load(System::Revision revision, const string &markup, const stream &memory) {
|
||||||
if(size == 0) size = 32768;
|
romsize = memory.size() ? memory.size() : 32768u;
|
||||||
romdata = allocate<uint8>(romsize = size, 0xff);
|
romdata = allocate<uint8>(romsize, 0xff);
|
||||||
if(data) memcpy(romdata, data, size);
|
memory.read(romdata, memory.size());
|
||||||
|
|
||||||
information.markup = markup;
|
information.markup = markup;
|
||||||
information.mapper = Mapper::Unknown;
|
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";
|
information.battery = document["cartridge"]["ram"]["nonvolatile"].data == "true";
|
||||||
|
|
||||||
switch(information.mapper) { default:
|
switch(information.mapper) { default:
|
||||||
case Mapper::MBC0: mapper = &mbc0; break;
|
case Mapper::MBC0: mapper = &mbc0; break;
|
||||||
case Mapper::MBC1: mapper = &mbc1; break;
|
case Mapper::MBC1: mapper = &mbc1; break;
|
||||||
case Mapper::MBC2: mapper = &mbc2; break;
|
case Mapper::MBC2: mapper = &mbc2; break;
|
||||||
case Mapper::MBC3: mapper = &mbc3; break;
|
case Mapper::MBC3: mapper = &mbc3; break;
|
||||||
case Mapper::MBC5: mapper = &mbc5; break;
|
case Mapper::MBC5: mapper = &mbc5; break;
|
||||||
case Mapper::MMM01: mapper = &mmm01; break;
|
case Mapper::MMM01: mapper = &mmm01; break;
|
||||||
case Mapper::HuC1: mapper = &huc1; break;
|
case Mapper::HuC1: mapper = &huc1; break;
|
||||||
case Mapper::HuC3: mapper = &huc3; break;
|
case Mapper::HuC3: mapper = &huc3; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ramdata = new uint8_t[ramsize = information.ramsize]();
|
ramdata = new uint8_t[ramsize = information.ramsize]();
|
||||||
|
@@ -45,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||||||
MMIO *mapper;
|
MMIO *mapper;
|
||||||
bool bootrom_enable;
|
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();
|
void unload();
|
||||||
|
|
||||||
uint8 rom_read(unsigned addr);
|
uint8 rom_read(unsigned addr);
|
||||||
|
@@ -10,7 +10,12 @@ void Interface::lcdScanline() {
|
|||||||
void Interface::joypWrite(bool p15, bool p14) {
|
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) {
|
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||||
|
@@ -2,7 +2,8 @@ struct Interface {
|
|||||||
virtual void lcdScanline();
|
virtual void lcdScanline();
|
||||||
virtual void joypWrite(bool p15, bool p14);
|
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 void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
virtual bool inputPoll(unsigned id);
|
virtual bool inputPoll(unsigned id);
|
||||||
|
|
||||||
|
@@ -12,8 +12,8 @@ void PPU::cgb_render() {
|
|||||||
if(status.ob_enable) cgb_render_ob();
|
if(status.ob_enable) cgb_render_ob();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 *output = screen + status.ly * 160;
|
uint32 *output = screen + status.ly * 160;
|
||||||
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
|
||||||
interface->lcdScanline();
|
interface->lcdScanline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,8 +12,8 @@ void PPU::dmg_render() {
|
|||||||
if(status.ob_enable) dmg_render_ob();
|
if(status.ob_enable) dmg_render_ob();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 *output = screen + status.ly * 160;
|
uint32 *output = screen + status.ly * 160;
|
||||||
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[line[n]];
|
||||||
interface->lcdScanline();
|
interface->lcdScanline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ struct PPU : Thread, MMIO {
|
|||||||
uint8 obpi;
|
uint8 obpi;
|
||||||
} status;
|
} status;
|
||||||
|
|
||||||
uint16 screen[160 * 144];
|
uint32 screen[160 * 144];
|
||||||
uint16 line[160];
|
uint16 line[160];
|
||||||
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
|
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
|
||||||
uint8 origin[160];
|
uint8 origin[160];
|
||||||
|
@@ -5,20 +5,34 @@ namespace GameBoy {
|
|||||||
|
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
unsigned Video::palette_dmg(unsigned color) const {
|
void Video::generate_palette() {
|
||||||
unsigned R = monochrome[color][0] * 1023.0;
|
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
|
||||||
unsigned G = monochrome[color][1] * 1023.0;
|
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
|
||||||
unsigned B = monochrome[color][2] * 1023.0;
|
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 Video::palette_sgb(unsigned color) const {
|
||||||
unsigned R = (3 - color) * 341;
|
unsigned R = (3 - color) * 21845;
|
||||||
unsigned G = (3 - color) * 341;
|
unsigned G = (3 - color) * 21845;
|
||||||
unsigned B = (3 - color) * 341;
|
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 {
|
unsigned Video::palette_cgb(unsigned color) const {
|
||||||
@@ -34,42 +48,11 @@ unsigned Video::palette_cgb(unsigned color) const {
|
|||||||
G = min(960, G);
|
G = min(960, G);
|
||||||
B = min(960, B);
|
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) {
|
return interface->videoColor(color, R, G, B);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const double Video::monochrome[4][3] = {
|
const double Video::monochrome[4][3] = {
|
||||||
|
@@ -1,17 +1,15 @@
|
|||||||
struct Video {
|
struct Video {
|
||||||
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
|
uint32_t *palette;
|
||||||
unsigned *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();
|
||||||
~Video();
|
~Video();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const double monochrome[4][3];
|
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;
|
extern Video video;
|
||||||
|
@@ -4,7 +4,12 @@ namespace GameBoyAdvance {
|
|||||||
|
|
||||||
Interface *interface = nullptr;
|
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) {
|
void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
struct Interface {
|
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 void audioSample(int16_t lsample, int16_t rsample);
|
||||||
virtual bool inputPoll(unsigned id);
|
virtual bool inputPoll(unsigned id);
|
||||||
|
|
||||||
|
@@ -153,7 +153,7 @@ void PPU::frame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PPU::PPU() {
|
PPU::PPU() {
|
||||||
output = new uint16[240 * 160];
|
output = new uint32[240 * 160];
|
||||||
blur = new uint16[240 * 160];
|
blur = new uint16[240 * 160];
|
||||||
|
|
||||||
regs.bg[0].id = BG0;
|
regs.bg[0].id = BG0;
|
||||||
|
@@ -3,7 +3,7 @@ struct PPU : Thread, MMIO {
|
|||||||
uint16 pram[512];
|
uint16 pram[512];
|
||||||
#include "registers.hpp"
|
#include "registers.hpp"
|
||||||
#include "state.hpp"
|
#include "state.hpp"
|
||||||
uint16 *output;
|
uint32 *output;
|
||||||
uint16 *blur;
|
uint16 *blur;
|
||||||
|
|
||||||
static void Enter();
|
static void Enter();
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
void PPU::render_forceblank() {
|
void PPU::render_forceblank() {
|
||||||
uint16 *line = output + regs.vcounter * 240;
|
uint32 *line = output + regs.vcounter * 240;
|
||||||
uint16 *last = blur + regs.vcounter * 240;
|
uint16 *last = blur + regs.vcounter * 240;
|
||||||
for(unsigned x = 0; x < 240; x++) {
|
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;
|
last[x] = 0x7fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::render_screen() {
|
void PPU::render_screen() {
|
||||||
uint16 *line = output + regs.vcounter * 240;
|
uint32 *line = output + regs.vcounter * 240;
|
||||||
uint16 *last = blur + regs.vcounter * 240;
|
uint16 *last = blur + regs.vcounter * 240;
|
||||||
|
|
||||||
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
|
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
|
//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;
|
last[x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,40 +4,17 @@ namespace GameBoyAdvance {
|
|||||||
|
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
unsigned Video::color(unsigned color) const {
|
void Video::generate_palette() {
|
||||||
uint5 b = color >> 10;
|
for(unsigned color = 0; color < (1 << 15); color++) {
|
||||||
uint5 g = color >> 5;
|
uint5 b = color >> 10;
|
||||||
uint5 r = color >> 0;
|
uint5 g = color >> 5;
|
||||||
|
uint5 r = color >> 0;
|
||||||
|
|
||||||
uint10 R = (r << 5) | (r << 0);
|
uint16 R = r << 11 | r << 6 | r << 1 | r >> 4;
|
||||||
uint10 G = (g << 5) | (g << 0);
|
uint16 G = g << 11 | g << 6 | g << 1 | g >> 4;
|
||||||
uint10 B = (b << 5) | (b << 0);
|
uint16 B = b << 11 | b << 6 | b << 1 | b >> 4;
|
||||||
|
|
||||||
return (R << 20) | (G << 10) | (B << 0);
|
palette[color] = interface->videoColor(color, R, G, B);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
struct Video {
|
struct Video {
|
||||||
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
|
|
||||||
unsigned *palette;
|
unsigned *palette;
|
||||||
|
void generate_palette();
|
||||||
|
|
||||||
unsigned color(unsigned color) const;
|
|
||||||
void generate(Format format);
|
|
||||||
Video();
|
Video();
|
||||||
~Video();
|
~Video();
|
||||||
};
|
};
|
||||||
|
@@ -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
|
|
@@ -1,6 +1,8 @@
|
|||||||
#ifndef NALL_BIT_HPP
|
#ifndef NALL_BIT_HPP
|
||||||
#define NALL_BIT_HPP
|
#define NALL_BIT_HPP
|
||||||
|
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
template<unsigned bits>
|
template<unsigned bits>
|
||||||
inline uintmax_t uclamp(const uintmax_t x) {
|
inline uintmax_t uclamp(const uintmax_t x) {
|
||||||
|
@@ -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
|
|
@@ -6,6 +6,7 @@
|
|||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
#include <nall/windows/utf8.hpp>
|
#include <nall/windows/utf8.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
||||||
@@ -16,25 +17,19 @@ namespace nall {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
class file {
|
struct file {
|
||||||
public:
|
|
||||||
enum class mode : unsigned { read, write, readwrite, writeread };
|
enum class mode : unsigned { read, write, readwrite, writeread };
|
||||||
enum class index : unsigned { absolute, relative };
|
enum class index : unsigned { absolute, relative };
|
||||||
enum class time : unsigned { create, modify, access };
|
enum class time : unsigned { create, modify, access };
|
||||||
|
|
||||||
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
|
static vector<uint8_t> read(const string &filename) {
|
||||||
data = 0;
|
vector<uint8_t> memory;
|
||||||
file fp;
|
file fp;
|
||||||
if(fp.open(filename, mode::read) == false) return false;
|
if(fp.open(filename, mode::read)) {
|
||||||
size = fp.size();
|
memory.resize(fp.size());
|
||||||
data = new uint8_t[size];
|
fp.read(memory.data(), memory.size());
|
||||||
fp.read(data, size);
|
}
|
||||||
fp.close();
|
return memory;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
|
|
||||||
return file::read(filename, (uint8_t*&)data, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||||
|
@@ -19,12 +19,10 @@ struct gzip {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool gzip::decompress(const string &filename) {
|
bool gzip::decompress(const string &filename) {
|
||||||
uint8_t *data;
|
if(auto memory = file::read(filename)) {
|
||||||
unsigned size;
|
return decompress(memory.data(), memory.size());
|
||||||
if(file::read(filename, data, size) == false) return false;
|
}
|
||||||
bool result = decompress(data, size);
|
return false;
|
||||||
delete[] data;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
||||||
|
@@ -11,8 +11,6 @@ struct ips {
|
|||||||
inline bool apply();
|
inline bool apply();
|
||||||
inline void source(const uint8_t *data, unsigned size);
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
inline void modify(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();
|
||||||
inline ~ips();
|
inline ~ips();
|
||||||
|
|
||||||
@@ -88,14 +86,6 @@ void ips::modify(const uint8_t *data, unsigned size) {
|
|||||||
modifyData = data, modifySize = 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) {
|
ips::ips() : data(nullptr), sourceData(nullptr), modifyData(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -58,12 +58,10 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool png::decode(const string &filename) {
|
bool png::decode(const string &filename) {
|
||||||
uint8_t *data;
|
if(auto memory = file::read(filename)) {
|
||||||
unsigned size;
|
return decode(memory.data(), memory.size());
|
||||||
if(file::read(filename, data, size) == false) return false;
|
}
|
||||||
bool result = decode(data, size);
|
return false;
|
||||||
delete[] data;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
#ifdef NALL_STREAM_INTERNAL_HPP
|
#ifndef NALL_STREAM_AUTO_HPP
|
||||||
|
#define NALL_STREAM_AUTO_HPP
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
|
|
||||||
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct filestream : stream {
|
struct filestream : stream {
|
||||||
inline bool seekable() const { return true; }
|
bool seekable() const { return true; }
|
||||||
inline bool readable() const { return true; }
|
bool readable() const { return true; }
|
||||||
inline bool writable() const { return pwritable; }
|
bool writable() const { return pwritable; }
|
||||||
inline bool randomaccess() const { return false; }
|
bool randomaccess() const { return false; }
|
||||||
|
|
||||||
inline unsigned size() const { return pfile.size(); }
|
unsigned size() const { return pfile.size(); }
|
||||||
inline unsigned offset() const { return pfile.offset(); }
|
unsigned offset() const { return pfile.offset(); }
|
||||||
inline void seek(unsigned offset) const { pfile.seek(offset); }
|
void seek(unsigned offset) const { pfile.seek(offset); }
|
||||||
|
|
||||||
inline uint8_t read() const { return pfile.read(); }
|
uint8_t read() const { return pfile.read(); }
|
||||||
inline void write(uint8_t data) const { pfile.write(data); }
|
void write(uint8_t data) const { pfile.write(data); }
|
||||||
|
|
||||||
inline filestream(const string &filename) {
|
filestream(const string &filename) {
|
||||||
pfile.open(filename, file::mode::readwrite);
|
pfile.open(filename, file::mode::readwrite);
|
||||||
pwritable = pfile.open();
|
pwritable = pfile.open();
|
||||||
if(!pwritable) pfile.open(filename, file::mode::read);
|
if(!pwritable) pfile.open(filename, file::mode::read);
|
||||||
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct gzipstream : memorystream {
|
struct gzipstream : memorystream {
|
||||||
inline gzipstream(const stream &stream) {
|
gzipstream(const stream &stream) {
|
||||||
unsigned size = stream.size();
|
unsigned size = stream.size();
|
||||||
uint8_t *data = new uint8_t[size];
|
uint8_t *data = new uint8_t[size];
|
||||||
stream.read(data, size);
|
stream.read(data, size);
|
||||||
@@ -18,7 +21,7 @@ struct gzipstream : memorystream {
|
|||||||
memcpy(pdata, archive.data, psize);
|
memcpy(pdata, archive.data, psize);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ~gzipstream() {
|
~gzipstream() {
|
||||||
if(pdata) delete[] pdata;
|
if(pdata) delete[] pdata;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct httpstream : stream {
|
struct httpstream : stream {
|
||||||
inline bool seekable() const { return true; }
|
bool seekable() const { return true; }
|
||||||
inline bool readable() const { return true; }
|
bool readable() const { return true; }
|
||||||
inline bool writable() const { return true; }
|
bool writable() const { return true; }
|
||||||
inline bool randomaccess() const { return true; }
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
inline unsigned size() const { return psize; }
|
unsigned size() const { return psize; }
|
||||||
inline unsigned offset() const { return poffset; }
|
unsigned offset() const { return poffset; }
|
||||||
inline void seek(unsigned offset) const { poffset = offset; }
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
inline uint8_t read() const { return pdata[poffset++]; }
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
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;
|
string uri = url;
|
||||||
uri.ltrim<1>("http://");
|
uri.ltrim<1>("http://");
|
||||||
lstring part = uri.split<1>("/");
|
lstring part = uri.split<1>("/");
|
||||||
@@ -29,7 +32,7 @@ struct httpstream : stream {
|
|||||||
connection.download(part[1], pdata, psize);
|
connection.download(part[1], pdata, psize);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ~httpstream() {
|
~httpstream() {
|
||||||
if(pdata) delete[] pdata;
|
if(pdata) delete[] pdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct memorystream : stream {
|
struct memorystream : stream {
|
||||||
inline bool seekable() const { return true; }
|
bool seekable() const { return true; }
|
||||||
inline bool readable() const { return true; }
|
bool readable() const { return true; }
|
||||||
inline bool writable() const { return pwritable; }
|
bool writable() const { return pwritable; }
|
||||||
inline bool randomaccess() const { return true; }
|
bool randomaccess() const { return true; }
|
||||||
|
|
||||||
inline unsigned size() const { return psize; }
|
uint8_t *data() const { return pdata; }
|
||||||
inline unsigned offset() const { return poffset; }
|
unsigned size() const { return psize; }
|
||||||
inline void seek(unsigned offset) const { poffset = offset; }
|
unsigned offset() const { return poffset; }
|
||||||
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
inline uint8_t read() const { return pdata[poffset++]; }
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
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;
|
pdata = data, psize = size, poffset = 0;
|
||||||
pwritable = true;
|
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;
|
pdata = (uint8_t*)data, psize = size, poffset = 0;
|
||||||
pwritable = false;
|
pwritable = false;
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct mmapstream : stream {
|
struct mmapstream : stream {
|
||||||
inline bool seekable() const { return true; }
|
bool seekable() const { return true; }
|
||||||
inline bool readable() const { return true; }
|
bool readable() const { return true; }
|
||||||
inline bool writable() const { return pwritable; }
|
bool writable() const { return pwritable; }
|
||||||
inline bool randomaccess() const { return false; }
|
bool randomaccess() const { return false; }
|
||||||
|
|
||||||
inline unsigned size() const { return pmmap.size(); }
|
unsigned size() const { return pmmap.size(); }
|
||||||
inline unsigned offset() const { return poffset; }
|
unsigned offset() const { return poffset; }
|
||||||
inline void seek(unsigned offset) const { poffset = offset; }
|
void seek(unsigned offset) const { poffset = offset; }
|
||||||
|
|
||||||
inline uint8_t read() const { return pdata[poffset++]; }
|
uint8_t read() const { return pdata[poffset++]; }
|
||||||
inline void write(uint8_t data) const { pdata[poffset++] = data; }
|
void write(uint8_t data) const { pdata[poffset++] = data; }
|
||||||
|
|
||||||
inline uint8_t read(unsigned offset) const { return pdata[offset]; }
|
uint8_t read(unsigned offset) const { return pdata[offset]; }
|
||||||
inline void write(unsigned offset, uint8_t data) const { pdata[offset] = data; }
|
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);
|
pmmap.open(filename, filemap::mode::readwrite);
|
||||||
pwritable = pmmap.open();
|
pwritable = pmmap.open();
|
||||||
if(!pwritable) pmmap.open(filename, filemap::mode::read);
|
if(!pwritable) pmmap.open(filename, filemap::mode::read);
|
||||||
|
@@ -9,6 +9,7 @@ struct stream {
|
|||||||
virtual bool writable() const = 0;
|
virtual bool writable() const = 0;
|
||||||
virtual bool randomaccess() const = 0;
|
virtual bool randomaccess() const = 0;
|
||||||
|
|
||||||
|
virtual uint8_t* data() const { return nullptr; }
|
||||||
virtual unsigned size() const = 0;
|
virtual unsigned size() const = 0;
|
||||||
virtual unsigned offset() const = 0;
|
virtual unsigned offset() const = 0;
|
||||||
virtual void seek(unsigned offset) const = 0;
|
virtual void seek(unsigned offset) const = 0;
|
||||||
@@ -16,44 +17,45 @@ struct stream {
|
|||||||
virtual uint8_t read() const = 0;
|
virtual uint8_t read() const = 0;
|
||||||
virtual void write(uint8_t data) const = 0;
|
virtual void write(uint8_t data) const = 0;
|
||||||
|
|
||||||
inline virtual uint8_t read(unsigned) const { return 0; }
|
virtual uint8_t read(unsigned) const { return 0; }
|
||||||
inline virtual void write(unsigned, uint8_t) const {}
|
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();
|
return offset() >= size();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void copy(uint8_t *&data, unsigned &length) const {
|
uintmax_t readl(unsigned length = 1) 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 data = 0, shift = 0;
|
uintmax_t data = 0, shift = 0;
|
||||||
while(length--) { data |= read() << shift; shift += 8; }
|
while(length--) { data |= read() << shift; shift += 8; }
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uintmax_t readm(unsigned length = 1) const {
|
uintmax_t readm(unsigned length = 1) const {
|
||||||
uintmax_t data = 0;
|
uintmax_t data = 0;
|
||||||
while(length--) data = (data << 8) | read();
|
while(length--) data = (data << 8) | read();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void read(uint8_t *data, unsigned length) const {
|
void read(uint8_t *data, unsigned length) const {
|
||||||
while(length--) *data++ = read();
|
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--) {
|
while(length--) {
|
||||||
write(data);
|
write(data);
|
||||||
data >>= 8;
|
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;
|
uintmax_t shift = 8 * length;
|
||||||
while(length--) {
|
while(length--) {
|
||||||
shift -= 8;
|
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++);
|
while(length--) write(*data++);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct byte {
|
struct byte {
|
||||||
inline operator uint8_t() const { return s.read(offset); }
|
operator uint8_t() const { return s.read(offset); }
|
||||||
inline byte& operator=(uint8_t data) { s.write(offset, data); }
|
byte& operator=(uint8_t data) { s.write(offset, data); }
|
||||||
inline byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
byte(const stream &s, unsigned offset) : s(s), offset(offset) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const stream &s;
|
const stream &s;
|
||||||
const unsigned offset;
|
const unsigned offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline byte operator[](unsigned offset) const {
|
byte operator[](unsigned offset) const {
|
||||||
return byte(*this, offset);
|
return byte(*this, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline stream() {}
|
stream() {}
|
||||||
inline virtual ~stream() {}
|
virtual ~stream() {}
|
||||||
stream(const stream&) = delete;
|
stream(const stream&) = delete;
|
||||||
stream& operator=(const stream&) = delete;
|
stream& operator=(const stream&) = delete;
|
||||||
};
|
};
|
||||||
|
36
bsnes/nall/stream/vector.hpp
Executable file
36
bsnes/nall/stream/vector.hpp
Executable 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
|
@@ -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 {
|
namespace nall {
|
||||||
|
|
||||||
struct zipstream : memorystream {
|
struct zipstream : memorystream {
|
||||||
inline zipstream(const stream &stream, const string &filter = "*") {
|
zipstream(const stream &stream, const string &filter = "*") {
|
||||||
unsigned size = stream.size();
|
unsigned size = stream.size();
|
||||||
uint8_t *data = new uint8_t[size];
|
uint8_t *data = new uint8_t[size];
|
||||||
stream.read(data, size);
|
stream.read(data, size);
|
||||||
@@ -20,7 +23,7 @@ struct zipstream : memorystream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ~zipstream() {
|
~zipstream() {
|
||||||
if(pdata) delete[] pdata;
|
if(pdata) delete[] pdata;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
#include <nall/array.hpp>
|
|
||||||
#include <nall/atoi.hpp>
|
#include <nall/atoi.hpp>
|
||||||
#include <nall/function.hpp>
|
#include <nall/function.hpp>
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
|
@@ -21,7 +21,10 @@ namespace nall {
|
|||||||
unsigned objectsize;
|
unsigned objectsize;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
operator bool() const { return pool; }
|
||||||
T* data() { return pool; }
|
T* data() { return pool; }
|
||||||
|
|
||||||
|
bool empty() const { return pool == nullptr; }
|
||||||
unsigned size() const { return objectsize; }
|
unsigned size() const { return objectsize; }
|
||||||
unsigned capacity() const { return poolsize; }
|
unsigned capacity() const { return poolsize; }
|
||||||
|
|
||||||
|
@@ -4,7 +4,12 @@ namespace Famicom {
|
|||||||
|
|
||||||
Interface *interface = nullptr;
|
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) {
|
void Interface::audioSample(const int16_t sample) {
|
||||||
@@ -18,4 +23,8 @@ void Interface::message(const string &text) {
|
|||||||
print(text, "\n");
|
print(text, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interface::loadCartridge(const string &markup, const stream &memory) {
|
||||||
|
cartridge.load(markup, memory.data(), memory.size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
struct Interface {
|
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 void audioSample(int16_t sample);
|
||||||
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||||
|
|
||||||
virtual void message(const string &text);
|
virtual void message(const string &text);
|
||||||
|
|
||||||
|
void loadCartridge(const string &markup, const stream &memory);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
@@ -282,7 +282,7 @@ void PPU::scrolly_increment() {
|
|||||||
//
|
//
|
||||||
|
|
||||||
void PPU::raster_pixel() {
|
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 mask = 0x8000 >> (status.xaddr + (status.lx & 7));
|
||||||
unsigned palette = 0, object_palette = 0;
|
unsigned palette = 0, object_palette = 0;
|
||||||
@@ -325,7 +325,7 @@ void PPU::raster_pixel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(raster_enable() == false) palette = 0;
|
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() {
|
void PPU::raster_sprite() {
|
||||||
|
@@ -98,7 +98,7 @@ struct PPU : Thread {
|
|||||||
} oam[8], soam[8];
|
} oam[8], soam[8];
|
||||||
} raster;
|
} raster;
|
||||||
|
|
||||||
uint16 buffer[256 * 262];
|
uint32 buffer[256 * 262];
|
||||||
uint8 ciram[2048];
|
uint8 ciram[2048];
|
||||||
uint8 cgram[32];
|
uint8 cgram[32];
|
||||||
uint8 oam[256];
|
uint8 oam[256];
|
||||||
|
@@ -5,7 +5,19 @@ namespace Famicom {
|
|||||||
|
|
||||||
Video video;
|
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,
|
unsigned n, double saturation, double hue,
|
||||||
double contrast, double brightness, double gamma
|
double contrast, double brightness, double gamma
|
||||||
) {
|
) {
|
||||||
@@ -46,43 +58,11 @@ unsigned Video::palette30(
|
|||||||
q *= saturation;
|
q *= saturation;
|
||||||
|
|
||||||
auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); };
|
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 r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q);
|
||||||
unsigned g = 1023.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
unsigned g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q);
|
||||||
unsigned b = 1023.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
|
unsigned b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q);
|
||||||
return (uclamp<10>(r) << 20) + (uclamp<10>(g) << 10) + (uclamp<10>(b) << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Video::generate(Format format) {
|
return interface->videoColor(n, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
struct Video {
|
struct Video {
|
||||||
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
|
|
||||||
unsigned *palette;
|
unsigned *palette;
|
||||||
|
void generate_palette();
|
||||||
|
|
||||||
unsigned palette30(unsigned, double, double, double, double, double);
|
|
||||||
void generate(Format format);
|
|
||||||
Video();
|
Video();
|
||||||
~Video();
|
~Video();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t generate_color(unsigned, double, double, double, double, double);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Video video;
|
extern Video video;
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
#ifndef RUBY_H
|
#ifndef RUBY_H
|
||||||
#define RUBY_H
|
#define RUBY_H
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
#include <nall/any.hpp>
|
#include <nall/any.hpp>
|
||||||
#include <nall/array.hpp>
|
|
||||||
#include <nall/bit.hpp>
|
#include <nall/bit.hpp>
|
||||||
#include <nall/input.hpp>
|
#include <nall/input.hpp>
|
||||||
#include <nall/intrinsics.hpp>
|
#include <nall/intrinsics.hpp>
|
||||||
|
@@ -70,6 +70,7 @@ void ICD2::reset() {
|
|||||||
pulselock = true;
|
pulselock = true;
|
||||||
|
|
||||||
GameBoy::interface = this;
|
GameBoy::interface = this;
|
||||||
|
GameBoy::video.generate_palette();
|
||||||
GameBoy::system.init();
|
GameBoy::system.init();
|
||||||
GameBoy::system.power();
|
GameBoy::system.power();
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ void ICD2::lcdScanline() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::ppu.status.ly & 7) * 160);
|
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) {
|
void ICD2::joypWrite(bool p15, bool p14) {
|
||||||
@@ -80,7 +80,11 @@ void ICD2::joypWrite(bool p15, bool p14) {
|
|||||||
packetlock = true;
|
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) {
|
void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
void lcdScanline();
|
void lcdScanline();
|
||||||
void joypWrite(bool p15, bool p14);
|
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);
|
void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
bool inputPoll(unsigned id);
|
bool inputPoll(unsigned id);
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#ifdef ICD2_CPP
|
#ifdef ICD2_CPP
|
||||||
|
|
||||||
//convert linear pixel data { 0x00, 0x55, 0xaa, 0xff } to 2bpp planar tiledata
|
//convert linear pixel data to 2bpp planar tiledata
|
||||||
void ICD2::render(const uint16 *source) {
|
void ICD2::render(const uint32 *source) {
|
||||||
memset(lcd.output, 0x00, 320 * sizeof(uint16));
|
memset(lcd.output, 0x00, 320 * sizeof(uint16));
|
||||||
|
|
||||||
for(unsigned y = 0; y < 8; y++) {
|
for(unsigned y = 0; y < 8; y++) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
void render(const uint16 *source);
|
void render(const uint32 *source);
|
||||||
|
|
||||||
uint8 r6000_ly; //SGB BIOS' cache of LY
|
uint8 r6000_ly; //SGB BIOS' cache of LY
|
||||||
uint8 r6000_row; //SGB BIOS' cache of ROW
|
uint8 r6000_row; //SGB BIOS' cache of ROW
|
||||||
@@ -13,7 +13,7 @@ unsigned r7800; //VRAM offset
|
|||||||
uint8 mlt_req; //number of active joypads
|
uint8 mlt_req; //number of active joypads
|
||||||
|
|
||||||
struct LCD {
|
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
|
uint16 output[320]; //one tile row of 2bpp video data
|
||||||
unsigned row; //active ICD2 rendering tile row
|
unsigned row; //active ICD2 rendering tile row
|
||||||
} lcd;
|
} lcd;
|
||||||
|
@@ -4,6 +4,11 @@ namespace SuperFamicom {
|
|||||||
|
|
||||||
Interface *interface = nullptr;
|
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) {
|
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
struct Interface {
|
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 videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
||||||
virtual void audioSample(int16_t lsample, int16_t rsample);
|
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||||
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
|
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
|
||||||
|
@@ -150,7 +150,7 @@ uint32 PPU::Screen::get_pixel(bool swap) {
|
|||||||
//========
|
//========
|
||||||
|
|
||||||
if(self.regs.display_disable) return 0;
|
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) {
|
uint16 PPU::Screen::addsub(unsigned x, unsigned y, bool halve) {
|
||||||
|
@@ -2,48 +2,25 @@
|
|||||||
|
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
unsigned Video::palette30(unsigned color) {
|
void Video::generate_palette() {
|
||||||
unsigned l = (color >> 15) & 15;
|
for(unsigned color = 0; color < (1 << 19); color++) {
|
||||||
unsigned b = (color >> 10) & 31;
|
unsigned l = (color >> 15) & 15;
|
||||||
unsigned g = (color >> 5) & 31;
|
unsigned b = (color >> 10) & 31;
|
||||||
unsigned r = (color >> 0) & 31;
|
unsigned g = (color >> 5) & 31;
|
||||||
|
unsigned r = (color >> 0) & 31;
|
||||||
|
|
||||||
double L = (1.0 + l) / 16.0;
|
double L = (1.0 + l) / 16.0;
|
||||||
if(l == 0) L *= 0.5;
|
if(l == 0) L *= 0.5;
|
||||||
unsigned R = L * ((r << 5) + (r << 0));
|
unsigned R = L * (r << 11 | r << 6 | r << 1 | r >> 4);
|
||||||
unsigned G = L * ((g << 5) + (g << 0));
|
unsigned G = L * (g << 11 | g << 6 | g << 1 | g >> 4);
|
||||||
unsigned B = L * ((b << 5) + (b << 0));
|
unsigned B = L * (b << 11 | b << 6 | b << 1 | b >> 4);
|
||||||
|
|
||||||
return (R << 20) + (G << 10) + (B << 0);
|
palette[color] = interface->videoColor(color, R, G, B);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::Video() {
|
Video::Video() {
|
||||||
palette = new unsigned[1 << 19];
|
palette = new unsigned[1 << 19]();
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::~Video() {
|
Video::~Video() {
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
struct Video {
|
struct Video {
|
||||||
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
|
|
||||||
unsigned *palette;
|
unsigned *palette;
|
||||||
|
void generate_palette();
|
||||||
unsigned palette30(unsigned color);
|
|
||||||
void generate(Format format);
|
|
||||||
Video();
|
Video();
|
||||||
~Video();
|
~Video();
|
||||||
|
|
||||||
|
@@ -16,6 +16,8 @@ namespace GBA = GameBoyAdvance;
|
|||||||
#include <nall/filemap.hpp>
|
#include <nall/filemap.hpp>
|
||||||
#include <nall/input.hpp>
|
#include <nall/input.hpp>
|
||||||
#include <nall/bps/patch.hpp>
|
#include <nall/bps/patch.hpp>
|
||||||
|
#include <nall/stream/memory.hpp>
|
||||||
|
#include <nall/stream/vector.hpp>
|
||||||
#include <nall/nes/cartridge.hpp>
|
#include <nall/nes/cartridge.hpp>
|
||||||
#include <nall/snes/cartridge.hpp>
|
#include <nall/snes/cartridge.hpp>
|
||||||
#include <nall/gb/cartridge.hpp>
|
#include <nall/gb/cartridge.hpp>
|
||||||
|
@@ -12,18 +12,50 @@ bool InterfaceCore::loadFirmware(string filename, string keyname, uint8_t *targe
|
|||||||
string firmware = key["firmware"].data;
|
string firmware = key["firmware"].data;
|
||||||
string hash = key["sha256"].data;
|
string hash = key["sha256"].data;
|
||||||
|
|
||||||
uint8_t *data;
|
if(auto memory = file::read({dir(filename),firmware})) {
|
||||||
unsigned size;
|
if(nall::sha256(memory.data(), memory.size()) == hash) {
|
||||||
if(file::read({dir(filename),firmware}, data, size) == true) {
|
memcpy(targetdata, memory.data(), min(targetsize, memory.size()));
|
||||||
if(nall::sha256(data, size) == hash) {
|
|
||||||
memcpy(targetdata, data, min(targetsize, size));
|
|
||||||
result = true;
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
MessageWindow::information(Window::None, {"Warning: Firmware SHA256 sum is incorrect:\n\n", dir(filename), firmware});
|
MessageWindow::information(Window::None, {"Warning: Firmware SHA256 sum is incorrect:\n\n", dir(filename), firmware});
|
||||||
}
|
}
|
||||||
delete[] data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
@@ -18,28 +18,26 @@ bool InterfaceGB::cartridgeLoaded() {
|
|||||||
bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &filename) {
|
bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &filename) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data;
|
vector<uint8_t> memory;
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
if(filename.endswith("/")) {
|
if(filename.endswith("/")) {
|
||||||
if(file::read({ filename, "program.rom" }, data, size) == false) return false;
|
memory = file::read({filename, "program.rom"});
|
||||||
interface->base = { true, filename };
|
interface->base = {true, filename};
|
||||||
} else {
|
} else {
|
||||||
if(file::read(filename, data, size) == false) return false;
|
memory = file::read(filename);
|
||||||
interface->base = { false, nall::basename(filename) };
|
interface->base = {false, nall::basename(filename)};
|
||||||
}
|
}
|
||||||
|
if(memory.empty()) return false;
|
||||||
|
|
||||||
interface->game = interface->base;
|
interface->game = interface->base;
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
interface->applyPatch(interface->base, data, size);
|
interface->applyPatch(interface->base, memory);
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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();
|
GB::system.power();
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(GB::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
filemap fp;
|
filemap fp;
|
||||||
@@ -49,7 +47,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
|
|||||||
}
|
}
|
||||||
|
|
||||||
GB::interface = this;
|
GB::interface = this;
|
||||||
GB::video.generate(GB::Video::Format::RGB30);
|
GB::video.generate_palette();
|
||||||
interface->loadCartridge(::Interface::Mode::GB);
|
interface->loadCartridge(::Interface::Mode::GB);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -103,19 +101,12 @@ void InterfaceGB::setCheats(const lstring &list) {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void InterfaceGB::videoRefresh(const uint16_t *data) {
|
uint32_t InterfaceGB::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
static uint32_t output[160 * 144];
|
return color(red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
for(unsigned y = 0; y < 144; y++) {
|
void InterfaceGB::videoRefresh(const uint32_t *data) {
|
||||||
const uint16_t *sp = data + y * 160;
|
interface->videoRefresh(data, 160 * sizeof(uint32_t), 160, 144);
|
||||||
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::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {
|
void InterfaceGB::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {
|
||||||
|
@@ -16,7 +16,8 @@ struct InterfaceGB : InterfaceCore, GB::Interface {
|
|||||||
|
|
||||||
void setCheats(const lstring &list = lstring{});
|
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);
|
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
|
||||||
bool inputPoll(unsigned id);
|
bool inputPoll(unsigned id);
|
||||||
};
|
};
|
||||||
|
@@ -16,28 +16,26 @@ bool InterfaceGBA::cartridgeLoaded() {
|
|||||||
bool InterfaceGBA::loadCartridge(const string &filename) {
|
bool InterfaceGBA::loadCartridge(const string &filename) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data;
|
vector<uint8_t> memory;
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
if(filename.endswith("/")) {
|
if(filename.endswith("/")) {
|
||||||
if(file::read({filename, "program.rom"}, data, size) == false) return false;
|
memory = file::read({filename, "program.rom"});
|
||||||
interface->base = {true, filename};
|
interface->base = {true, filename};
|
||||||
} else {
|
} else {
|
||||||
if(file::read(filename, data, size) == false) return false;
|
memory = file::read(filename);
|
||||||
interface->base = {false, nall::basename(filename)};
|
interface->base = {false, nall::basename(filename)};
|
||||||
}
|
}
|
||||||
|
if(memory.empty()) return false;
|
||||||
|
|
||||||
interface->game = interface->base;
|
interface->game = interface->base;
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
interface->applyPatch(interface->base, data, size);
|
interface->applyPatch(interface->base, memory);
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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();
|
GBA::system.power();
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(GBA::cartridge.ram_size()) {
|
if(GBA::cartridge.ram_size()) {
|
||||||
filemap fp;
|
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);
|
interface->loadCartridge(::Interface::Mode::GBA);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -86,19 +84,12 @@ void InterfaceGBA::setCheats(const lstring &list) {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void InterfaceGBA::videoRefresh(const uint16_t *data) {
|
uint32_t InterfaceGBA::videoColor(uint15_t source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
static uint32_t output[240 * 160];
|
return color(red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
for(unsigned y = 0; y < 160; y++) {
|
void InterfaceGBA::videoRefresh(const uint32_t *data) {
|
||||||
const uint16_t *sp = data + y * 240;
|
interface->videoRefresh(data, 240 * sizeof(uint32_t), 240, 160);
|
||||||
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::audioSample(int16_t lsample, int16_t rsample) {
|
void InterfaceGBA::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
|
@@ -16,7 +16,8 @@ struct InterfaceGBA : InterfaceCore, GBA::Interface {
|
|||||||
|
|
||||||
void setCheats(const lstring &list = lstring{});
|
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);
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
bool inputPoll(unsigned id);
|
bool inputPoll(unsigned id);
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
#include "../base.hpp"
|
#include "../base.hpp"
|
||||||
#include "core.cpp"
|
#include "core.cpp"
|
||||||
#include "palette.cpp"
|
|
||||||
#include "nes/nes.cpp"
|
#include "nes/nes.cpp"
|
||||||
#include "snes/snes.cpp"
|
#include "snes/snes.cpp"
|
||||||
#include "gb/gb.cpp"
|
#include "gb/gb.cpp"
|
||||||
@@ -169,13 +168,12 @@ bool Interface::saveState(unsigned slot) {
|
|||||||
|
|
||||||
bool Interface::loadState(unsigned slot) {
|
bool Interface::loadState(unsigned slot) {
|
||||||
string filename = game.filename({ "state-", slot, ".bst" }, { "-", slot, ".bst" });
|
string filename = game.filename({ "state-", slot, ".bst" }, { "-", slot, ".bst" });
|
||||||
uint8_t *data;
|
auto memory = file::read(filename);
|
||||||
unsigned size;
|
if(memory.empty()) {
|
||||||
if(file::read(filename, data, size) == false) {
|
|
||||||
utility->showMessage(string{ "Slot ", slot, " save file not found" });
|
utility->showMessage(string{ "Slot ", slot, " save file not found" });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
serializer s(data, size);
|
serializer s(memory.data(), memory.size());
|
||||||
bool result = unserialize(s);
|
bool result = unserialize(s);
|
||||||
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
|
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
|
||||||
return result;
|
return result;
|
||||||
@@ -185,6 +183,15 @@ void Interface::setCheatCodes(const lstring &list) {
|
|||||||
if(core) return core->setCheats(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() {
|
string Interface::sha256() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return NES::cartridge.sha256();
|
case Mode::NES: return NES::cartridge.sha256();
|
||||||
@@ -205,13 +212,13 @@ Interface::Interface() : core(nullptr) {
|
|||||||
|
|
||||||
//internal
|
//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");
|
string patchname = filepath.filename("patch.bps", ".bps");
|
||||||
if(file::exists(patchname) == false) return false;
|
if(file::exists(patchname) == false) return false;
|
||||||
|
|
||||||
bpspatch bps;
|
bpspatch bps;
|
||||||
bps.modify(patchname);
|
bps.modify(patchname);
|
||||||
bps.source(data, size);
|
bps.source(memory.data(), memory.size());
|
||||||
unsigned targetSize = bps.size();
|
unsigned targetSize = bps.size();
|
||||||
uint8_t *targetData = new uint8_t[targetSize];
|
uint8_t *targetData = new uint8_t[targetSize];
|
||||||
bps.target(targetData, targetSize);
|
bps.target(targetData, targetSize);
|
||||||
@@ -220,9 +227,9 @@ bool Interface::applyPatch(CartridgePath &filepath, uint8_t *&data, unsigned &si
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] data;
|
memory.resize(targetSize);
|
||||||
data = targetData;
|
memcpy(memory.data(), targetData, targetSize);
|
||||||
size = targetSize;
|
delete[] targetData;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +237,7 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
|
|||||||
uint32_t *output;
|
uint32_t *output;
|
||||||
unsigned outputPitch;
|
unsigned outputPitch;
|
||||||
|
|
||||||
if(filter.opened()) {
|
if(application->depth == 30 && filter.opened()) {
|
||||||
filter.render(input, inputPitch, width, height);
|
filter.render(input, inputPitch, width, height);
|
||||||
input = filter.data;
|
input = filter.data;
|
||||||
inputPitch = filter.pitch;
|
inputPitch = filter.pitch;
|
||||||
@@ -242,11 +249,21 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
|
|||||||
inputPitch >>= 2, outputPitch >>= 2;
|
inputPitch >>= 2, outputPitch >>= 2;
|
||||||
|
|
||||||
for(unsigned y = 0; y < height; y++) {
|
for(unsigned y = 0; y < height; y++) {
|
||||||
const uint32_t *sp = input + y * inputPitch;
|
memcpy(output + y * outputPitch, input + y * inputPitch, width * sizeof(uint32_t));
|
||||||
uint32_t *dp = output + y * outputPitch;
|
}
|
||||||
for(unsigned x = 0; x < width; x++) {
|
|
||||||
uint32_t color = *sp++;
|
if(config->video.maskOverscan && (mode() == Mode::NES || mode() == Mode::SNES)) {
|
||||||
*dp++ = palette((color >> 20) & 1023, (color >> 10) & 1023, (color >> 0) & 1023);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#include "palette.hpp"
|
|
||||||
|
|
||||||
struct InterfaceCore {
|
struct InterfaceCore {
|
||||||
bool loadFirmware(string filename, string keyname, uint8_t *targetdata, unsigned targetsize);
|
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 string markup() = 0;
|
||||||
virtual bool cartridgeLoaded() = 0;
|
virtual bool cartridgeLoaded() = 0;
|
||||||
@@ -69,11 +68,12 @@ struct Interface : property<Interface> {
|
|||||||
bool saveState(unsigned slot);
|
bool saveState(unsigned slot);
|
||||||
bool loadState(unsigned slot);
|
bool loadState(unsigned slot);
|
||||||
void setCheatCodes(const lstring &list = lstring{});
|
void setCheatCodes(const lstring &list = lstring{});
|
||||||
|
void updatePalette();
|
||||||
string sha256();
|
string sha256();
|
||||||
|
|
||||||
Interface();
|
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);
|
void videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height);
|
||||||
|
|
||||||
CartridgePath base; //base cartridge connected to emulated system
|
CartridgePath base; //base cartridge connected to emulated system
|
||||||
|
@@ -29,28 +29,26 @@ bool InterfaceNES::cartridgeLoaded() {
|
|||||||
bool InterfaceNES::loadCartridge(const string &filename) {
|
bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data;
|
vector<uint8_t> memory;
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
if(filename.endswith("/")) {
|
if(filename.endswith("/")) {
|
||||||
if(file::read({filename, "program.rom"}, data, size) == false) return false;
|
memory = file::read({filename, "program.rom"});
|
||||||
interface->base = {true, filename};
|
interface->base = {true, filename};
|
||||||
} else {
|
} else {
|
||||||
file::read(filename, data, size);
|
memory = file::read(filename);
|
||||||
interface->base = {false, nall::basename(filename)};
|
interface->base = {false, nall::basename(filename)};
|
||||||
}
|
}
|
||||||
|
if(memory.empty()) return false;
|
||||||
|
|
||||||
interface->game = interface->base;
|
interface->game = interface->base;
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
interface->applyPatch(interface->base, data, size);
|
interface->applyPatch(interface->base, memory);
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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();
|
NES::system.power();
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(NES::cartridge.ram_size()) {
|
if(NES::cartridge.ram_size()) {
|
||||||
filemap fp;
|
filemap fp;
|
||||||
@@ -60,7 +58,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface->loadCartridge(::Interface::Mode::NES);
|
interface->loadCartridge(::Interface::Mode::NES);
|
||||||
NES::video.generate(NES::Video::Format::RGB30);
|
NES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,34 +114,12 @@ void InterfaceNES::setCheats(const lstring &list) {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void InterfaceNES::videoRefresh(const uint16_t *data) {
|
uint32_t InterfaceNES::videoColor(uint9_t source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
static uint32_t output[256 * 240];
|
return color(red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
for(unsigned y = 0; y < 240; y++) {
|
void InterfaceNES::videoRefresh(const uint32_t *data) {
|
||||||
const uint16_t *sp = data + y * 256;
|
interface->videoRefresh(data, 256 * sizeof(uint32_t), 256, 240);
|
||||||
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::audioSample(int16_t sample) {
|
void InterfaceNES::audioSample(int16_t sample) {
|
||||||
|
@@ -18,7 +18,8 @@ struct InterfaceNES : InterfaceCore, NES::Interface {
|
|||||||
|
|
||||||
void setCheats(const lstring &list = lstring{});
|
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);
|
void audioSample(int16_t sample);
|
||||||
int16_t inputPoll(bool port, unsigned device, unsigned id);
|
int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||||
};
|
};
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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;
|
|
@@ -37,178 +37,156 @@ bool InterfaceSNES::cartridgeLoaded() {
|
|||||||
return SNES::cartridge.loaded();
|
return SNES::cartridge.loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadCartridge(const string &filename, CartridgePath &cartridge, uint8_t *&data, unsigned &size) {
|
vector<uint8_t> InterfaceSNES::loadCartridge(const string &filename, CartridgePath &cartridge) {
|
||||||
data = nullptr, size = 0u;
|
vector<uint8_t> memory;
|
||||||
auto backup = cartridge;
|
auto backup = cartridge;
|
||||||
string suffix;
|
string suffix;
|
||||||
if(filename.endswith("/")) {
|
if(filename.endswith("/")) {
|
||||||
cartridge = { true, filename };
|
cartridge = {true, filename};
|
||||||
} else {
|
} else {
|
||||||
suffix = { ".", extension(filename) };
|
suffix = {".", extension(filename)};
|
||||||
cartridge = { false, nall::basename(filename) };
|
cartridge = {false, nall::basename(filename)};
|
||||||
}
|
}
|
||||||
if(file::read(cartridge.filename("program.rom", suffix), data, size) == false) {
|
memory = file::read(cartridge.filename("program.rom", suffix));
|
||||||
cartridge = backup;
|
interface->applyPatch(cartridge, memory);
|
||||||
return false;
|
if(memory.empty()) cartridge = backup;
|
||||||
}
|
return memory;
|
||||||
interface->applyPatch(cartridge, data, size);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadCartridge(string basename) {
|
bool InterfaceSNES::loadCartridge(string basename) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data;
|
auto memory = loadCartridge(basename, interface->base);
|
||||||
unsigned size;
|
if(memory.empty()) return false;
|
||||||
if(loadCartridge(basename, interface->base, data, size) == false) return false;
|
|
||||||
|
|
||||||
interface->game = interface->base;
|
interface->game = interface->base;
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
interface->loadCartridge(::Interface::Mode::SNES);
|
interface->loadCartridge(::Interface::Mode::SNES);
|
||||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
SNES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slotname) {
|
bool InterfaceSNES::loadSatellaviewSlottedCartridge(string basename, string slotname) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data[2];
|
auto memory = loadCartridge(basename, interface->base);
|
||||||
unsigned size[2];
|
if(memory.empty()) return false;
|
||||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
auto memoryBS = loadCartridge(slotname, interface->slot[0]);
|
||||||
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
|
|
||||||
|
|
||||||
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();
|
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;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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]);
|
SNES::cartridge.rom.copy(memory.data(), memory.size());
|
||||||
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
if(memoryBS) SNES::bsxflash.memory.copy(memoryBS.data(), memoryBS.size());
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, markup);
|
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
|
||||||
if(data[1]) delete[] data[1];
|
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
interface->loadCartridge(::Interface::Mode::SNES);
|
interface->loadCartridge(::Interface::Mode::SNES);
|
||||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
SNES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
|
bool InterfaceSNES::loadSatellaviewCartridge(string basename, string slotname) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data[2];
|
auto memory = loadCartridge(basename, interface->base);
|
||||||
unsigned size[2];
|
if(memory.empty()) return false;
|
||||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
auto memoryBS = loadCartridge(slotname, interface->slot[0]);
|
||||||
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
|
|
||||||
|
|
||||||
interface->game = !data[1] ? interface->base : interface->slot[0];
|
interface->game = memoryBS.empty() ? interface->base : interface->slot[0];
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
if(data[1]) interface->cartridgeTitle = interface->slot[0].title();
|
if(memoryBS) interface->cartridgeTitle = interface->slot[0].title();
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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]);
|
SNES::cartridge.rom.copy(memory.data(), memory.size());
|
||||||
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
if(memoryBS) SNES::bsxflash.memory.copy(memoryBS.data(), memoryBS.size());
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, markup);
|
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
|
||||||
if(data[1]) delete[] data[1];
|
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
interface->loadCartridge(::Interface::Mode::SNES);
|
interface->loadCartridge(::Interface::Mode::SNES);
|
||||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
SNES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname, string slotBname) {
|
bool InterfaceSNES::loadSufamiTurboCartridge(string basename, string slotAname, string slotBname) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data[3];
|
auto memory = loadCartridge(basename, interface->base);
|
||||||
unsigned size[3];
|
if(memory.empty()) return false;
|
||||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
auto memorySTA = loadCartridge(slotAname, interface->slot[0]);
|
||||||
loadCartridge(slotAname, interface->slot[0], data[1], size[1]);
|
auto memorySTB = loadCartridge(slotBname, interface->slot[1]);
|
||||||
loadCartridge(slotBname, interface->slot[1], data[2], size[2]);
|
|
||||||
|
|
||||||
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();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
if( data[1] && !data[2]) interface->cartridgeTitle = interface->slot[0].title();
|
if( memorySTA && !memorySTB) interface->cartridgeTitle = interface->slot[0].title();
|
||||||
if(!data[1] && data[2]) interface->cartridgeTitle = interface->slot[1].title();
|
if(!memorySTA && memorySTB) interface->cartridgeTitle = interface->slot[1].title();
|
||||||
if( data[1] && data[2]) interface->cartridgeTitle = {
|
if( memorySTA && memorySTB) interface->cartridgeTitle = {
|
||||||
interface->slot[0].title(), " + ", interface->slot[1].title()
|
interface->slot[0].title(), " + ", interface->slot[1].title()
|
||||||
};
|
};
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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]);
|
SNES::cartridge.rom.copy(memory.data(), memory.size());
|
||||||
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
|
if(memorySTA) SNES::sufamiturbo.slotA.rom.copy(memory.data(), memory.size());
|
||||||
if(data[2]) SNES::sufamiturbo.slotB.rom.copy(data[1], size[1]);
|
if(memorySTB) SNES::sufamiturbo.slotB.rom.copy(memory.data(), memory.size());
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, markup);
|
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
|
||||||
if(data[1]) delete[] data[1];
|
|
||||||
if(data[2]) delete[] data[2];
|
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
interface->loadCartridge(::Interface::Mode::SNES);
|
interface->loadCartridge(::Interface::Mode::SNES);
|
||||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
SNES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname) {
|
bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname) {
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
|
|
||||||
uint8_t *data[2];
|
auto memory = loadCartridge(basename, interface->base);
|
||||||
unsigned size[2];
|
if(memory.empty()) return false;
|
||||||
if(loadCartridge(basename, interface->base, data[0], size[0]) == false) return false;
|
auto memoryGB = loadCartridge(slotname, interface->slot[0]);
|
||||||
loadCartridge(slotname, interface->slot[0], data[1], size[1]);
|
|
||||||
|
|
||||||
interface->game = !data[1] ? interface->base : interface->slot[0];
|
interface->game = memoryGB.empty() ? interface->base : interface->slot[0];
|
||||||
interface->cartridgeTitle = interface->base.title();
|
interface->cartridgeTitle = interface->base.title();
|
||||||
if(data[1]) interface->cartridgeTitle = interface->slot[0].title();
|
if(memoryGB) interface->cartridgeTitle = interface->slot[0].title();
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
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;
|
string gbMarkup;
|
||||||
gbMarkup.readfile(interface->slot[0].filename("manifest.xml", ".xml"));
|
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]);
|
SNES::cartridge.rom.copy(memory.data(), memory.size());
|
||||||
GB::cartridge.load(GB::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
GB::cartridge.load(GB::System::Revision::SuperGameBoy, gbMarkup, vectorstream{memoryGB});
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
|
||||||
if(data[1]) delete[] data[1];
|
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
interface->loadCartridge(::Interface::Mode::SNES);
|
interface->loadCartridge(::Interface::Mode::SNES);
|
||||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
SNES::video.generate_palette();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,21 +231,15 @@ void InterfaceSNES::loadMemory() {
|
|||||||
string filename = memoryName(memory);
|
string filename = memoryName(memory);
|
||||||
if(filename.empty()) continue;
|
if(filename.empty()) continue;
|
||||||
|
|
||||||
uint8_t *data;
|
if(auto read = file::read(filename)) {
|
||||||
unsigned size;
|
memcpy(memory.data, read.data(), min(memory.size, read.size()));
|
||||||
if(file::read(filename, data, size)) {
|
|
||||||
memcpy(memory.data, data, min(memory.size, size));
|
|
||||||
delete[] data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
if(GB::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
uint8_t *data;
|
if(auto read = file::read(interface->slot[0].filename("save.ram", ".sav"))) {
|
||||||
unsigned size;
|
memcpy(GB::cartridge.ramdata, read.data(), min(GB::cartridge.ramsize, read.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,41 +306,20 @@ void InterfaceSNES::setCheats(const lstring &list) {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
uint32_t InterfaceSNES::videoColor(uint19_t source, uint16_t red, uint16_t green, uint16_t blue) {
|
||||||
static uint32_t output[512 * 480];
|
return color(red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned width = 256 << hires;
|
void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
||||||
unsigned height = 240 << interlace;
|
unsigned width = 256 << hires;
|
||||||
unsigned pitch = 1024 >> interlace;
|
unsigned height = 240 << interlace;
|
||||||
|
unsigned pitch = 1024 >> interlace;
|
||||||
|
|
||||||
//skip first line; as it is always blank (by SNES design)
|
//skip first line; as it is always blank (by SNES design)
|
||||||
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
|
if(overscan == false) data += 1 * 1024; // 8 + 224 + 8
|
||||||
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
|
if(overscan == true ) data += 9 * 1024; // 0 + 240 + 0
|
||||||
|
|
||||||
for(unsigned y = 0; y < height; y++) {
|
interface->videoRefresh(data, pitch * sizeof(uint32_t), width, height);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
|
@@ -6,7 +6,7 @@ struct InterfaceSNES : InterfaceCore, SNES::Interface {
|
|||||||
void setController(bool port, unsigned device);
|
void setController(bool port, unsigned device);
|
||||||
|
|
||||||
bool cartridgeLoaded();
|
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 loadCartridge(string basename);
|
||||||
bool loadSatellaviewSlottedCartridge(string basename, string slotname);
|
bool loadSatellaviewSlottedCartridge(string basename, string slotname);
|
||||||
bool loadSatellaviewCartridge(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{});
|
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 videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
||||||
void audioSample(int16_t lsample, int16_t rsample);
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
||||||
|
@@ -88,7 +88,6 @@ Application::Application(int argc, char **argv) {
|
|||||||
video.driver("None");
|
video.driver("None");
|
||||||
video.init();
|
video.init();
|
||||||
}
|
}
|
||||||
palette.update();
|
|
||||||
utility->bindVideoFilter();
|
utility->bindVideoFilter();
|
||||||
utility->bindVideoShader();
|
utility->bindVideoShader();
|
||||||
|
|
||||||
|
@@ -103,5 +103,5 @@ void VideoSettings::synchronize() {
|
|||||||
overscanHorizontal.value.setText({ config->video.maskOverscanHorizontal, "px" });
|
overscanHorizontal.value.setText({ config->video.maskOverscanHorizontal, "px" });
|
||||||
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
|
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
|
||||||
|
|
||||||
palette.update();
|
interface->updatePalette();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user