From cec33c1d0f49d97c2d4b361fa43d59c2fd463aa8 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 15 Jan 2016 21:06:51 +1100 Subject: [PATCH] Update to v096r07 release. byuu says: Changelog: - configuration files are now stored in localpath() instead of configpath() - Video gamma/saturation/luminance sliders are gone now, sorry - added Video Filter->Blur Emulation [1] - added Video Filter->Scanline Emulation [2] - improvements to GBA audio emulation (fixes Minish Cap) [Jonas Quinn] [1] For the Famicom, this does nothing. For the Super Famicom, this performs horizontal blending for proper pseudo-hires translucency. For the Game Boy, Game Boy Color, and Game Boy Advance, this performs interframe blending (each frame is the average of the current and previous frame), which is important for things like the GBVideoPlayer. [2] Right now, this only applies to the Super Famicom, but it'll come to the Famicom in the future. For the Super Famicom, this option doesn't just add scanlines, it simulates the phosphor decay that's visible in interlace mode. If you observe an interlaced game like RPM Racing on a real SNES, you'll notice that even on perfectly still screens, the image appears to shake. This option emulates that effect. Note 1: the buffering right now is a little sub-optimal, so there will be a slight speed hit with this new support. Since the core is now generating native ARGB8888 colors, it might as well call out to the interface to lock/unlock/refresh the video, that way it can render directly to the screen. Although ... that might not be such a hot idea, since the GBx interframe blending reads from the target buffer, and that tends to be a catastrophic option for performance. Note 2: the balanced and performance profiles for the SNES are completely busted again. This WIP took 6 1/2 hours, and I'm exhausted. Very much not looking forward to working on those, since those two have all kinds of fucked up speedup tricks for non-interlaced and/or non-hires video modes. Note 3: if you're on Windows and you saved your system folders somewhere else, now'd be a good time to move them to %localappdata%/higan --- higan/GNUmakefile | 7 +- higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 13 +- higan/fc/interface/interface.cpp | 16 +- higan/fc/interface/interface.hpp | 9 +- higan/fc/system/system.cpp | 5 +- higan/fc/video/video.cpp | 53 +-- higan/fc/video/video.hpp | 9 +- higan/gb/interface/interface.cpp | 23 +- higan/gb/interface/interface.hpp | 10 +- higan/gb/ppu/ppu.cpp | 2 +- higan/gb/system/system.cpp | 5 +- higan/gb/video/video.cpp | 143 ++++---- higan/gb/video/video.hpp | 11 +- higan/gba/apu/registers.hpp | 1 - higan/gba/apu/sequencer.cpp | 21 +- higan/gba/apu/serialization.cpp | 1 - higan/gba/interface/interface.cpp | 19 +- higan/gba/interface/interface.hpp | 10 +- higan/gba/system/system.cpp | 5 +- higan/gba/video/video.cpp | 87 ++--- higan/gba/video/video.hpp | 10 +- higan/sfc/coprocessor/icd2/icd2.cpp | 1 - higan/sfc/interface/interface.cpp | 22 +- higan/sfc/interface/interface.hpp | 11 +- higan/sfc/ppu/ppu.cpp | 7 +- higan/sfc/ppu/ppu.hpp | 1 - higan/sfc/ppu/screen/screen.cpp | 10 +- higan/sfc/ppu/screen/screen.hpp | 2 +- higan/sfc/system/system.cpp | 5 +- higan/sfc/system/video.cpp | 311 +++++++++++------- higan/sfc/system/video.hpp | 12 +- higan/target-tomoko/GNUmakefile | 28 +- .../configuration/configuration.cpp | 6 +- .../presentation/presentation.cpp | 18 +- .../presentation/presentation.hpp | 2 + higan/target-tomoko/program/interface.cpp | 36 +- higan/target-tomoko/program/media.cpp | 3 +- higan/target-tomoko/program/program.cpp | 29 +- higan/target-tomoko/program/program.hpp | 5 +- higan/target-tomoko/program/utility.cpp | 8 - higan/target-tomoko/settings/hotkeys.cpp | 2 +- higan/target-tomoko/settings/input.cpp | 2 +- higan/target-tomoko/settings/settings.hpp | 13 - higan/target-tomoko/settings/video.cpp | 15 - higan/target-tomoko/tomoko.cpp | 2 +- higan/target-tomoko/tools/cheat-database.cpp | 2 +- nall/file.hpp | 2 + 48 files changed, 574 insertions(+), 443 deletions(-) diff --git a/higan/GNUmakefile b/higan/GNUmakefile index 1e8fce2e..9e194d41 100644 --- a/higan/GNUmakefile +++ b/higan/GNUmakefile @@ -35,12 +35,7 @@ ifeq ($(platform),windows) link += -Wl,-enable-runtime-pseudo-reloc else ifeq ($(platform),macosx) flags += -march=native -else ifeq ($(platform),linux) - flags += -march=native -fopenmp - link += -fopenmp - link += -Wl,-export-dynamic - link += -lX11 -lXext -else ifeq ($(platform),bsd) +else ifneq ($(filter $(platform),linux bsd),) flags += -march=native -fopenmp link += -fopenmp link += -Wl,-export-dynamic diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index e1a86f6f..38fc8db9 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "096.06"; + static const string Version = "096.07"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 0999cbf4..decd72c3 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -49,8 +49,7 @@ struct Interface { virtual auto loadRequest(uint, string, string, bool) -> void {} virtual auto loadRequest(uint, string, bool) -> void {} virtual auto saveRequest(uint, string) -> void {} - virtual auto videoColor(uint, uint16, uint16, uint16, uint16) -> uint32 { return 0u; } - virtual auto videoRefresh(const uint32*, const uint32*, uint, uint, uint) -> void {} + virtual auto videoRefresh(const uint32*, uint, uint, uint) -> void {} virtual auto audioSample(int16, int16) -> void {} virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; } virtual auto inputRumble(uint, uint, uint, bool) -> void {} @@ -64,8 +63,7 @@ struct Interface { auto loadRequest(uint id, string name, string type, bool required) -> void { return bind->loadRequest(id, name, type, required); } auto loadRequest(uint id, string path, bool required) -> void { return bind->loadRequest(id, path, required); } auto saveRequest(uint id, string path) -> void { return bind->saveRequest(id, path); } - auto videoColor(uint source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 { return bind->videoColor(source, alpha, red, green, blue); } - auto videoRefresh(const uint32* palette, const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(palette, data, pitch, width, height); } + auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(data, pitch, width, height); } auto audioSample(int16 lsample, int16 rsample) -> void { return bind->audioSample(lsample, rsample); } auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); } auto inputRumble(uint port, uint device, uint input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); } @@ -106,9 +104,10 @@ struct Interface { //cheat functions virtual auto cheatSet(const lstring& = lstring{}) -> void {} - //utility functions - enum class PaletteMode : uint { Literal, Channel, Standard, Emulation }; - virtual auto paletteUpdate(PaletteMode mode) -> void {} + //settings + virtual auto cap(const string& name) -> bool { return false; } + virtual auto get(const string& name) -> any { return {}; } + virtual auto set(const string& name, const any& value) -> bool { return false; } }; } diff --git a/higan/fc/interface/interface.cpp b/higan/fc/interface/interface.cpp index d43143f8..ef12013d 100644 --- a/higan/fc/interface/interface.cpp +++ b/higan/fc/interface/interface.cpp @@ -3,6 +3,7 @@ namespace Famicom { Interface* interface = nullptr; +Settings settings; Interface::Interface() { interface = this; @@ -166,8 +167,19 @@ auto Interface::cheatSet(const lstring& list) -> void { } } -auto Interface::paletteUpdate(PaletteMode mode) -> void { - video.generate_palette(mode); +auto Interface::cap(const string& name) -> bool { + if(name == "Color Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Color Emulation") return settings.colorEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Color Emulation" && value.is()) return settings.colorEmulation = value.get(), true; + return false; } } diff --git a/higan/fc/interface/interface.hpp b/higan/fc/interface/interface.hpp index f79fe94f..b161cc0d 100644 --- a/higan/fc/interface/interface.hpp +++ b/higan/fc/interface/interface.hpp @@ -48,12 +48,19 @@ struct Interface : Emulator::Interface { auto cheatSet(const lstring&) -> void; - auto paletteUpdate(PaletteMode mode) -> void; + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; private: vector device; }; +struct Settings { + bool colorEmulation = true; +}; + extern Interface* interface; +extern Settings settings; } diff --git a/higan/fc/system/system.cpp b/higan/fc/system/system.cpp index 667447d5..a54d7b51 100644 --- a/higan/fc/system/system.cpp +++ b/higan/fc/system/system.cpp @@ -8,7 +8,7 @@ System system; auto System::run() -> void { scheduler.enter(); if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { - interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240); + video.refresh(); } } @@ -36,7 +36,7 @@ auto System::runthreadtosave() -> void { scheduler.enter(); if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break; if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { - interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240); + video.refresh(); } } } @@ -65,6 +65,7 @@ auto System::reset() -> void { ppu.reset(); input.reset(); scheduler.reset(); + video.reset(); } auto System::init() -> void { diff --git a/higan/fc/video/video.cpp b/higan/fc/video/video.cpp index 36ce5846..9f7ce703 100644 --- a/higan/fc/video/video.cpp +++ b/higan/fc/video/video.cpp @@ -7,34 +7,41 @@ namespace Famicom { Video video; Video::Video() { - palette = new uint32_t[1 << 9](); + output = new uint32[256 * 240]; + paletteStandard = new uint32[1 << 9]; + paletteEmulation = new uint32[1 << 9]; } Video::~Video() { - delete[] palette; + delete[] output; + delete[] paletteStandard; + delete[] paletteEmulation; } -auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void { +auto Video::reset() -> void { + memory::fill(output, 256 * 240); + for(auto color : range(1 << 9)) { - if(mode == Emulator::Interface::PaletteMode::Literal) { - palette[color] = color; - } else if(mode == Emulator::Interface::PaletteMode::Channel) { - uint emphasis = (color >> 6) & 7; - uint luma = (color >> 4) & 3; - uint chroma = (color >> 0) & 15; - emphasis = image::normalize(emphasis, 3, 16); - luma = image::normalize(luma, 2, 16); - chroma = image::normalize(chroma, 4, 16); - palette[color] = interface->videoColor(color, 0, emphasis, luma, chroma); - } else if(mode == Emulator::Interface::PaletteMode::Standard) { - palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 2.2); - } else if(mode == Emulator::Interface::PaletteMode::Emulation) { - palette[color] = generate_color(color, 2.0, 0.0, 1.0, 1.0, 1.8); - } + paletteStandard[color] = generateColor(color, 2.0, 0.0, 1.0, 1.0, 2.2); + paletteEmulation[color] = generateColor(color, 2.0, 0.0, 1.0, 1.0, 1.8); } } -auto Video::generate_color( +auto Video::refresh() -> void { + auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard; + + for(uint y = 0; y < 240; y++) { + auto source = ppu.buffer + y * 256; + auto target = output + y * 256; + for(uint x = 0; x < 256; x++) { + *target++ = palette[*source++]; + } + } + + interface->videoRefresh(output, 4 * 256, 256, 240); +} + +auto Video::generateColor( uint n, double saturation, double hue, double contrast, double brightness, double gamma ) -> uint32 { @@ -75,11 +82,11 @@ auto Video::generate_color( q *= saturation; auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); }; - uint r = 65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q); - uint g = 65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q); - uint b = 65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q); + uint r = uclamp<16>(65535.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q)); + uint g = uclamp<16>(65535.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q)); + uint b = uclamp<16>(65535.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q)); - return interface->videoColor(n, 0, uclamp<16>(r), uclamp<16>(g), uclamp<16>(b)); + return (255 << 24) | ((r >> 8) << 16) | ((g >> 8) << 8) | ((b >> 8) << 0); } } diff --git a/higan/fc/video/video.hpp b/higan/fc/video/video.hpp index 7a2ebc8d..e0a73a23 100644 --- a/higan/fc/video/video.hpp +++ b/higan/fc/video/video.hpp @@ -2,12 +2,15 @@ struct Video { Video(); ~Video(); - auto generate_palette(Emulator::Interface::PaletteMode mode) -> void; + auto reset() -> void; + auto refresh() -> void; - uint32* palette = nullptr; + uint32* output = nullptr; + uint32* paletteStandard = nullptr; + uint32* paletteEmulation = nullptr; private: - auto generate_color(uint, double, double, double, double, double) -> uint32; + auto generateColor(uint, double, double, double, double, double) -> uint32; }; extern Video video; diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index cd46c486..aa740750 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -3,6 +3,7 @@ namespace GameBoy { Interface* interface = nullptr; +Settings settings; Interface::Interface() { interface = this; @@ -167,10 +168,6 @@ auto Interface::cheatSet(const lstring& list) -> void { } } -auto Interface::paletteUpdate(PaletteMode mode) -> void { - video.generate_palette(mode); -} - auto Interface::lcdScanline() -> void { if(hook) hook->lcdScanline(); } @@ -183,4 +180,22 @@ auto Interface::joypWrite(bool p15, bool p14) -> void { if(hook) hook->joypWrite(p15, p14); } +auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) return settings.blurEmulation = value.get(), true; + if(name == "Color Emulation" && value.is()) return settings.colorEmulation = value.get(), true; + return false; +} + } diff --git a/higan/gb/interface/interface.hpp b/higan/gb/interface/interface.hpp index 6436b656..55723826 100644 --- a/higan/gb/interface/interface.hpp +++ b/higan/gb/interface/interface.hpp @@ -50,7 +50,9 @@ struct Interface : Emulator::Interface { auto cheatSet(const lstring&) -> void; - auto paletteUpdate(PaletteMode mode) -> void; + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; //Super Game Boy bindings struct Hook { @@ -68,6 +70,12 @@ private: vector device; }; +struct Settings { + bool blurEmulation = true; + bool colorEmulation = true; +}; + extern Interface* interface; +extern Settings settings; } diff --git a/higan/gb/ppu/ppu.cpp b/higan/gb/ppu/ppu.cpp index 80a67406..a097c754 100644 --- a/higan/gb/ppu/ppu.cpp +++ b/higan/gb/ppu/ppu.cpp @@ -160,7 +160,7 @@ auto PPU::power() -> void { status.obpi_increment = 0; status.obpi = 0; - for(auto& n : screen) n = 0x0000; + for(auto& n : screen) n = 0; bg.color = 0; bg.palette = 0; diff --git a/higan/gb/system/system.cpp b/higan/gb/system/system.cpp index 858b9dd0..1bb6d80b 100644 --- a/higan/gb/system/system.cpp +++ b/higan/gb/system/system.cpp @@ -16,7 +16,7 @@ auto System::run() -> void { scheduler.enter(); if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144); + video.refresh(); } } @@ -40,7 +40,7 @@ auto System::runthreadtosave() -> void { scheduler.enter(); if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break; if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144); + video.refresh(); } } } @@ -71,6 +71,7 @@ auto System::power() -> void { cpu.power(); ppu.power(); apu.power(); + video.power(); scheduler.init(); clocks_executed = 0; diff --git a/higan/gb/video/video.cpp b/higan/gb/video/video.cpp index 30564e6e..a19fa351 100644 --- a/higan/gb/video/video.cpp +++ b/higan/gb/video/video.cpp @@ -5,89 +5,84 @@ namespace GameBoy { Video video; Video::Video() { - palette = new uint32_t[1 << 15](); + output = new uint32[160 * 144]; + paletteStandard = new uint32[1 << 15]; + paletteEmulation = new uint32[1 << 15]; } Video::~Video() { - delete[] palette; + delete[] output; + delete[] paletteStandard; + delete[] paletteEmulation; } -auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void { - this->mode = mode; - if(system.dmg()) for(auto n : range(4)) palette[n] = paletteDMG(n); - if(system.sgb()) for(auto n : range(4)) palette[n] = paletteSGB(n); - if(system.cgb()) for(auto n : range(1 << 15)) palette[n] = paletteCGB(n); +auto Video::power() -> void { + memory::fill(output, 160 * 144 * sizeof(uint32)); + + if(system.dmg()) { + for(auto color : range(1 << 2)) { + uint L = image::normalize(3 - color, 2, 8); + uint R = monochrome[color][0] >> 8; + uint G = monochrome[color][1] >> 8; + uint B = monochrome[color][2] >> 8; + paletteStandard[color] = (255 << 24) | (L << 16) | (L << 8) | (L << 0); + paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); + } + } + + if(system.sgb()) { + for(auto color : range(1 << 2)) { + paletteStandard[color] = color; + paletteEmulation[color] = color; + } + } + + if(system.cgb()) { + for(auto color : range(1 << 15)) { + uint r = (uint5)(color >> 0); + uint g = (uint5)(color >> 5); + uint b = (uint5)(color >> 10); + + { uint R = image::normalize(r, 5, 8); + uint G = image::normalize(g, 5, 8); + uint B = image::normalize(b, 5, 8); + paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); + } + + { uint R = (r * 26 + g * 4 + b * 2); + uint G = ( g * 24 + b * 8); + uint B = (r * 6 + g * 4 + b * 22); + R = min(960, R) >> 2; + G = min(960, G) >> 2; + B = min(960, B) >> 2; + paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); + } + } + } } -auto Video::paletteDMG(uint color) const -> uint { - if(mode == Emulator::Interface::PaletteMode::Literal) { - return color; +auto Video::refresh() -> void { + auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard; + + for(uint y = 0; y < 144; y++) { + auto source = ppu.screen + y * 160; + auto target = output + y * 160; + + if(settings.blurEmulation) { + for(uint x = 0; x < 160; x++) { + auto a = palette[*source++]; + auto b = *target; + *target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1; + } + } else { + for(uint x = 0; x < 160; x++) { + auto color = palette[*source++]; + *target++ = color; + } + } } - if(mode == Emulator::Interface::PaletteMode::Channel) { - uint L = image::normalize(color, 2, 16); - return interface->videoColor(color, 0, 0, 0, L); - } - - if(mode == Emulator::Interface::PaletteMode::Standard) { - uint L = image::normalize(3 - color, 2, 16); - return interface->videoColor(color, 0, L, L, L); - } - - if(mode == Emulator::Interface::PaletteMode::Emulation) { - uint R = monochrome[color][0]; - uint G = monochrome[color][1]; - uint B = monochrome[color][2]; - return interface->videoColor(color, 0, R, G, B); - } - - return 0; -} - -auto Video::paletteSGB(uint color) const -> uint { - return color; -} - -auto Video::paletteCGB(uint color) const -> uint { - if(mode == Emulator::Interface::PaletteMode::Literal) { - return color; - } - - uint r = (color >> 0) & 31; - uint g = (color >> 5) & 31; - uint b = (color >> 10) & 31; - - if(mode == Emulator::Interface::PaletteMode::Channel) { - r = image::normalize(r, 5, 16); - g = image::normalize(g, 5, 16); - b = image::normalize(b, 5, 16); - return interface->videoColor(color, 0, r, g, b); - } - - if(mode == Emulator::Interface::PaletteMode::Standard) { - r = image::normalize(r, 5, 16); - g = image::normalize(g, 5, 16); - b = image::normalize(b, 5, 16); - return interface->videoColor(color, 0, r, g, b); - } - - if(mode == Emulator::Interface::PaletteMode::Emulation) { - uint R = (r * 26 + g * 4 + b * 2); - uint G = ( g * 24 + b * 8); - uint B = (r * 6 + g * 4 + b * 22); - - R = min(960, R); - G = min(960, G); - B = min(960, B); - - R = R << 6 | R >> 4; - G = G << 6 | G >> 4; - B = B << 6 | B >> 4; - - return interface->videoColor(color, 0, R, G, B); - } - - return 0; + interface->videoRefresh(output, 4 * 160, 160, 144); } #define DMG_PALETTE_GREEN diff --git a/higan/gb/video/video.hpp b/higan/gb/video/video.hpp index 17ef9f97..a058f7d7 100644 --- a/higan/gb/video/video.hpp +++ b/higan/gb/video/video.hpp @@ -2,16 +2,15 @@ struct Video { Video(); ~Video(); - auto generate_palette(Emulator::Interface::PaletteMode mode) -> void; + auto power() -> void; + auto refresh() -> void; - uint32* palette = nullptr; + uint32* output = nullptr; + uint32* paletteStandard = nullptr; + uint32* paletteEmulation = nullptr; private: - Emulator::Interface::PaletteMode mode; static const uint16 monochrome[4][3]; - auto paletteDMG(uint color) const -> uint; - auto paletteSGB(uint color) const -> uint; - auto paletteCGB(uint color) const -> uint; }; extern Video video; diff --git a/higan/gba/apu/registers.hpp b/higan/gba/apu/registers.hpp index 99fcff37..fbf99219 100644 --- a/higan/gba/apu/registers.hpp +++ b/higan/gba/apu/registers.hpp @@ -125,7 +125,6 @@ struct Sequencer { uint3 rvolume; uint1 lenable[4]; uint1 renable[4]; - uint1 enable[4]; uint1 masterenable; uint12 base; diff --git a/higan/gba/apu/sequencer.cpp b/higan/gba/apu/sequencer.cpp index 6bc48f31..71eef05f 100644 --- a/higan/gba/apu/sequencer.cpp +++ b/higan/gba/apu/sequencer.cpp @@ -20,10 +20,10 @@ auto APU::runsequencer() -> void { } r.base++; - if(r.enable[0]) square1.run(); - if(r.enable[1]) square2.run(); - if(r.enable[2]) wave.run(); - if(r.enable[3]) noise.run(); + if(square1.enable) square1.run(); + if(square2.enable) square2.run(); + if(wave.enable) wave.run(); + if(noise.enable) noise.run(); } auto APU::Sequencer::read(uint addr) const -> uint8 { @@ -39,7 +39,13 @@ auto APU::Sequencer::read(uint addr) const -> uint8 { | (lenable[2] << 6) | (lenable[3] << 7) ); - case 2: return (masterenable << 7); + case 2: return ( + (apu.square1.enable << 0) + | (apu.square2.enable << 1) + | (apu.wave.enable << 2) + | (apu.noise.enable << 3) + | (masterenable << 7) + ); } } @@ -62,10 +68,6 @@ auto APU::Sequencer::write(uint addr, uint8 byte) -> void { break; case 2: //NR52 - enable[0] = byte >> 0; - enable[1] = byte >> 1; - enable[2] = byte >> 2; - enable[3] = byte >> 3; masterenable = byte >> 7; break; } @@ -76,7 +78,6 @@ auto APU::Sequencer::power() -> void { rvolume = 0; for(auto& n : lenable) n = 0; for(auto& n : renable) n = 0; - for(auto& n : enable) n = 0; masterenable = 0; base = 0; step = 0; diff --git a/higan/gba/apu/serialization.cpp b/higan/gba/apu/serialization.cpp index ba73ec12..0918cf05 100644 --- a/higan/gba/apu/serialization.cpp +++ b/higan/gba/apu/serialization.cpp @@ -86,7 +86,6 @@ auto APU::serialize(serializer& s) -> void { s.integer(sequencer.rvolume); for(auto& flag : sequencer.lenable) s.integer(flag); for(auto& flag : sequencer.renable) s.integer(flag); - for(auto& flag : sequencer.enable) s.integer(flag); s.integer(sequencer.masterenable); s.integer(sequencer.base); s.integer(sequencer.step); diff --git a/higan/gba/interface/interface.cpp b/higan/gba/interface/interface.cpp index 41475e4e..7ae297be 100644 --- a/higan/gba/interface/interface.cpp +++ b/higan/gba/interface/interface.cpp @@ -3,6 +3,7 @@ namespace GameBoyAdvance { Interface* interface = nullptr; +Settings settings; Interface::Interface() { interface = this; @@ -153,8 +154,22 @@ auto Interface::unserialize(serializer& s) -> bool { return system.unserialize(s); } -auto Interface::paletteUpdate(PaletteMode mode) -> void { - video.generatePalette(mode); +auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) return settings.blurEmulation = value.get(), true; + if(name == "Color Emulation" && value.is()) return settings.colorEmulation = value.get(), true; + return false; } } diff --git a/higan/gba/interface/interface.hpp b/higan/gba/interface/interface.hpp index 675f72d6..c677b3c4 100644 --- a/higan/gba/interface/interface.hpp +++ b/higan/gba/interface/interface.hpp @@ -45,12 +45,20 @@ struct Interface : Emulator::Interface { auto serialize() -> serializer; auto unserialize(serializer&) -> bool; - auto paletteUpdate(PaletteMode mode) -> void; + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; private: vector device; }; +struct Settings { + bool blurEmulation = true; + bool colorEmulation = true; +}; + extern Interface* interface; +extern Settings settings; } diff --git a/higan/gba/system/system.cpp b/higan/gba/system/system.cpp index 464d3064..d48a672d 100644 --- a/higan/gba/system/system.cpp +++ b/higan/gba/system/system.cpp @@ -20,6 +20,7 @@ auto System::power() -> void { ppu.power(); apu.power(); cartridge.power(); + video.power(); scheduler.power(); } @@ -39,7 +40,7 @@ auto System::run() -> void { scheduler.enter(); if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break; } - interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160); + video.refresh(); } auto System::runtosave() -> void { @@ -62,7 +63,7 @@ auto System::runthreadtosave() -> void { scheduler.enter(); if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break; if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { - interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160); + video.refresh(); } } } diff --git a/higan/gba/video/video.cpp b/higan/gba/video/video.cpp index 6f8e045c..bca222a2 100644 --- a/higan/gba/video/video.cpp +++ b/higan/gba/video/video.cpp @@ -5,62 +5,65 @@ namespace GameBoyAdvance { Video video; Video::Video() { - palette = new uint32[1 << 15](); + output = new uint32[240 * 160]; + paletteStandard = new uint32[1 << 15]; + paletteEmulation = new uint32[1 << 15]; } Video::~Video() { - delete[] palette; + delete[] output; + delete[] paletteStandard; + delete[] paletteEmulation; } -auto Video::generatePalette(Emulator::Interface::PaletteMode mode) -> void { +auto Video::power() -> void { + memory::fill(output, 240 * 160 * sizeof(uint32)); + for(auto color : range(1 << 15)) { - if(mode == Emulator::Interface::PaletteMode::Literal) { - palette[color] = color; - continue; + uint B = (uint5)(color >> 10); + uint G = (uint5)(color >> 5); + uint R = (uint5)(color >> 0); + + { uint b = image::normalize(B, 5, 8); + uint g = image::normalize(G, 5, 8); + uint r = image::normalize(R, 5, 8); + paletteStandard[color] = (255 << 24) | (r << 16) | (g << 8) | (b << 0); } - uint B = (color >> 10) & 31; - uint G = (color >> 5) & 31; - uint R = (color >> 0) & 31; - - if(mode == Emulator::Interface::PaletteMode::Channel) { - R = image::normalize(R, 5, 16); - G = image::normalize(G, 5, 16); - B = image::normalize(B, 5, 16); - palette[color] = interface->videoColor(color, 0, R, G, B); - continue; - } - - if(mode == Emulator::Interface::PaletteMode::Standard) { - R = image::normalize(R, 5, 16); - G = image::normalize(G, 5, 16); - B = image::normalize(B, 5, 16); - palette[color] = interface->videoColor(color, 0, R, G, B); - continue; - } - - if(mode == Emulator::Interface::PaletteMode::Emulation) { - double lcdGamma = 4.0, outGamma = 2.2; + { double lcdGamma = 4.0, outGamma = 2.2; double lb = pow(B / 31.0, lcdGamma); double lg = pow(G / 31.0, lcdGamma); double lr = pow(R / 31.0, lcdGamma); - B = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); - G = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); - R = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); - - palette[color] = interface->videoColor(color, 0, R, G, B); - continue; + uint b = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); + uint g = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); + uint r = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); + paletteEmulation[color] = (255 << 24) | ((r >> 8) << 16) | ((g >> 8) << 8) | ((b >> 8) << 0); } - - palette[color] = 0; } } -const uint8 Video::curve[32] = { - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, - 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38, - 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x80, - 0x88, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, -}; +auto Video::refresh() -> void { + auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard; + + for(uint y = 0; y < 160; y++) { + auto source = ppu.output + y * 240; + auto target = output + y * 240; + + if(settings.blurEmulation) { + for(uint x = 0; x < 240; x++) { + auto a = palette[*source++]; + auto b = *target; + *target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1; + } + } else { + for(uint x = 0; x < 240; x++) { + auto color = palette[*source++]; + *target++ = color; + } + } + } + + interface->videoRefresh(output, 240 * sizeof(uint32), 240, 160); +} } diff --git a/higan/gba/video/video.hpp b/higan/gba/video/video.hpp index 8f235443..fda1ea71 100644 --- a/higan/gba/video/video.hpp +++ b/higan/gba/video/video.hpp @@ -2,12 +2,12 @@ struct Video { Video(); ~Video(); - auto generatePalette(Emulator::Interface::PaletteMode mode) -> void; + auto power() -> void; + auto refresh() -> void; - uint32* palette = nullptr; - -private: - static const uint8 curve[32]; + uint32* output = nullptr; + uint32* paletteStandard = nullptr; + uint32* paletteEmulation = nullptr; }; extern Video video; diff --git a/higan/sfc/coprocessor/icd2/icd2.cpp b/higan/sfc/coprocessor/icd2/icd2.cpp index 77fd95e0..c816a7ed 100644 --- a/higan/sfc/coprocessor/icd2/icd2.cpp +++ b/higan/sfc/coprocessor/icd2/icd2.cpp @@ -71,7 +71,6 @@ auto ICD2::reset() -> void { joyp14lock = 0; pulselock = true; - GameBoy::video.generate_palette(Emulator::Interface::PaletteMode::Literal); GameBoy::system.init(); GameBoy::system.power(); } diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index c243acd1..ad4d2256 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -3,6 +3,7 @@ namespace SuperFamicom { Interface* interface = nullptr; +Settings settings; Interface::Interface() { interface = this; @@ -469,8 +470,25 @@ auto Interface::cheatSet(const lstring& list) -> void { } } -auto Interface::paletteUpdate(PaletteMode mode) -> void { - video.generate_palette(mode); +auto Interface::cap(const string& name) -> bool { + if(name == "Blur Emulation") return true; + if(name == "Color Emulation") return true; + if(name == "Scanline Emulation") return true; + return false; +} + +auto Interface::get(const string& name) -> any { + if(name == "Blur Emulation") return settings.blurEmulation; + if(name == "Color Emulation") return settings.colorEmulation; + if(name == "Scanline Emulation") return settings.scanlineEmulation; + return {}; +} + +auto Interface::set(const string& name, const any& value) -> bool { + if(name == "Blur Emulation" && value.is()) return settings.blurEmulation = value.get(), true; + if(name == "Color Emulation" && value.is()) return settings.colorEmulation = value.get(), true; + if(name == "Scanline Emulation" && value.is()) return settings.scanlineEmulation = value.get(), true; + return false; } } diff --git a/higan/sfc/interface/interface.hpp b/higan/sfc/interface/interface.hpp index cd4ffbcb..c804101d 100644 --- a/higan/sfc/interface/interface.hpp +++ b/higan/sfc/interface/interface.hpp @@ -117,11 +117,20 @@ struct Interface : Emulator::Interface { auto cheatSet(const lstring&) -> void; - auto paletteUpdate(PaletteMode mode) -> void; + auto cap(const string& name) -> bool override; + auto get(const string& name) -> any override; + auto set(const string& name, const any& value) -> bool override; vector device; }; +struct Settings { + bool blurEmulation = true; + bool colorEmulation = true; + bool scanlineEmulation = true; +}; + extern Interface* interface; +extern Settings settings; } diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index e0f98dea..5371853b 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -19,12 +19,11 @@ bg4(*this, Background::ID::BG4), sprite(*this), window(*this), screen(*this) { - surface = new uint32[512 * 512]; - output = surface + 16 * 512; + output = new uint32[512 * 512]; } PPU::~PPU() { - delete[] surface; + delete[] output; } auto PPU::step(uint clocks) -> void { @@ -110,7 +109,7 @@ auto PPU::power() -> void { auto PPU::reset() -> void { create(Enter, system.cpuFrequency()); PPUcounter::reset(); - memory::fill(surface, 512 * 512 * sizeof(uint32)); + memory::fill(output, 512 * 480 * sizeof(uint32)); mmio_reset(); bg1.reset(); diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index 757548d9..4e688a0f 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -27,7 +27,6 @@ privileged: uint ppu1_version = 1; //allowed: 1 uint ppu2_version = 3; //allowed: 1, 2, 3 - uint32* surface = nullptr; uint32* output = nullptr; struct { diff --git a/higan/sfc/ppu/screen/screen.cpp b/higan/sfc/ppu/screen/screen.cpp index d6c66a2e..dd54a370 100644 --- a/higan/sfc/ppu/screen/screen.cpp +++ b/higan/sfc/ppu/screen/screen.cpp @@ -2,8 +2,8 @@ PPU::Screen::Screen(PPU& self) : self(self) { } auto PPU::Screen::scanline() -> void { - output = self.output + self.vcounter() * 1024; - if(self.display.interlace && self.field()) output += 512; + line = self.output + self.vcounter() * 1024; + if(self.display.interlace && self.field()) line += 512; //the first hires pixel of each scanline is transparent //note: exact value initializations are not confirmed on hardware @@ -19,14 +19,14 @@ auto PPU::Screen::scanline() -> void { } auto PPU::Screen::run() -> void { - if(ppu.vcounter() == 0) return; + if(self.vcounter() == 0) return; bool hires = self.regs.pseudo_hires || self.regs.bgmode == 5 || self.regs.bgmode == 6; auto sscolor = get_pixel_sub(hires); auto mscolor = get_pixel_main(); - *output++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor); - *output++ = (self.regs.display_brightness << 15) | (mscolor); + *line++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor); + *line++ = (self.regs.display_brightness << 15) | (mscolor); } auto PPU::Screen::get_pixel_sub(bool hires) -> uint16 { diff --git a/higan/sfc/ppu/screen/screen.hpp b/higan/sfc/ppu/screen/screen.hpp index 821289e5..fb86e56f 100644 --- a/higan/sfc/ppu/screen/screen.hpp +++ b/higan/sfc/ppu/screen/screen.hpp @@ -1,5 +1,5 @@ struct Screen { - uint32* output; + uint32* line; struct Regs { bool addsub_mode; diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 89f911ce..ba0d4fc0 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -23,7 +23,7 @@ auto System::run() -> void { scheduler.enter(); if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - video.update(); + video.refresh(); } } @@ -60,7 +60,7 @@ auto System::runThreadToSave() -> void { scheduler.enter(); if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break; if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { - video.update(); + video.refresh(); } } } @@ -243,6 +243,7 @@ auto System::reset() -> void { if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110); if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1); + video.reset(); scheduler.init(); device.connect(0, configuration.controllerPort1); device.connect(1, configuration.controllerPort2); diff --git a/higan/sfc/system/video.cpp b/higan/sfc/system/video.cpp index 92d4f823..dea13c66 100644 --- a/higan/sfc/system/video.cpp +++ b/higan/sfc/system/video.cpp @@ -1,56 +1,211 @@ Video video; Video::Video() { - palette = new uint32[1 << 19](); + output = new uint32[512 * 512](); + paletteStandard = new uint32[1 << 19]; + paletteEmulation = new uint32[1 << 19]; + + output += 16 * 512; //overscan padding } Video::~Video() { - delete[] palette; + output -= 16 * 512; + delete[] output; + delete[] paletteStandard; + delete[] paletteEmulation; } -auto Video::generate_palette(Emulator::Interface::PaletteMode mode) -> void { +auto Video::reset() -> void { + memory::fill(output, 512 * 480 * sizeof(uint32)); //padding area already cleared + for(auto color : range(1 << 19)) { - if(mode == Emulator::Interface::PaletteMode::Literal) { - palette[color] = color; - continue; + uint l = (uint4)(color >> 15); + uint b = (uint5)(color >> 10); + uint g = (uint5)(color >> 5); + uint r = (uint5)(color >> 0); + + double L = (1.0 + l) / 16.0 * (l ? 1.0 : 0.5); + + { uint R = L * image::normalize(r, 5, 8); + uint G = L * image::normalize(g, 5, 8); + uint B = L * image::normalize(b, 5, 8); + paletteStandard[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); } - uint l = (color >> 15) & 15; - uint b = (color >> 10) & 31; - uint g = (color >> 5) & 31; - uint r = (color >> 0) & 31; - - if(mode == Emulator::Interface::PaletteMode::Channel) { - l = image::normalize(l, 4, 16); - r = image::normalize(r, 5, 16); - g = image::normalize(g, 5, 16); - b = image::normalize(b, 5, 16); - palette[color] = interface->videoColor(color, l, r, g, b); - continue; + { uint R = L * gamma_ramp[r]; + uint G = L * gamma_ramp[g]; + uint B = L * gamma_ramp[b]; + paletteEmulation[color] = (255 << 24) | (R << 16) | (G << 8) | (B << 0); } + } - if(mode == Emulator::Interface::PaletteMode::Emulation) { - r = gamma_ramp[r]; - g = gamma_ramp[g]; - b = gamma_ramp[b]; - } else { - r = image::normalize(r, 5, 8); - g = image::normalize(g, 5, 8); - b = image::normalize(b, 5, 8); - } - - double L = (1.0 + l) / 16.0; - if(l == 0) L *= 0.5; - uint R = L * image::normalize(r, 8, 16); - uint G = L * image::normalize(g, 8, 16); - uint B = L * image::normalize(b, 8, 16); - - palette[color] = interface->videoColor(color, 0, R, G, B); + for(auto color : range(1 << 19)) { + uint l = (uint4)(color >> 15); + uint b = (uint5)(color >> 10); + uint g = (uint5)(color >> 5); + uint r = (uint5)(color >> 0); } } +auto Video::refresh() -> void { + auto palette = settings.colorEmulation ? paletteEmulation : paletteStandard; + + if(settings.scanlineEmulation) { + for(uint y = 0; y < 240; y++) { + auto sourceLo = ppu.output + y * 1024; + auto sourceHi = ppu.output + y * 1024 + 512; + auto targetLo = output + y * 1024; + auto targetHi = output + y * 1024 + 512; + if(!ppu.interlace()) { + for(uint x = 0; x < 512; x++) { + auto color = palette[*sourceLo++]; + *targetLo++ = color; + *targetHi++ = (255 << 24) | ((color & 0xfefefe) >> 1); + } + } else if(!ppu.field()) { + for(uint x = 0; x < 512; x++) { + auto color = palette[*sourceHi++]; + *targetLo++ = palette[*sourceLo++]; + *targetHi++ = (255 << 24) | ((color & 0xfefefe) >> 1); + } + } else { + for(uint x = 0; x < 512; x++) { + auto color = palette[*sourceLo++]; + *targetLo++ = (255 << 24) | ((color & 0xfefefe) >> 1); + *targetHi++ = palette[*sourceHi++]; + } + } + } + } else { + for(uint y = 0; y < 240; y++) { + auto sourceLo = ppu.output + y * 1024; + auto sourceHi = ppu.output + y * 1024 + 512; + auto targetLo = output + y * 1024; + auto targetHi = output + y * 1024 + 512; + if(!ppu.interlace()) { + for(uint x = 0; x < 512; x++) { + auto color = palette[*sourceLo++]; + *targetLo++ = color; + *targetHi++ = color; + } + } else { + for(uint x = 0; x < 512; x++) { + *targetLo++ = palette[*sourceLo++]; + *targetHi++ = palette[*sourceHi++]; + } + } + } + } + + if(settings.blurEmulation) { + for(uint y = 0; y < 480; y++) { + auto target = output + y * 512; + for(uint x = 0; x < 512; x++) { + auto a = target[x]; + auto b = target[x + (x != 511)]; + target[x] = (a + b - ((a ^ b) & 0x01010101)) >> 1; + } + } + } + + drawCursors(); + + #if defined(PROFILE_ACCURACY) + interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480); + #endif + + #if defined(PROFILE_BALANCED) || defined(PROFILE_PERFORMANCE) + if(hires) { + //normalize line widths + auto data = (uint32*)output; + if(ppu.interlace() && ppu.field()) data += 512; + + for(uint y = 0; y < 240; y++) { + if(line_width[y] == 512) continue; + uint32* buffer = data + y * 1024; + for(int x = 255; x >= 0; x--) { + buffer[(x * 2) + 0] = buffer[(x * 2) + 1] = buffer[x]; + } + } + } + + //overscan: when disabled, shift image down (by scrolling video buffer up) to center image onscreen + //(memory before ppu.output is filled with black scanlines) + interface->videoRefresh( + output - (ppu.overscan() ? 0 : 7 * 1024), + 4 * (1024 >> ppu.interlace()), + 256 << hires, + 240 << ppu.interlace() + ); + + hires = false; + #endif +} + //internal +auto Video::drawCursor(uint32 color, int x, int y) -> void { + auto data = (uint32*)output; + if(ppu.interlace() && ppu.field()) data += 512; + + for(int cy = 0; cy < 15; cy++) { + int vy = y + cy - 7; + if(vy <= 0 || vy >= 240) continue; //do not draw offscreen + + bool hires = (line_width[vy] == 512); + for(int cx = 0; cx < 15; cx++) { + int vx = x + cx - 7; + if(vx < 0 || vx >= 256) continue; //do not draw offscreen + uint8 pixel = cursor[cy * 15 + cx]; + if(pixel == 0) continue; + uint32 pixelcolor = pixel == 1 ? 0xff000000 : color; + + if(!hires) { + *(data + vy * 1024 + vx) = pixelcolor; + } else { + *(data + vy * 1024 + vx * 2 + 0) = pixelcolor; + *(data + vy * 1024 + vx * 2 + 1) = pixelcolor; + *(data + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor; + *(data + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor; + } + } + } +} + +auto Video::drawCursors() -> void { + switch(configuration.controllerPort2) { + case Device::ID::SuperScope: + if(dynamic_cast(device.controllerPort2)) { + auto& controller = (SuperScope&)*device.controllerPort2; + drawCursor(0xff0000ff, controller.x, controller.y); + } + break; + case Device::ID::Justifier: + case Device::ID::Justifiers: + if(dynamic_cast(device.controllerPort2)) { + auto& controller = (Justifier&)*device.controllerPort2; + drawCursor(0xffff0000, controller.player1.x, controller.player1.y); + if(!controller.chained) break; + drawCursor(0xff00bf00, controller.player2.x, controller.player2.y); + } + break; + } +} + +auto Video::scanline() -> void { + uint y = cpu.vcounter(); + if(y >= 240) return; + + hires |= ppu.hires(); + uint width = ppu.hires() ? 512 : 256; + line_width[y] = width; +} + +auto Video::init() -> void { + hires = false; + for(auto& n : line_width) n = 256; +} + const uint8 Video::gamma_ramp[32] = { 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, @@ -75,89 +230,3 @@ const uint8 Video::cursor[15 * 15] = { 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, }; - -auto Video::draw_cursor(uint16 color, int x, int y) -> void { - uint32* data = (uint32*)ppu.output; - if(ppu.interlace() && ppu.field()) data += 512; - - for(int cy = 0; cy < 15; cy++) { - int vy = y + cy - 7; - if(vy <= 0 || vy >= 240) continue; //do not draw offscreen - - bool hires = (line_width[vy] == 512); - for(int cx = 0; cx < 15; cx++) { - int vx = x + cx - 7; - if(vx < 0 || vx >= 256) continue; //do not draw offscreen - uint8 pixel = cursor[cy * 15 + cx]; - if(pixel == 0) continue; - uint32 pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color); - - if(hires == false) { - *((uint32*)data + vy * 1024 + vx) = pixelcolor; - } else { - *((uint32*)data + vy * 1024 + vx * 2 + 0) = pixelcolor; - *((uint32*)data + vy * 1024 + vx * 2 + 1) = pixelcolor; - } - } - } -} - -auto Video::update() -> void { - switch(configuration.controllerPort2) { - case Device::ID::SuperScope: - if(dynamic_cast(device.controllerPort2)) { - auto& controller = (SuperScope&)*device.controllerPort2; - draw_cursor(0x7c00, controller.x, controller.y); - } - break; - case Device::ID::Justifier: - case Device::ID::Justifiers: - if(dynamic_cast(device.controllerPort2)) { - auto& controller = (Justifier&)*device.controllerPort2; - draw_cursor(0x001f, controller.player1.x, controller.player1.y); - if(!controller.chained) break; - draw_cursor(0x02e0, controller.player2.x, controller.player2.y); - } - break; - } - - auto data = (uint32*)ppu.output; - if(ppu.interlace() && ppu.field()) data += 512; - - if(hires) { - //normalize line widths - for(unsigned y = 0; y < 240; y++) { - if(line_width[y] == 512) continue; - uint32* buffer = data + y * 1024; - for(signed x = 255; x >= 0; x--) { - buffer[(x * 2) + 0] = buffer[(x * 2) + 1] = buffer[x]; - } - } - } - - //overscan: when disabled, shift image down (by scrolling video buffer up) to center image onscreen - //(memory before ppu.output is filled with black scanlines) - interface->videoRefresh( - video.palette, - ppu.output - (ppu.overscan() ? 0 : 7 * 1024), - 4 * (1024 >> ppu.interlace()), - 256 << hires, - 240 << ppu.interlace() - ); - - hires = false; -} - -auto Video::scanline() -> void { - uint y = cpu.vcounter(); - if(y >= 240) return; - - hires |= ppu.hires(); - uint width = (ppu.hires() == false ? 256 : 512); - line_width[y] = width; -} - -auto Video::init() -> void { - hires = false; - for(auto& n : line_width) n = 256; -} diff --git a/higan/sfc/system/video.hpp b/higan/sfc/system/video.hpp index 76e8074b..4ed63209 100644 --- a/higan/sfc/system/video.hpp +++ b/higan/sfc/system/video.hpp @@ -1,9 +1,13 @@ struct Video { Video(); ~Video(); - auto generate_palette(Emulator::Interface::PaletteMode mode) -> void; - uint32_t* palette = nullptr; + auto reset() -> void; + auto refresh() -> void; + + uint32* output = nullptr; + uint32* paletteStandard = nullptr; + uint32* paletteEmulation = nullptr; private: bool hires; @@ -13,9 +17,11 @@ private: auto scanline() -> void; auto init() -> void; + auto drawCursor(uint32 color, int x, int y) -> void; + auto drawCursors() -> void; + static const uint8 gamma_ramp[32]; static const uint8 cursor[15 * 15]; - auto draw_cursor(uint16 color, int x, int y) -> void; friend class System; }; diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index 05798755..d6c9ea77 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -1,4 +1,4 @@ -name := tomoko +name := higan processors := arm gsu hg51b lr35902 r6502 r65816 spc700 upd96050 include processor/GNUmakefile @@ -68,12 +68,12 @@ obj/ui-resource.o: build: $(objects) $(strip $(compiler) -o out/$(name) $(objects) $(link)) ifeq ($(platform),macosx) - @if [ -d out/higan.app ]; then rm -r out/higan.app; fi - mkdir -p out/higan.app/Contents/MacOS/ - mkdir -p out/higan.app/Contents/Resources/ - mv out/$(name) out/higan.app/Contents/MacOS/higan - cp data/higan.plist out/higan.app/Contents/Info.plist - sips -s format icns data/higan.png --out out/higan.app/Contents/Resources/higan.icns + @if [ -d out/$(name).app ]; then rm -r out/$(name).app; fi + mkdir -p out/$(name).app/Contents/MacOS/ + mkdir -p out/$(name).app/Contents/Resources/ + mv out/$(name) out/$(name).app/Contents/MacOS/$(name) + cp data/$(name).plist out/$(name).app/Contents/Info.plist + sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns endif install: @@ -83,18 +83,18 @@ else ifeq ($(platform),windows) else ifeq ($(platform),macosx) mkdir -p ~/Library/Application\ Support/$(name)/ mkdir -p ~/Emulation/System/ - cp -R out/higan.app /Applications/higan.app + cp -R out/$(name).app /Applications/$(name).app cp data/cheats.bml ~/Library/Application\ Support/$(name)/ - cp -R profile/* ~/Emulation/System/ -else + cp -R profile/* ~/Library/Application\ Support/$(name)/ +else ifneq ($(filter $(platform),linux bsd),) mkdir -p $(prefix)/bin/ mkdir -p $(prefix)/share/icons/ mkdir -p $(prefix)/$(name)/ mkdir -p ~/Emulation/System/ cp out/$(name) $(prefix)/bin/$(name) - cp data/higan.png $(prefix)/share/icons/$(name).png + cp data/$(name).png $(prefix)/share/icons/$(name).png cp data/cheats.bml $(prefix)/$(name)/cheats.bml - cp -R profile/* ~/Emulation/System/ + cp -R profile/* $(prefix)/$(name)/ endif uninstall: @@ -102,8 +102,8 @@ ifeq ($(shell id -un),root) $(error "make uninstall should not be run as root") else ifeq ($(platform),windows) else ifeq ($(platform),macosx) - if [ -d /Applications/higan.app ]; then rm -r /Applications/higan.app; fi -else + if [ -d /Applications/$(name).app ]; then rm -r /Applications/$(name).app; fi +else ifneq ($(filter $(platform),linux bsd),) if [ -f $(prefix)/bin/$(name) ]; then rm $(prefix)/bin/$(name); fi if [ -f $(prefix)/share/icons/$(name).png ]; then rm $(prefix)/share/icons/$(name).png; fi endif diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index 451a8b5a..7da17ed6 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -2,7 +2,7 @@ Settings settings; Settings::Settings() { - Markup::Node::operator=(BML::unserialize(string::read(locate({configpath(), "tomoko/"}, "settings.bml")))); + Markup::Node::operator=(BML::unserialize(string::read(locate({localpath(), "higan/"}, "settings.bml")))); auto set = [&](const string& name, const string& value) { //create node and set to default value only if it does not already exist @@ -20,7 +20,9 @@ Settings::Settings() { set("Video/AspectCorrection", true); set("Video/Filter", "Blur"); set("Video/Shader", "None"); + set("Video/BlurEmulation", true); set("Video/ColorEmulation", true); + set("Video/ScanlineEmulation", true); set("Video/Saturation", 100); set("Video/Gamma", 100); set("Video/Luminance", 100); @@ -45,5 +47,5 @@ Settings::Settings() { } auto Settings::quit() -> void { - file::write(locate({configpath(), "tomoko/"}, "settings.bml"), BML::serialize(*this)); + file::write(locate({localpath(), "higan/"}, "settings.bml"), BML::serialize(*this)); } diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index 3f675f57..53906d1b 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -74,9 +74,17 @@ Presentation::Presentation() { settings["Video/Filter"].setValue("Blur"); program->updateVideoFilter(); }); + blurEmulation.setText("Blur Emulation").setChecked(settings["Video/BlurEmulation"].boolean()).onToggle([&] { + settings["Video/BlurEmulation"].setValue(blurEmulation.checked()); + if(emulator) emulator->set("Blur Emulation", blurEmulation.checked()); + }); colorEmulation.setText("Color Emulation").setChecked(settings["Video/ColorEmulation"].boolean()).onToggle([&] { settings["Video/ColorEmulation"].setValue(colorEmulation.checked()); - program->updateVideoPalette(); + if(emulator) emulator->set("Color Emulation", colorEmulation.checked()); + }); + scanlineEmulation.setText("Scanline Emulation").setChecked(settings["Video/ScanlineEmulation"].boolean()).onToggle([&] { + settings["Video/ScanlineEmulation"].setValue(scanlineEmulation.checked()); + if(emulator) emulator->set("Scanline Emulation", scanlineEmulation.checked()); }); maskOverscan.setText("Mask Overscan").setChecked(settings["Video/Overscan/Mask"].boolean()).onToggle([&] { settings["Video/Overscan/Mask"].setValue(maskOverscan.checked()); @@ -131,6 +139,8 @@ Presentation::Presentation() { statusBar.setFont(Font().setBold()); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); + viewport.setDroppable().onDrop([&](auto locations) { program->load(locations(0)); }); + onClose([&] { program->quit(); }); setTitle({"higan v", Emulator::Version}); @@ -187,6 +197,10 @@ auto Presentation::updateEmulator() -> void { } systemMenuSeparatorPorts.setVisible(inputPort1.visible() || inputPort2.visible() || inputPort3.visible()); + + emulator->set("Blur Emulation", blurEmulation.checked()); + emulator->set("Color Emulation", colorEmulation.checked()); + emulator->set("Scanline Emulation", scanlineEmulation.checked()); } auto Presentation::resizeViewport() -> void { @@ -261,7 +275,7 @@ auto Presentation::loadShaders() -> void { return; } - auto pathname = locate({localpath(), "tomoko/"}, "Video Shaders/"); + auto pathname = locate({localpath(), "higan/"}, "Video Shaders/"); for(auto shader : directory::folders(pathname, "*.shader")) { MenuRadioItem item{&videoShaderMenu}; item.setText(string{shader}.rtrim(".shader/", 1L)).onActivate([=] { diff --git a/higan/target-tomoko/presentation/presentation.hpp b/higan/target-tomoko/presentation/presentation.hpp index b8c9ba30..d40b69ad 100644 --- a/higan/target-tomoko/presentation/presentation.hpp +++ b/higan/target-tomoko/presentation/presentation.hpp @@ -32,7 +32,9 @@ struct Presentation : Window { MenuRadioItem videoFilterBlur{&videoFilterMenu}; Group videoFilters{&videoFilterNone, &videoFilterBlur}; MenuSeparator videoFilterSeparator{&videoFilterMenu}; + MenuCheckItem blurEmulation{&videoFilterMenu}; MenuCheckItem colorEmulation{&videoFilterMenu}; + MenuCheckItem scanlineEmulation{&videoFilterMenu}; MenuCheckItem maskOverscan{&videoFilterMenu}; Menu videoShaderMenu{&settingsMenu}; MenuRadioItem videoShaderNone{&videoShaderMenu}; diff --git a/higan/target-tomoko/program/interface.cpp b/higan/target-tomoko/program/interface.cpp index 6f8773ed..be612b95 100644 --- a/higan/target-tomoko/program/interface.cpp +++ b/higan/target-tomoko/program/interface.cpp @@ -45,39 +45,7 @@ auto Program::saveRequest(uint id, string filename) -> void { return emulator->save(id, stream); } -auto Program::videoColor(uint source, uint16 a, uint16 r, uint16 g, uint16 b) -> uint32 { - if(settings["Video/Saturation"].natural() != 100) { - uint16 grayscale = uclamp<16>((r + g + b) / 3); - double saturation = settings["Video/Saturation"].natural() * 0.01; - double inverse = max(0.0, 1.0 - saturation); - r = uclamp<16>(r * saturation + grayscale * inverse); - g = uclamp<16>(g * saturation + grayscale * inverse); - b = uclamp<16>(b * saturation + grayscale * inverse); - } - - if(settings["Video/Gamma"].natural() != 100) { - double exponent = settings["Video/Gamma"].natural() * 0.01; - double reciprocal = 1.0 / 32767.0; - r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent); - g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent); - b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent); - } - - if(settings["Video/Luminance"].natural() != 100) { - double luminance = settings["Video/Luminance"].natural() * 0.01; - r = r * luminance; - g = g * luminance; - b = b * luminance; - } - - a >>= 8; - r >>= 8; - g >>= 8; - b >>= 8; - return a << 24 | r << 16 | g << 8 | b << 0; -} - -auto Program::videoRefresh(const uint32* palette, const uint32* data, uint pitch, uint width, uint height) -> void { +auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { uint32* output; uint length; @@ -88,7 +56,7 @@ auto Program::videoRefresh(const uint32* palette, const uint32* data, uint pitch const uint32* sp = data + y * pitch; uint32* dp = output + y * length; for(auto x : range(width)) { - *dp++ = palette[*sp++]; + *dp++ = *sp++; } } diff --git a/higan/target-tomoko/program/media.cpp b/higan/target-tomoko/program/media.cpp index 8442535e..a3d3276e 100644 --- a/higan/target-tomoko/program/media.cpp +++ b/higan/target-tomoko/program/media.cpp @@ -16,13 +16,12 @@ auto Program::loadMedia(string location) -> void { auto Program::loadMedia(Emulator::Interface& emulator_, Emulator::Interface::Media& media, const string& location) -> void { unloadMedia(); - mediaPaths(0) = locate({settings["Library/Location"].text(), "System/"}, {media.name, ".sys/"}); + mediaPaths(0) = locate({localpath(), "higan/"}, {media.name, ".sys/"}); mediaPaths(media.id) = location; folderPaths.append(location); emulator = &emulator_; emulator->load(media.id); - updateVideoPalette(); dsp.setFrequency(emulator->audioFrequency()); emulator->power(); diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 87422f6d..cce4e59b 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -11,7 +11,7 @@ Program* program = nullptr; Program::Program(lstring args) { program = this; - directory::create({configpath(), "tomoko/"}); + directory::create({localpath(), "tomoko/"}); Application::onMain({&Program::main, this}); Application::Windows::onModalChange([](bool modal) { if(modal && audio) audio->clear(); }); @@ -72,14 +72,27 @@ Program::Program(lstring args) { if(argument == "--fullscreen") { presentation->toggleFullScreen(); } else { - auto location = argument; - if(directory::exists(location)) { - loadMedia(location); - } else if(file::exists(location)) { - if(auto result = execute("icarus", "--import", location)) { - loadMedia(result.strip()); - } + load(argument); + } + } +} + +auto Program::load(string location) -> void { + if(directory::exists(location)) { + loadMedia(location); + } else if(file::exists(location)) { + //special handling to allow importing the Game Boy Advance BIOS + if(file::size(location) == 16384 && file::sha256(location).beginsWith("fd2547724b505f48")) { + auto target = locate({localpath(), "higan/"}, "Game Boy Advance.sys/"); + if(file::copy(location, {target, "bios.rom"})) { + MessageDialog().setTitle(Emulator::Name).setText("Game Boy Advance BIOS imported successfully!").information(); } + return; + } + + //ask icarus to import the game; and play it upon success + if(auto result = execute("icarus", "--import", location)) { + loadMedia(result.strip()); } } } diff --git a/higan/target-tomoko/program/program.hpp b/higan/target-tomoko/program/program.hpp index 7a630f16..9033c311 100644 --- a/higan/target-tomoko/program/program.hpp +++ b/higan/target-tomoko/program/program.hpp @@ -1,6 +1,7 @@ struct Program : Emulator::Interface::Bind { //program.cpp Program(lstring args); + auto load(string) -> void; auto main() -> void; auto quit() -> void; @@ -8,8 +9,7 @@ struct Program : Emulator::Interface::Bind { auto loadRequest(uint id, string name, string type, bool required) -> void override; auto loadRequest(uint id, string path, bool required) -> void override; auto saveRequest(uint id, string path) -> void override; - auto videoColor(uint source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 override; - auto videoRefresh(const uint32* palette, const uint32* data, uint pitch, uint width, uint height) -> void override; + auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override; auto audioSample(int16 lsample, int16 rsample) -> void override; auto inputPoll(uint port, uint device, uint input) -> int16 override; auto inputRumble(uint port, uint device, uint input, bool enable) -> void override; @@ -33,7 +33,6 @@ struct Program : Emulator::Interface::Bind { auto showMessage(const string& text) -> void; auto updateStatusText() -> void; auto updateVideoFilter() -> void; - auto updateVideoPalette() -> void; auto updateAudio() -> void; auto updateAudioVolume() -> void; auto updateDSP() -> void; diff --git a/higan/target-tomoko/program/utility.cpp b/higan/target-tomoko/program/utility.cpp index 331eefc0..03ba5095 100644 --- a/higan/target-tomoko/program/utility.cpp +++ b/higan/target-tomoko/program/utility.cpp @@ -48,14 +48,6 @@ auto Program::updateVideoFilter() -> void { } } -auto Program::updateVideoPalette() -> void { - if(!emulator) return; - emulator->paletteUpdate(settings["Video/ColorEmulation"].boolean() - ? Emulator::Interface::PaletteMode::Emulation - : Emulator::Interface::PaletteMode::Standard - ); -} - auto Program::updateAudio() -> void { if(!audio) return; audio->clear(); diff --git a/higan/target-tomoko/settings/hotkeys.cpp b/higan/target-tomoko/settings/hotkeys.cpp index da2bfff4..030e5e55 100644 --- a/higan/target-tomoko/settings/hotkeys.cpp +++ b/higan/target-tomoko/settings/hotkeys.cpp @@ -73,6 +73,6 @@ auto HotkeySettings::inputEvent(shared_pointer device, uint group, timer.setEnabled(false); settingsManager->statusBar.setText(); settingsManager->layout.setEnabled(); - }).setInterval(1000).setEnabled(); + }).setInterval(200).setEnabled(); } } diff --git a/higan/target-tomoko/settings/input.cpp b/higan/target-tomoko/settings/input.cpp index 1931668f..ebafdb90 100644 --- a/higan/target-tomoko/settings/input.cpp +++ b/higan/target-tomoko/settings/input.cpp @@ -145,6 +145,6 @@ auto InputSettings::inputEvent(shared_pointer device, uint group, u timer.setEnabled(false); settingsManager->statusBar.setText(); settingsManager->layout.setEnabled(); - }).setInterval(1000).setEnabled(); + }).setInterval(200).setEnabled(); } } diff --git a/higan/target-tomoko/settings/settings.hpp b/higan/target-tomoko/settings/settings.hpp index 21efb622..0fc6380a 100644 --- a/higan/target-tomoko/settings/settings.hpp +++ b/higan/target-tomoko/settings/settings.hpp @@ -2,19 +2,6 @@ struct VideoSettings : TabFrameItem { VideoSettings(TabFrame*); VerticalLayout layout{this}; - Label colorAdjustmentLabel{&layout, Size{~0, 0}}; - HorizontalLayout saturationLayout{&layout, Size{~0, 0}}; - Label saturationLabel{&saturationLayout, Size{80, 0}}; - Label saturationValue{&saturationLayout, Size{80, 0}}; - HorizontalSlider saturationSlider{&saturationLayout, Size{~0, 0}}; - HorizontalLayout gammaLayout{&layout, Size{~0, 0}}; - Label gammaLabel{&gammaLayout, Size{80, 0}}; - Label gammaValue{&gammaLayout, Size{80, 0}}; - HorizontalSlider gammaSlider{&gammaLayout, Size{~0, 0}}; - HorizontalLayout luminanceLayout{&layout, Size{~0, 0}}; - Label luminanceLabel{&luminanceLayout, Size{80, 0}}; - Label luminanceValue{&luminanceLayout, Size{80, 0}}; - HorizontalSlider luminanceSlider{&luminanceLayout, Size{~0, 0}}; Label overscanMaskLabel{&layout, Size{~0, 0}}; HorizontalLayout horizontalMaskLayout{&layout, Size{~0, 0}}; Label horizontalMaskLabel{&horizontalMaskLayout, Size{80, 0}}; diff --git a/higan/target-tomoko/settings/video.cpp b/higan/target-tomoko/settings/video.cpp index 5ca392fa..fc3ce544 100644 --- a/higan/target-tomoko/settings/video.cpp +++ b/higan/target-tomoko/settings/video.cpp @@ -4,14 +4,6 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) { layout.setMargin(5); - colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment"); - saturationLabel.setText("Saturation:"); - saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] { update(); }); - gammaLabel.setText("Gamma:"); - gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] { update(); }); - luminanceLabel.setText("Luminance:"); - luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] { update(); }); - overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask"); horizontalMaskLabel.setText("Horizontal:"); horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { update(); }); @@ -22,15 +14,8 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) { } auto VideoSettings::update() -> void { - settings["Video/Saturation"].setValue(saturationSlider.position()); - settings["Video/Gamma"].setValue(100 + gammaSlider.position()); - settings["Video/Luminance"].setValue(luminanceSlider.position()); settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position()); settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position()); - saturationValue.setText({saturationSlider.position(), "%"}); - gammaValue.setText({100 + gammaSlider.position(), "%"}); - luminanceValue.setText({luminanceSlider.position(), "%"}); horizontalMaskValue.setText({horizontalMaskSlider.position(), "px"}); verticalMaskValue.setText({verticalMaskSlider.position(), "px"}); - program->updateVideoPalette(); } diff --git a/higan/target-tomoko/tomoko.cpp b/higan/target-tomoko/tomoko.cpp index 88d77c99..ae916f07 100644 --- a/higan/target-tomoko/tomoko.cpp +++ b/higan/target-tomoko/tomoko.cpp @@ -14,7 +14,7 @@ auto locate(string pathname, string filename) -> string { #include auto nall::main(lstring args) -> void { - Application::setName("tomoko"); + Application::setName("higan"); new Program(args); Application::run(); } diff --git a/higan/target-tomoko/tools/cheat-database.cpp b/higan/target-tomoko/tools/cheat-database.cpp index 5c4cad9a..55caeed9 100644 --- a/higan/target-tomoko/tools/cheat-database.cpp +++ b/higan/target-tomoko/tools/cheat-database.cpp @@ -20,7 +20,7 @@ auto CheatDatabase::findCodes() -> void { if(!emulator) return; auto sha256 = emulator->sha256(); - auto contents = string::read(locate({localpath(), "tomoko/"}, "cheats.bml")); + auto contents = string::read(locate({localpath(), "higan/"}, "cheats.bml")); auto document = BML::unserialize(contents); for(auto cartridge : document.find("cartridge")) { diff --git a/nall/file.hpp b/nall/file.hpp index b659a245..6049b247 100644 --- a/nall/file.hpp +++ b/nall/file.hpp @@ -16,6 +16,7 @@ struct file : file_system_object, varint { enum class index : uint { absolute, relative }; static auto copy(const string& sourcename, const string& targetname) -> bool { + if(sourcename == targetname) return true; file rd, wr; if(rd.open(sourcename, mode::read) == false) return false; if(wr.open(targetname, mode::write) == false) return false; @@ -26,6 +27,7 @@ struct file : file_system_object, varint { //attempt to rename file first //this will fail if paths point to different file systems; fall back to copy+remove in this case static auto move(const string& sourcename, const string& targetname) -> bool { + if(sourcename == targetname) return true; if(rename(sourcename, targetname)) return true; if(!writable(sourcename)) return false; if(copy(sourcename, targetname)) {