mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r40 release.
byuu says: Changelog: - hiro: added BrowserDialog::openObject() [match file *or* folder by filters] - hiro: BrowserDialog accept button is now disabled when it would otherwise do nothing - eg openFile without a folder to enter or file to open selected - eg saveFile without a file name or with a file name that matches a folder name - bsnes: added support for gamepaks (game folders) - bsnes: store all save states inside per-game .bsz (ZIP) archives instead of .bst/ folders - this reduces the number of state files from 10+ to 1; without having folders sort before files - hiro: both gtk2 and gtk3 now use cairo to render Canvas; supports sx,sy [BearOso] - higan, bsnes: fast PPU/DSP are now run-time options instead of compile-time options - bsnes: disable fast PPU when loading Air Strike Patrol / Desert Fighter - bsnes: disable fast DSP when loading Koushien 2 - bsnes: added options to advanced panel to disable fast PPU and/or fast DSP
This commit is contained in:
parent
91bb781b73
commit
5a8c814e25
|
@ -4,11 +4,9 @@ include ../nall/GNUmakefile
|
|||
|
||||
binary := application
|
||||
target := bsnes
|
||||
profile := fast
|
||||
objects := libco emulator audio video resource
|
||||
|
||||
flags += -I. -I..
|
||||
flags += $(if $(call streq,$(profile),accurate),-DPROFILE_ACCURATE,-DPROFILE_FAST)
|
||||
|
||||
ifeq ($(platform),windows)
|
||||
ifeq ($(binary),application)
|
||||
|
|
|
@ -13,7 +13,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.39";
|
||||
static const string Version = "106.40";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -45,7 +45,7 @@ struct Thread {
|
|||
s.integer(_clock);
|
||||
}
|
||||
|
||||
protected:
|
||||
//protected:
|
||||
cothread_t _handle = nullptr;
|
||||
uintmax _frequency = 0;
|
||||
uintmax _scalar = 0;
|
||||
|
|
|
@ -2,7 +2,7 @@ processors += wdc65816 spc700 arm7tdmi gsu hg51b upd96050
|
|||
|
||||
objects += sfc-interface sfc-system sfc-controller
|
||||
objects += sfc-cartridge sfc-memory
|
||||
objects += sfc-cpu sfc-smp sfc-dsp $(if $(call streq,$(profile),accurate),sfc-ppu,sfc-ppu-fast)
|
||||
objects += sfc-cpu sfc-smp sfc-dsp sfc-ppu sfc-ppu-fast
|
||||
objects += sfc-expansion sfc-satellaview sfc-21fx
|
||||
objects += sfc-icd sfc-mcc sfc-dip sfc-event
|
||||
objects += sfc-sa1 sfc-superfx
|
||||
|
|
|
@ -192,18 +192,18 @@ auto DSP::main() -> void {
|
|||
}
|
||||
|
||||
auto DSP::tick() -> void {
|
||||
#if defined(PROFILE_ACCURATE)
|
||||
if(!system.fastDSP()) {
|
||||
step(3 * 8);
|
||||
synchronize(smp);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
auto DSP::sample(int16 left, int16 right) -> void {
|
||||
stream->sample(left / 32768.0, right / 32768.0);
|
||||
#if defined(PROFILE_FAST)
|
||||
if(system.fastDSP()) {
|
||||
step(32 * 3 * 8);
|
||||
synchronize(smp);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* register interface for S-SMP $00f2,$00f3 */
|
||||
|
|
|
@ -233,6 +233,9 @@ auto Interface::cheatSet(const string_vector& list) -> void {
|
|||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
if(name == "Fast PPU") return true;
|
||||
if(name == "Fast DSP") return true;
|
||||
if(name == "Mode") return true;
|
||||
if(name == "Blur Emulation") return true;
|
||||
if(name == "Color Emulation") return true;
|
||||
if(name == "Scanline Emulation") return true;
|
||||
|
@ -240,6 +243,14 @@ auto Interface::cap(const string& name) -> bool {
|
|||
}
|
||||
|
||||
auto Interface::get(const string& name) -> any {
|
||||
if(name == "Mode") return string{
|
||||
system.fastPPU() && system.fastDSP() ? "[Fast PPU+DSP] "
|
||||
: system.fastPPU() ? "[Fast PPU] "
|
||||
: system.fastDSP() ? "[Fast DSP] "
|
||||
: ""
|
||||
};
|
||||
if(name == "Fast PPU") return settings.fastPPU;
|
||||
if(name == "Fast DSP") return settings.fastDSP;
|
||||
if(name == "Blur Emulation") return settings.blurEmulation;
|
||||
if(name == "Color Emulation") return settings.colorEmulation;
|
||||
if(name == "Scanline Emulation") return settings.scanlineEmulation;
|
||||
|
@ -247,6 +258,14 @@ auto Interface::get(const string& name) -> any {
|
|||
}
|
||||
|
||||
auto Interface::set(const string& name, const any& value) -> bool {
|
||||
if(name == "Fast PPU" && value.is<bool>()) {
|
||||
settings.fastPPU = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
if(name == "Fast DSP" && value.is<bool>()) {
|
||||
settings.fastDSP = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
if(name == "Blur Emulation" && value.is<bool>()) {
|
||||
settings.blurEmulation = value.get<bool>();
|
||||
return true;
|
||||
|
|
|
@ -67,6 +67,9 @@ struct Interface : Emulator::Interface {
|
|||
};
|
||||
|
||||
struct Settings {
|
||||
bool fastPPU = false;
|
||||
bool fastDSP = false;
|
||||
|
||||
bool blurEmulation = true;
|
||||
bool colorEmulation = true;
|
||||
bool scanlineEmulation = true;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <sfc/sfc.hpp>
|
||||
#define PPU PPUfast
|
||||
#define ppu ppufast
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
|
@ -10,7 +12,6 @@ PPU ppu;
|
|||
#include "object.cpp"
|
||||
#include "window.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include <sfc/ppu/counter/serialization.cpp>
|
||||
|
||||
PPU::PPU() {
|
||||
output = new uint32[512 * 512] + 16 * 512; //overscan offset
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
//* vertical mosaic coordinates are not exact
|
||||
//* (hardware-mod) 128KB VRAM mode not supported
|
||||
|
||||
#define PPU PPUfast
|
||||
#define ppu ppufast
|
||||
|
||||
struct PPU : Thread, PPUcounter {
|
||||
alwaysinline auto interlace() const -> bool { return latch.interlace; }
|
||||
alwaysinline auto overscan() const -> bool { return latch.overscan; }
|
||||
|
@ -304,3 +307,6 @@ public:
|
|||
};
|
||||
|
||||
extern PPU ppu;
|
||||
|
||||
#undef PPU
|
||||
#undef ppu
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
auto PPU::latchCounters() -> void {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.latchCounters();
|
||||
}
|
||||
|
||||
cpu.synchronize(ppu);
|
||||
io.hcounter = hdot();
|
||||
io.vcounter = vcounter();
|
||||
latch.counters = 1;
|
||||
}
|
||||
|
||||
auto PPU::addressVRAM() const -> uint16 {
|
||||
uint16 address = io.vramAddress;
|
||||
switch(io.vramMapping) {
|
||||
|
@ -619,13 +630,6 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::latchCounters() -> void {
|
||||
cpu.synchronize(ppu);
|
||||
io.hcounter = hdot();
|
||||
io.vcounter = vcounter();
|
||||
latch.counters = 1;
|
||||
}
|
||||
|
||||
auto PPU::updateVideoMode() -> void {
|
||||
switch(io.bgMode) {
|
||||
case 0:
|
||||
|
|
|
@ -25,6 +25,10 @@ bg4(Background::ID::BG4) {
|
|||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
if(system.fastPPU()) {
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
output -= 16 * 512;
|
||||
delete[] output;
|
||||
}
|
||||
|
@ -78,6 +82,10 @@ auto PPU::main() -> void {
|
|||
}
|
||||
|
||||
auto PPU::load(Markup::Node node) -> bool {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.load(node);
|
||||
}
|
||||
|
||||
ppu1.version = max(1, min(1, node["ppu1/version"].natural()));
|
||||
ppu2.version = max(1, min(3, node["ppu2/version"].natural()));
|
||||
ppu.vram.mask = node["ppu1/ram/size"].natural() / sizeof(uint16) - 1;
|
||||
|
@ -86,6 +94,12 @@ auto PPU::load(Markup::Node node) -> bool {
|
|||
}
|
||||
|
||||
auto PPU::power(bool reset) -> void {
|
||||
if(system.fastPPU()) {
|
||||
ppufast.power(reset);
|
||||
_handle = ppufast._handle;
|
||||
return;
|
||||
}
|
||||
|
||||
create(Enter, system.cpuFrequency());
|
||||
PPUcounter::reset();
|
||||
memory::fill<uint32>(output, 512 * 480);
|
||||
|
@ -220,6 +234,10 @@ auto PPU::frame() -> void {
|
|||
}
|
||||
|
||||
auto PPU::refresh() -> void {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.refresh();
|
||||
}
|
||||
|
||||
auto output = this->output;
|
||||
if(!overscan()) output -= 12 * 512;
|
||||
auto pitch = 512;
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
#include <sfc/ppu-fast/ppu.hpp>
|
||||
|
||||
struct PPU : Thread, PPUcounter {
|
||||
alwaysinline auto interlace() const -> bool { return display.interlace; }
|
||||
alwaysinline auto overscan() const -> bool { return display.overscan; }
|
||||
alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; }
|
||||
//ppu.cpp
|
||||
alwaysinline auto interlace() const -> bool { if(system.fastPPU()) return ppufast.interlace(); return display.interlace; }
|
||||
alwaysinline auto overscan() const -> bool { if(system.fastPPU()) return ppufast.overscan(); return display.overscan; }
|
||||
alwaysinline auto vdisp() const -> uint { if(system.fastPPU()) return ppufast.vdisp(); return !io.overscan ? 225 : 240; }
|
||||
|
||||
PPU();
|
||||
~PPU();
|
||||
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto latchCounters() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
//ppu.cpp
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
|
||||
//io.cpp
|
||||
alwaysinline auto addressVRAM() const -> uint16;
|
||||
alwaysinline auto readVRAM() -> uint16;
|
||||
|
@ -25,10 +34,8 @@ struct PPU : Thread, PPUcounter {
|
|||
alwaysinline auto writeCGRAM(uint8 addr, uint15 data) -> void;
|
||||
auto readIO(uint24 addr, uint8 data) -> uint8;
|
||||
auto writeIO(uint24 addr, uint8 data) -> void;
|
||||
auto latchCounters() -> void;
|
||||
auto updateVideoMode() -> void;
|
||||
|
||||
private:
|
||||
struct VRAM {
|
||||
auto& operator[](uint addr) { return data[addr & mask]; }
|
||||
uint16 data[64 * 1024];
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
auto PPU::serialize(serializer& s) -> void {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.serialize(s);
|
||||
}
|
||||
|
||||
Thread::serialize(s);
|
||||
PPUcounter::serialize(s);
|
||||
|
||||
|
|
|
@ -46,21 +46,17 @@ namespace SuperFamicom {
|
|||
static inline auto PAL() -> bool;
|
||||
};
|
||||
|
||||
#include <sfc/system/system.hpp>
|
||||
#include <sfc/memory/memory.hpp>
|
||||
#include <sfc/ppu/counter/counter.hpp>
|
||||
|
||||
#include <sfc/cpu/cpu.hpp>
|
||||
#include <sfc/smp/smp.hpp>
|
||||
#include <sfc/dsp/dsp.hpp>
|
||||
#if defined(PROFILE_ACCURATE)
|
||||
#include <sfc/ppu/ppu.hpp>
|
||||
#elif defined(PROFILE_FAST)
|
||||
#include <sfc/ppu-fast/ppu.hpp>
|
||||
#endif
|
||||
|
||||
#include <sfc/controller/controller.hpp>
|
||||
#include <sfc/expansion/expansion.hpp>
|
||||
#include <sfc/system/system.hpp>
|
||||
#include <sfc/coprocessor/coprocessor.hpp>
|
||||
#include <sfc/slot/slot.hpp>
|
||||
#include <sfc/cartridge/cartridge.hpp>
|
||||
|
|
|
@ -13,6 +13,9 @@ auto System::serialize() -> serializer {
|
|||
s.array(hash);
|
||||
s.array(description);
|
||||
|
||||
s.boolean(hacks.fastPPU);
|
||||
s.boolean(hacks.fastDSP);
|
||||
|
||||
serializeAll(s);
|
||||
return s;
|
||||
}
|
||||
|
@ -31,6 +34,12 @@ auto System::unserialize(serializer& s) -> bool {
|
|||
if(signature != 0x31545342) return false;
|
||||
if(string{version} != Emulator::SerializerVersion) return false;
|
||||
|
||||
s.boolean(hacks.fastPPU);
|
||||
s.boolean(hacks.fastDSP);
|
||||
|
||||
settings.fastPPU = hacks.fastPPU;
|
||||
settings.fastDSP = hacks.fastDSP;
|
||||
|
||||
power(/* reset = */ false);
|
||||
serializeAll(s);
|
||||
return true;
|
||||
|
@ -42,9 +51,9 @@ auto System::serialize(serializer& s) -> void {
|
|||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
system.serialize(s);
|
||||
random.serialize(s);
|
||||
cartridge.serialize(s);
|
||||
system.serialize(s);
|
||||
cpu.serialize(s);
|
||||
smp.serialize(s);
|
||||
ppu.serialize(s);
|
||||
|
@ -91,6 +100,9 @@ auto System::serializeInit() -> void {
|
|||
s.array(hash);
|
||||
s.array(description);
|
||||
|
||||
s.boolean(hacks.fastPPU);
|
||||
s.boolean(hacks.fastDSP);
|
||||
|
||||
serializeAll(s);
|
||||
serializeSize = s.size();
|
||||
}
|
||||
|
|
|
@ -88,6 +88,9 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
hacks.fastPPU = settings.fastPPU;
|
||||
hacks.fastDSP = settings.fastDSP;
|
||||
|
||||
Emulator::video.reset();
|
||||
Emulator::video.setInterface(interface);
|
||||
Emulator::video.setPalette();
|
||||
|
|
|
@ -6,6 +6,9 @@ struct System {
|
|||
inline auto cpuFrequency() const -> double { return information.cpuFrequency; }
|
||||
inline auto apuFrequency() const -> double { return information.apuFrequency; }
|
||||
|
||||
inline auto fastPPU() const -> bool { return hacks.fastPPU; }
|
||||
inline auto fastDSP() const -> bool { return hacks.fastDSP; }
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
||||
|
@ -29,6 +32,11 @@ private:
|
|||
double apuFrequency = 32040.0 * 768.0;
|
||||
} information;
|
||||
|
||||
struct Hacks {
|
||||
bool fastPPU = false;
|
||||
bool fastDSP = false;
|
||||
} hacks;
|
||||
|
||||
uint serializeSize = 0;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -10,11 +10,11 @@ auto InputManager::bindHotkeys() -> void {
|
|||
}));
|
||||
|
||||
hotkeys.append(InputHotkey("Save State").onPress([&] {
|
||||
program->saveState({"Quick/Slot ", stateSlot});
|
||||
program->saveState({"quick/slot ", stateSlot});
|
||||
}));
|
||||
|
||||
hotkeys.append(InputHotkey("Load State").onPress([&] {
|
||||
program->loadState({"Quick/Slot ", stateSlot});
|
||||
program->loadState({"quick/slot ", stateSlot});
|
||||
}));
|
||||
|
||||
hotkeys.append(InputHotkey("Increment State Slot").onPress([&] {
|
||||
|
|
|
@ -13,7 +13,10 @@ Presentation::Presentation() {
|
|||
loadRecentGame.setText("Load Recent Game");
|
||||
updateRecentGames();
|
||||
resetSystem.setText("Reset System").setEnabled(false).onActivate([&] {
|
||||
if(emulator->loaded()) emulator->reset();
|
||||
if(emulator->loaded()) {
|
||||
program->applyHacks();
|
||||
emulator->reset();
|
||||
}
|
||||
});
|
||||
unloadGame.setText("Unload Game").setEnabled(false).onActivate([&] {
|
||||
program->unload();
|
||||
|
@ -110,18 +113,18 @@ Presentation::Presentation() {
|
|||
saveState.setText("Save State");
|
||||
for(uint index : range(QuickStates)) {
|
||||
saveState.append(MenuItem().setText({"Slot ", 1 + index}).onActivate([=] {
|
||||
program->saveState({"Quick/Slot ", 1 + index});
|
||||
program->saveState({"quick/slot ", 1 + index});
|
||||
}));
|
||||
}
|
||||
loadState.setText("Load State");
|
||||
for(uint index : range(QuickStates)) {
|
||||
loadState.append(MenuItem().setText({"Slot ", 1 + index}).onActivate([=] {
|
||||
program->loadState({"Quick/Slot ", 1 + index});
|
||||
program->loadState({"quick/slot ", 1 + index});
|
||||
}));
|
||||
}
|
||||
loadState.append(MenuSeparator());
|
||||
loadState.append(MenuItem().setText("Recovery Slot").onActivate([&] {
|
||||
program->loadState("Quick/Recovery Slot");
|
||||
loadState.append(MenuItem().setText("Recovery").onActivate([&] {
|
||||
program->loadState("quick/recovery");
|
||||
}));
|
||||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||
if(pauseEmulation.checked()) audio->clear();
|
||||
|
@ -134,14 +137,15 @@ Presentation::Presentation() {
|
|||
aboutWindow->setCentered(*this).setVisible().setFocused();
|
||||
});
|
||||
|
||||
viewport.setDroppable().onDrop([&](auto locations) {
|
||||
viewport.setDroppable().onDrop([&](string_vector locations) {
|
||||
program->gameQueue = locations;
|
||||
program->load();
|
||||
presentation->setFocused();
|
||||
setFocused();
|
||||
});
|
||||
|
||||
statusBar.setFont(Font().setBold());
|
||||
statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
|
||||
program->updateMessage();
|
||||
|
||||
onClose([&] {
|
||||
program->quit();
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
auto Program::openPakSFC(string name, vfs::file::mode mode) -> vfs::shared::file {
|
||||
return vfs::fs::file::open({superNintendo.location, name}, mode);
|
||||
}
|
||||
|
||||
auto Program::openPakGB(string name, vfs::file::mode mode) -> vfs::shared::file {
|
||||
return vfs::fs::file::open({gameBoy.location, name}, mode);
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
auto Program::openRomSFC(string name, vfs::file::mode mode) -> vfs::shared::file {
|
||||
if(name == "program.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.program.data(), superNintendo.program.size());
|
||||
}
|
||||
|
||||
if(name == "data.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.data.data(), superNintendo.data.size());
|
||||
}
|
||||
|
||||
if(name == "expansion.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.expansion.data(), superNintendo.expansion.size());
|
||||
}
|
||||
|
||||
if(name == "arm6.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x28000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x00000], 0x20000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=ARM6)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "arm6.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x28000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x20000], 0x08000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=ARM6)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "hg51bs169.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xc00) {
|
||||
return vfs::memory::file::open(superNintendo.firmware.data(), superNintendo.firmware.size());
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=HG51BS169)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "lr35902.boot.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x100) {
|
||||
return vfs::memory::file::open(superNintendo.firmware.data(), superNintendo.firmware.size());
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Boot,architecture=LR35902)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".boot.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "upd7725.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x2000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x0000], 0x1800);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=uPD7725)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "upd7725.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x2000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x1800], 0x0800);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=uPD7725)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "upd96050.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xd000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x0000], 0xc000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=uPD96050)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "upd96050.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xd000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0xc000], 0x1000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=uPD96050)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "save.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(name == "download.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".psr"), mode);
|
||||
}
|
||||
|
||||
if(name == "time.rtc") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".rtc"), mode);
|
||||
}
|
||||
|
||||
if(name == "arm6.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(name == "hg51bs169.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(name == "upd7725.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(name == "upd96050.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(name == "msu1/data.rom") {
|
||||
return vfs::fs::file::open({Location::notsuffix(superNintendo.location), ".msu"}, mode);
|
||||
}
|
||||
|
||||
if(name.match("msu1/track-*.pcm")) {
|
||||
name.trimLeft("msu1/track-", 1L);
|
||||
return vfs::fs::file::open({Location::notsuffix(superNintendo.location), name}, mode);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Program::openRomGB(string name, vfs::file::mode mode) -> vfs::shared::file {
|
||||
if(name == "program.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(gameBoy.program.data(), gameBoy.program.size());
|
||||
}
|
||||
|
||||
if(name == "save.ram") {
|
||||
return vfs::fs::file::open(path("Saves", gameBoy.location, ".sav"), mode);
|
||||
}
|
||||
|
||||
if(name == "time.rtc") {
|
||||
return vfs::fs::file::open(path("Saves", gameBoy.location, ".sav"), mode);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
|
@ -8,6 +8,7 @@ auto Program::load() -> void {
|
|||
if(emulator->load(media.id)) {
|
||||
gameQueue = {};
|
||||
connectDevices();
|
||||
applyHacks();
|
||||
emulator->power();
|
||||
presentation->setTitle(emulator->title());
|
||||
presentation->resetSystem.setEnabled(true);
|
||||
|
@ -44,11 +45,24 @@ auto Program::loadFile(string location) -> vector<uint8_t> {
|
|||
}
|
||||
|
||||
auto Program::loadSuperNintendo(string location) -> void {
|
||||
auto rom = loadFile(location);
|
||||
if(!rom) return;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
//game pak
|
||||
if(location.endsWith("/")) {
|
||||
rom.append(file::read({location, "program.rom" }));
|
||||
rom.append(file::read({location, "data.rom" }));
|
||||
rom.append(file::read({location, "expansion.rom"}));
|
||||
for(auto filename : directory::files(location, "*.boot.rom" )) rom.append(file::read({location, filename}));
|
||||
for(auto filename : directory::files(location, "*.program.rom")) rom.append(file::read({location, filename}));
|
||||
for(auto filename : directory::files(location, "*.data.rom" )) rom.append(file::read({location, filename}));
|
||||
} else {
|
||||
//game ROM
|
||||
rom = loadFile(location);
|
||||
}
|
||||
|
||||
//Heuristics::SuperFamicom() call will remove copier header from rom if present
|
||||
auto heuristics = Heuristics::SuperFamicom(rom, location);
|
||||
superNintendo.label = heuristics.label();
|
||||
superNintendo.manifest = heuristics.manifest();
|
||||
superNintendo.document = BML::unserialize(superNintendo.manifest);
|
||||
superNintendo.location = location;
|
||||
|
@ -77,8 +91,14 @@ auto Program::loadSuperNintendo(string location) -> void {
|
|||
}
|
||||
|
||||
auto Program::loadGameBoy(string location) -> void {
|
||||
auto rom = loadFile(location);
|
||||
if(!rom) return;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
//game pak
|
||||
if(location.endsWith("/")) {
|
||||
rom.append(file::read({location, "program.rom"}));
|
||||
} else {
|
||||
rom = loadFile(location);
|
||||
}
|
||||
|
||||
auto heuristics = Heuristics::GameBoy(rom, location);
|
||||
gameBoy.manifest = heuristics.manifest();
|
||||
|
|
|
@ -6,174 +6,47 @@
|
|||
#include <icarus/heuristics/sufami-turbo.cpp>
|
||||
|
||||
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
|
||||
//System
|
||||
vfs::shared::file result;
|
||||
|
||||
if(id == 0 && name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(Resource::System::Manifest.data(), Resource::System::Manifest.size());
|
||||
if(id == 0) { //System
|
||||
if(name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(Resource::System::Manifest.data(), Resource::System::Manifest.size());
|
||||
}
|
||||
|
||||
if(id == 0 && name == "boards.bml" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size());
|
||||
if(name == "boards.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size());
|
||||
}
|
||||
|
||||
if(id == 0 && name == "ipl.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(Resource::System::IPLROM.data(), Resource::System::IPLROM.size());
|
||||
}
|
||||
|
||||
//Super Famicom
|
||||
|
||||
if(id == 1 && name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.manifest.data<uint8_t>(), superNintendo.manifest.size());
|
||||
}
|
||||
|
||||
if(id == 1 && name == "program.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.program.data(), superNintendo.program.size());
|
||||
}
|
||||
|
||||
if(id == 1 && name == "data.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.data.data(), superNintendo.data.size());
|
||||
}
|
||||
|
||||
if(id == 1 && name == "expansion.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(superNintendo.expansion.data(), superNintendo.expansion.size());
|
||||
}
|
||||
|
||||
if(id == 1 && name == "arm6.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x28000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x00000], 0x20000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=ARM6)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
if(name == "ipl.rom" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(Resource::System::IPLROM.data(), Resource::System::IPLROM.size());
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "arm6.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x28000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x20000], 0x08000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=ARM6)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
if(id == 1) { //Super Famicom
|
||||
if(name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(superNintendo.manifest.data<uint8_t>(), superNintendo.manifest.size());
|
||||
} else if(superNintendo.location.endsWith("/")) {
|
||||
result = openPakSFC(name, mode);
|
||||
} else {
|
||||
result = openRomSFC(name, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "hg51bs169.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xc00) {
|
||||
return vfs::memory::file::open(superNintendo.firmware.data(), superNintendo.firmware.size());
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=HG51BS169)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
if(id == 2) { //Game Boy
|
||||
if(name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(gameBoy.manifest.data<uint8_t>(), gameBoy.manifest.size());
|
||||
} else if(gameBoy.location.endsWith("/")) {
|
||||
result = openPakGB(name, mode);
|
||||
} else {
|
||||
result = openRomGB(name, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "lr35902.boot.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x100) {
|
||||
return vfs::memory::file::open(superNintendo.firmware.data(), superNintendo.firmware.size());
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Boot,architecture=LR35902)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".boot.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
if(!result && required) {
|
||||
MessageDialog({"Error: missing required data: ", name}).setParent(*presentation).error();
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd7725.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x2000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x0000], 0x1800);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=uPD7725)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd7725.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0x2000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x1800], 0x0800);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=uPD7725)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd96050.program.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xd000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0x0000], 0xc000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Program,architecture=uPD96050)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".program.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd96050.data.rom" && mode == vfs::file::mode::read) {
|
||||
if(superNintendo.firmware.size() == 0xd000) {
|
||||
return vfs::memory::file::open(&superNintendo.firmware.data()[0xc000], 0x1000);
|
||||
}
|
||||
if(auto memory = superNintendo.document["game/board/memory(type=ROM,content=Data,architecture=uPD96050)"]) {
|
||||
string location = locate({"firmware/", memory["identifier"].text().downcase(), ".data.rom"});
|
||||
return vfs::fs::file::open(location, mode);
|
||||
}
|
||||
}
|
||||
|
||||
if(id == 1 && name == "save.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "download.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".psr"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "time.rtc") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".rtc"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "arm6.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "hg51bs169.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd7725.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "upd96050.data.ram") {
|
||||
return vfs::fs::file::open(path("Saves", superNintendo.location, ".srm"), mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name == "msu1/data.rom") {
|
||||
return vfs::fs::file::open({Location::notsuffix(superNintendo.location), ".msu"}, mode);
|
||||
}
|
||||
|
||||
if(id == 1 && name.match("msu1/track-*.pcm")) {
|
||||
name.trimLeft("msu1/track-", 1L);
|
||||
return vfs::fs::file::open({Location::notsuffix(superNintendo.location), name}, mode);
|
||||
}
|
||||
|
||||
//Game Boy
|
||||
|
||||
if(id == 2 && name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(gameBoy.manifest.data<uint8_t>(), gameBoy.manifest.size());
|
||||
}
|
||||
|
||||
if(id == 2 && name == "program.rom" && mode == vfs::file::mode::read) {
|
||||
return vfs::memory::file::open(gameBoy.program.data(), gameBoy.program.size());
|
||||
}
|
||||
|
||||
if(id == 2 && name == "save.ram") {
|
||||
return vfs::fs::file::open(path("Saves", gameBoy.location, ".sav"), mode);
|
||||
}
|
||||
|
||||
if(id == 2 && name == "time.rtc") {
|
||||
return vfs::fs::file::open(path("Saves", gameBoy.location, ".sav"), mode);
|
||||
}
|
||||
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Program::load(uint id, string name, string type, string_vector options) -> Emulator::Platform::Load {
|
||||
|
@ -185,10 +58,10 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
dialog.setTitle("Load Super Nintendo");
|
||||
dialog.setPath(path("Games", settings["Path/Recent/SuperNintendo"].text()));
|
||||
dialog.setFilters({string{"Super Nintendo Games|*.sfc:*.smc:*.zip"}});
|
||||
superNintendo.location = dialog.openFile();
|
||||
superNintendo.location = dialog.openObject();
|
||||
}
|
||||
if(file::exists(superNintendo.location)) {
|
||||
settings["Path/Recent/SuperNintendo"].setValue(Location::path(superNintendo.location));
|
||||
if(inode::exists(superNintendo.location)) {
|
||||
settings["Path/Recent/SuperNintendo"].setValue(Location::dir(superNintendo.location));
|
||||
loadSuperNintendo(superNintendo.location);
|
||||
return {id, ""};
|
||||
}
|
||||
|
@ -202,10 +75,10 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
dialog.setTitle("Load Game Boy");
|
||||
dialog.setPath(path("Games", settings["Path/Recent/GameBoy"].text()));
|
||||
dialog.setFilters({string{"Game Boy Games|*.gb:*.gbc:*.zip"}});
|
||||
gameBoy.location = dialog.openFile();
|
||||
gameBoy.location = dialog.openObject();
|
||||
}
|
||||
if(file::exists(gameBoy.location)) {
|
||||
settings["Path/Recent/GameBoy"].setValue(Location::path(gameBoy.location));
|
||||
if(inode::exists(gameBoy.location)) {
|
||||
settings["Path/Recent/GameBoy"].setValue(Location::dir(gameBoy.location));
|
||||
loadGameBoy(gameBoy.location);
|
||||
return {id, ""};
|
||||
}
|
||||
|
@ -242,7 +115,7 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
|||
current = chrono::timestamp();
|
||||
if(current != previous) {
|
||||
previous = current;
|
||||
statusText = {"FPS: ", frameCounter};
|
||||
statusText = {emulator->get("Mode").get<string>(), "FPS: ", frameCounter};
|
||||
frameCounter = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,17 +22,43 @@ auto Program::path(string type, string location, string extension) -> string {
|
|||
}
|
||||
}
|
||||
|
||||
if(type == "States") {
|
||||
if(auto path = settings["Path/States"].text()) {
|
||||
pathname = path;
|
||||
}
|
||||
}
|
||||
|
||||
if(type == "Cheats") {
|
||||
if(auto path = settings["Path/Cheats"].text()) {
|
||||
pathname = path;
|
||||
}
|
||||
}
|
||||
|
||||
if(type == "States") {
|
||||
if(auto path = settings["Path/States"].text()) {
|
||||
pathname = path;
|
||||
}
|
||||
}
|
||||
|
||||
return {pathname, prefix, suffix};
|
||||
}
|
||||
|
||||
auto Program::gamePath() -> string {
|
||||
if(!emulator->loaded()) return "";
|
||||
if(gameBoy.location) return gameBoy.location;
|
||||
return superNintendo.location;
|
||||
}
|
||||
|
||||
auto Program::cheatPath() -> string {
|
||||
if(!emulator->loaded()) return "";
|
||||
auto location = gamePath();
|
||||
if(location.endsWith("/")) {
|
||||
return {location, "cheats.bml"};
|
||||
} else {
|
||||
return path("Cheats", location, ".cht");
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::statePath() -> string {
|
||||
if(!emulator->loaded()) return "";
|
||||
auto location = gamePath();
|
||||
if(location.endsWith("/")) {
|
||||
return {location, "bsnes/states/"};
|
||||
} else {
|
||||
return path("States", location, ".bsz");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "../bsnes.hpp"
|
||||
#include "interface.cpp"
|
||||
#include "game.cpp"
|
||||
#include "game-pak.cpp"
|
||||
#include "game-rom.cpp"
|
||||
#include "paths.cpp"
|
||||
#include "state.cpp"
|
||||
#include "states.cpp"
|
||||
#include "utility.cpp"
|
||||
unique_pointer<Program> program;
|
||||
|
||||
|
@ -66,7 +68,7 @@ Program::Program(string_vector arguments) {
|
|||
for(auto& argument : arguments) {
|
||||
if(argument == "--fullscreen") {
|
||||
presentation->toggleFullscreenMode();
|
||||
} else if(file::exists(argument)) {
|
||||
} else if(inode::exists(argument)) {
|
||||
gameQueue.append(argument);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,21 @@ struct Program : Emulator::Platform {
|
|||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
//game-pak.cpp
|
||||
auto openPakSFC(string name, vfs::file::mode mode) -> vfs::shared::file;
|
||||
auto openPakGB(string name, vfs::file::mode mode) -> vfs::shared::file;
|
||||
|
||||
//game-rom.cpp
|
||||
auto openRomSFC(string name, vfs::file::mode mode) -> vfs::shared::file;
|
||||
auto openRomGB(string name, vfs::file::mode mode) -> vfs::shared::file;
|
||||
|
||||
//paths.cpp
|
||||
auto path(string type, string location, string extension = "") -> string;
|
||||
|
||||
//state.cpp
|
||||
auto gamePath() -> string;
|
||||
auto cheatPath() -> string;
|
||||
auto statePath() -> string;
|
||||
|
||||
//states.cpp
|
||||
auto loadState(string filename) -> bool;
|
||||
auto saveState(string filename) -> bool;
|
||||
auto saveRecoveryState() -> bool;
|
||||
|
@ -35,12 +45,14 @@ struct Program : Emulator::Platform {
|
|||
auto initializeInputDriver() -> void;
|
||||
auto updateVideoShader() -> void;
|
||||
auto connectDevices() -> void;
|
||||
auto applyHacks() -> void;
|
||||
auto showMessage(string text) -> void;
|
||||
auto updateMessage() -> void;
|
||||
auto focused() -> bool;
|
||||
|
||||
public:
|
||||
struct SuperNintendo {
|
||||
string label;
|
||||
string location;
|
||||
string manifest;
|
||||
Markup::Node document;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
auto Program::statePath() -> string {
|
||||
if(!emulator->loaded()) return "";
|
||||
return path("States", superNintendo.location, ".bst/");
|
||||
}
|
||||
|
||||
auto Program::loadState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
string prefix = Location::prefix(location);
|
||||
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
||||
if(filename != "Quick/Recovery Slot") saveRecoveryState();
|
||||
auto memory = file::read(location);
|
||||
serializer s{memory.data(), memory.size()};
|
||||
if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false;
|
||||
return showMessage({"Loaded [", prefix, "]"}), true;
|
||||
}
|
||||
|
||||
auto Program::saveState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
directory::create({statePath(), "Quick/"});
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
string prefix = Location::prefix(location);
|
||||
serializer s = emulator->serialize();
|
||||
if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false;
|
||||
if(!file::write(location, s.data(), s.size())) return showMessage({"Unable to write [", prefix, "] to disk"}), false;
|
||||
return showMessage({"Saved [", prefix, "]"}), true;
|
||||
}
|
||||
|
||||
auto Program::saveRecoveryState() -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
directory::create({statePath(), "Quick/"});
|
||||
serializer s = emulator->serialize();
|
||||
if(!s.size()) return false;
|
||||
if(!file::write({statePath(), "Quick/Recovery Slot.bst"}, s.data(), s.size())) return false;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
auto Program::loadState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
|
||||
string prefix = Location::file(filename);
|
||||
vector<uint8_t> memory;
|
||||
|
||||
if(gamePath().endsWith("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
||||
if(filename != "quick/recovery") saveRecoveryState();
|
||||
memory = file::read(location);
|
||||
} else {
|
||||
string location = {filename, ".bst"};
|
||||
Decode::ZIP input;
|
||||
if(input.open(statePath())) {
|
||||
for(auto& file : input.file) {
|
||||
if(file.name != location) continue;
|
||||
memory = input.extract(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(memory) {
|
||||
serializer s{memory.data(), memory.size()};
|
||||
if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false;
|
||||
return showMessage({"Loaded [", prefix, "]"}), true;
|
||||
} else {
|
||||
return showMessage({"[", prefix, "] not found"}), false;
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::saveState(string filename) -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
|
||||
string prefix = Location::file(filename);
|
||||
serializer s = emulator->serialize();
|
||||
if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false;
|
||||
|
||||
if(gamePath().endsWith("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
directory::create(Location::path(location));
|
||||
if(!file::write(location, s.data(), s.size())) return showMessage({"Unable to write [", prefix, "] to disk"}), false;
|
||||
} else {
|
||||
string location = {filename, ".bst"};
|
||||
|
||||
struct State {
|
||||
string name;
|
||||
vector<uint8_t> memory;
|
||||
};
|
||||
vector<State> states;
|
||||
|
||||
Decode::ZIP input;
|
||||
if(input.open(statePath())) {
|
||||
for(auto& file : input.file) {
|
||||
if(!file.name.endsWith(".bst")) continue;
|
||||
if(file.name == location) continue;
|
||||
states.append({file.name, input.extract(file)});
|
||||
}
|
||||
}
|
||||
|
||||
Encode::ZIP output{statePath()};
|
||||
for(auto& state : states) {
|
||||
output.append(state.name, state.memory.data(), state.memory.size());
|
||||
}
|
||||
output.append(location, s.data(), s.size());
|
||||
}
|
||||
|
||||
return showMessage({"Saved [", prefix, "]"}), true;
|
||||
}
|
||||
|
||||
auto Program::saveRecoveryState() -> bool {
|
||||
if(!emulator->loaded()) return false;
|
||||
string location = {statePath(), "quick/recovery.bst"};
|
||||
directory::create(Location::path(location));
|
||||
serializer s = emulator->serialize();
|
||||
if(!s.size()) return false;
|
||||
if(!file::write(location, s.data(), s.size())) return false;
|
||||
return true;
|
||||
}
|
|
@ -65,6 +65,18 @@ auto Program::connectDevices() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto Program::applyHacks() -> void {
|
||||
bool fastPPU = settingsWindow->advanced.fastPPUOption.checked();
|
||||
bool fastDSP = settingsWindow->advanced.fastDSPOption.checked();
|
||||
|
||||
auto label = superNintendo.label;
|
||||
if(label == "AIR STRIKE PATROL" || label == "DESERT FIGHTER") fastPPU = false;
|
||||
if(label == "KOUSHIEN_2") fastDSP = false;
|
||||
|
||||
emulator->set("Fast PPU", fastPPU);
|
||||
emulator->set("Fast DSP", fastDSP);
|
||||
}
|
||||
|
||||
auto Program::showMessage(string text) -> void {
|
||||
statusTime = chrono::timestamp();
|
||||
statusMessage = text;
|
||||
|
|
|
@ -109,4 +109,12 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings.save();
|
||||
}
|
||||
});
|
||||
|
||||
emulatorLabel.setText("Emulator").setFont(Font().setBold());
|
||||
fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/FastPPU"].boolean()).onToggle([&] {
|
||||
settings["Emulator/FastPPU"].setValue(fastPPUOption.checked());
|
||||
});
|
||||
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/FastDSP"].boolean()).onToggle([&] {
|
||||
settings["Emulator/FastDSP"].setValue(fastDSPOption.checked());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,18 +39,6 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Saves"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
statesLabel.setText("States:");
|
||||
statesPath.setEditable(false);
|
||||
statesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
settings["Path/States"].setValue(location);
|
||||
refreshPaths();
|
||||
}
|
||||
});
|
||||
statesReset.setText("Reset").onActivate([&] {
|
||||
settings["Path/States"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
cheatsLabel.setText("Cheats:");
|
||||
cheatsPath.setEditable(false);
|
||||
cheatsAssign.setText("Assign ...").onActivate([&] {
|
||||
|
@ -63,6 +51,18 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Cheats"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
statesLabel.setText("States:");
|
||||
statesPath.setEditable(false);
|
||||
statesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
settings["Path/States"].setValue(location);
|
||||
refreshPaths();
|
||||
}
|
||||
});
|
||||
statesReset.setText("Reset").onActivate([&] {
|
||||
settings["Path/States"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
|
||||
refreshPaths();
|
||||
}
|
||||
|
@ -83,14 +83,14 @@ auto PathSettings::refreshPaths() -> void {
|
|||
} else {
|
||||
savesPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||
}
|
||||
if(auto location = settings["Path/States"].text()) {
|
||||
statesPath.setText(location).setForegroundColor();
|
||||
} else {
|
||||
statesPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||
}
|
||||
if(auto location = settings["Path/Cheats"].text()) {
|
||||
cheatsPath.setText(location).setForegroundColor();
|
||||
} else {
|
||||
cheatsPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||
}
|
||||
if(auto location = settings["Path/States"].text()) {
|
||||
statesPath.setText(location).setForegroundColor();
|
||||
} else {
|
||||
statesPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ Settings::Settings() {
|
|||
set("Path/Games", "");
|
||||
set("Path/Patches", "");
|
||||
set("Path/Saves", "");
|
||||
set("Path/States", "");
|
||||
set("Path/Cheats", "");
|
||||
set("Path/States", "");
|
||||
set("Path/Recent/SuperNintendo", Path::user());
|
||||
set("Path/Recent/GameBoy", Path::user());
|
||||
set("Path/Recent/BSMemory", Path::user());
|
||||
|
@ -52,6 +52,8 @@ Settings::Settings() {
|
|||
|
||||
set("UserInterface/ShowStatusBar", true);
|
||||
|
||||
set("Emulator/FastPPU", true);
|
||||
set("Emulator/FastDSP", true);
|
||||
set("Emulator/AutoSaveMemory/Enable", true);
|
||||
set("Emulator/AutoSaveMemory/Interval", 30);
|
||||
|
||||
|
|
|
@ -90,16 +90,16 @@ public:
|
|||
LineEdit savesPath{&savesLayout, Size{~0, 0}};
|
||||
Button savesAssign{&savesLayout, Size{80, 0}};
|
||||
Button savesReset{&savesLayout, Size{80, 0}};
|
||||
HorizontalLayout statesLayout{&layout, Size{~0, 0}};
|
||||
Label statesLabel{&statesLayout, Size{55, 0}};
|
||||
LineEdit statesPath{&statesLayout, Size{~0, 0}};
|
||||
Button statesAssign{&statesLayout, Size{80, 0}};
|
||||
Button statesReset{&statesLayout, Size{80, 0}};
|
||||
HorizontalLayout cheatsLayout{&layout, Size{~0, 0}};
|
||||
Label cheatsLabel{&cheatsLayout, Size{55, 0}};
|
||||
LineEdit cheatsPath{&cheatsLayout, Size{~0, 0}};
|
||||
Button cheatsAssign{&cheatsLayout, Size{80, 0}};
|
||||
Button cheatsReset{&cheatsLayout, Size{80, 0}};
|
||||
HorizontalLayout statesLayout{&layout, Size{~0, 0}};
|
||||
Label statesLabel{&statesLayout, Size{55, 0}};
|
||||
LineEdit statesPath{&statesLayout, Size{~0, 0}};
|
||||
Button statesAssign{&statesLayout, Size{80, 0}};
|
||||
Button statesReset{&statesLayout, Size{80, 0}};
|
||||
};
|
||||
|
||||
struct AdvancedSettings : TabFrameItem {
|
||||
|
@ -115,6 +115,9 @@ public:
|
|||
ComboButton audioDriverOption{&driverLayout, Size{~0, 0}};
|
||||
Label inputDriverLabel{&driverLayout, Size{0, 0}};
|
||||
ComboButton inputDriverOption{&driverLayout, Size{~0, 0}};
|
||||
Label emulatorLabel{&layout, Size{~0, 0}, 2};
|
||||
CheckLabel fastPPUOption{&layout, Size{~0, 0}};
|
||||
CheckLabel fastDSPOption{&layout, Size{~0, 0}};
|
||||
};
|
||||
|
||||
struct SettingsWindow : Window {
|
||||
|
|
|
@ -191,7 +191,7 @@ auto CheatEditor::removeCheats() -> void {
|
|||
|
||||
auto CheatEditor::loadCheats() -> void {
|
||||
cheats.reset();
|
||||
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
||||
auto location = program->cheatPath();
|
||||
auto document = BML::unserialize(string::read(location));
|
||||
for(auto cheat : document.find("cheat")) {
|
||||
cheats.append({cheat["name"].text(), cheat["code"].text(), (bool)cheat["enable"]});
|
||||
|
@ -211,7 +211,7 @@ auto CheatEditor::saveCheats() -> void {
|
|||
document.append(" enable\n");
|
||||
document.append("\n");
|
||||
}
|
||||
auto location = program->path("Cheats", program->superNintendo.location, ".cht");
|
||||
auto location = program->cheatPath();
|
||||
if(document) {
|
||||
file::write(location, document);
|
||||
} else {
|
||||
|
|
|
@ -48,7 +48,7 @@ auto StateWindow::doChange() -> void {
|
|||
|| c == '|') valid = false;
|
||||
}
|
||||
if(auto input = nameValue.property("input")) {
|
||||
if(name != input && file::exists({program->statePath(), name, ".bst"})) valid = false;
|
||||
if(name != input && file::exists({program->statePath(), "managed/", name, ".bst"})) valid = false;
|
||||
}
|
||||
nameValue.setBackgroundColor(valid ? Color{} : Color{255, 224, 224});
|
||||
acceptButton.setEnabled(valid);
|
||||
|
@ -81,12 +81,12 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) {
|
|||
});
|
||||
loadButton.setText("Load").onActivate([&] {
|
||||
if(auto item = stateList.selected()) {
|
||||
program->loadState(item.cell(0).text());
|
||||
program->loadState({"managed/", item.cell(0).text()});
|
||||
}
|
||||
});
|
||||
saveButton.setText("Save").onActivate([&] {
|
||||
if(auto item = stateList.selected()) {
|
||||
program->saveState(item.cell(0).text());
|
||||
program->saveState({"managed/", item.cell(0).text()});
|
||||
}
|
||||
});
|
||||
addButton.setText("Add").onActivate([&] {
|
||||
|
@ -107,16 +107,33 @@ auto StateManager::loadStates() -> void {
|
|||
stateList.append(TableViewHeader().setVisible(false)
|
||||
.append(TableViewColumn().setExpandable())
|
||||
);
|
||||
for(auto filename : directory::ifiles(program->statePath(), "*.bst")) {
|
||||
if(program->gamePath().endsWith("/")) {
|
||||
for(auto filename : directory::ifiles({program->statePath(), "managed/"}, "*.bst")) {
|
||||
stateList.append(TableViewItem()
|
||||
.append(TableViewCell().setText(filename.trimRight(".bst", 1L)))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Decode::ZIP input;
|
||||
if(input.open(program->statePath())) {
|
||||
string_vector states;
|
||||
for(auto& file : input.file) {
|
||||
if(!file.name.match("managed/*.bst")) continue;
|
||||
states.append(Location::prefix(file.name));
|
||||
}
|
||||
states.isort();
|
||||
for(auto& state : states) {
|
||||
stateList.append(TableViewItem()
|
||||
.append(TableViewCell().setText(state))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
stateList.resizeColumns().doChange();
|
||||
}
|
||||
|
||||
auto StateManager::createState(string name) -> void {
|
||||
program->saveState(name);
|
||||
program->saveState({"managed/", name});
|
||||
loadStates();
|
||||
for(auto item : stateList.items()) {
|
||||
if(item.cell(0).text() == name) item.setSelected();
|
||||
|
@ -126,8 +143,8 @@ auto StateManager::createState(string name) -> void {
|
|||
|
||||
auto StateManager::modifyState(string name) -> void {
|
||||
if(auto item = stateList.selected()) {
|
||||
string from = {program->statePath(), item.cell(0).text(), ".bst"};
|
||||
string to = {program->statePath(), name, ".bst"};
|
||||
string from = {program->statePath(), "managed/", item.cell(0).text(), ".bst"};
|
||||
string to = {program->statePath(), "managed/", name, ".bst"};
|
||||
if(from != to) {
|
||||
file::rename(from, to);
|
||||
loadStates();
|
||||
|
@ -144,7 +161,7 @@ auto StateManager::removeStates() -> void {
|
|||
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
|
||||
.setParent(*toolsWindow).question() == "Yes") {
|
||||
for(auto item : batched) {
|
||||
string location = {program->statePath(), item.cell(0).text(), ".bst"};
|
||||
string location = {program->statePath(), "managed/", item.cell(0).text(), ".bst"};
|
||||
file::remove(location);
|
||||
}
|
||||
loadStates();
|
||||
|
|
|
@ -36,28 +36,37 @@ private:
|
|||
auto BrowserDialogWindow::accept() -> void {
|
||||
auto batched = view.batched();
|
||||
|
||||
if(state.action == "openFile" && batched) {
|
||||
string name = batched.left()->cell(0)->text();
|
||||
if(state.action == "openFile" && batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(isFolder(name)) return setPath({state.path, name});
|
||||
response.selected.append(string{state.path, name});
|
||||
}
|
||||
|
||||
if(state.action == "openFiles") {
|
||||
if(state.action == "openFiles" && batched) {
|
||||
for(auto item : batched) {
|
||||
string name = item->cell(0)->text();
|
||||
response.selected.append(string{state.path, name, isFolder(name) ? "/" : ""});
|
||||
string name = item.text();
|
||||
if(isFolder(name)) {
|
||||
response.selected.reset();
|
||||
return;
|
||||
}
|
||||
response.selected.append(string{state.path, name});
|
||||
}
|
||||
}
|
||||
|
||||
if(state.action == "openFolder" && batched) {
|
||||
string name = batched.left()->cell(0)->text();
|
||||
if(state.action == "openFolder" && batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(!isMatch(name)) return setPath({state.path, name});
|
||||
response.selected.append(string{state.path, name, "/"});
|
||||
}
|
||||
|
||||
if(state.action == "openObject" && batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(!isMatch(name) && isFolder(name)) return setPath({state.path, name});
|
||||
response.selected.append(string{state.path, name, isFolder(name) ? "/" : ""});
|
||||
}
|
||||
|
||||
if(state.action == "saveFile") {
|
||||
string name = fileName.text();
|
||||
if(!name && batched) name = batched.left()->cell(0)->text();
|
||||
if(!name || isFolder(name)) return;
|
||||
if(file::exists({state.path, name})) {
|
||||
if(MessageDialog("File already exists. Overwrite it?").question() != "Yes") return;
|
||||
|
@ -66,11 +75,11 @@ auto BrowserDialogWindow::accept() -> void {
|
|||
}
|
||||
|
||||
if(state.action == "selectFolder") {
|
||||
if(batched) {
|
||||
string name = batched.left()->cell(0)->text();
|
||||
if(isFolder(name)) response.selected.append(string{state.path, name, "/"});
|
||||
} else {
|
||||
if(!batched) {
|
||||
response.selected.append(state.path);
|
||||
} else if(batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(isFolder(name)) response.selected.append(string{state.path, name, "/"});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,16 +88,16 @@ auto BrowserDialogWindow::accept() -> void {
|
|||
|
||||
//table view item double-clicked, or enter pressed on selected table view item
|
||||
auto BrowserDialogWindow::activate() -> void {
|
||||
auto selectedItem = view.selected();
|
||||
auto batched = view.batched();
|
||||
|
||||
if(state.action == "saveFile" && selectedItem) {
|
||||
string name = selectedItem->cell(0)->text();
|
||||
if(state.action == "saveFile" && batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(isFolder(name)) return setPath({state.path, name});
|
||||
fileName.setText(name);
|
||||
}
|
||||
|
||||
if(state.action == "selectFolder" && selectedItem) {
|
||||
string name = selectedItem->cell(0)->text();
|
||||
if(state.action == "selectFolder" && batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(isFolder(name)) return setPath({state.path, name});
|
||||
}
|
||||
|
||||
|
@ -97,12 +106,30 @@ auto BrowserDialogWindow::activate() -> void {
|
|||
|
||||
//table view item changed
|
||||
auto BrowserDialogWindow::change() -> void {
|
||||
fileName.setText("");
|
||||
if(state.action == "saveFile") {
|
||||
if(auto selectedItem = view.selected()) {
|
||||
string name = selectedItem->cell(0)->text();
|
||||
if(!isFolder(name)) fileName.setText(name);
|
||||
auto batched = view.batched();
|
||||
if(state.action == "openFile") {
|
||||
acceptButton.setEnabled(batched.size() == 1);
|
||||
}
|
||||
if(state.action == "openFiles") {
|
||||
bool enabled = true;
|
||||
for(auto item : batched) enabled &= !isFolder(item.text());
|
||||
if(batched.size() == 1 && isFolder(batched[0].text())) enabled = true;
|
||||
acceptButton.setEnabled(enabled);
|
||||
}
|
||||
if(state.action == "openFolder") {
|
||||
acceptButton.setEnabled(batched.size() == 1);
|
||||
}
|
||||
if(state.action == "openObject") {
|
||||
acceptButton.setEnabled(batched.size() == 1);
|
||||
}
|
||||
if(state.action == "saveFile") {
|
||||
if(batched.size() == 1) {
|
||||
string name = batched[0].text();
|
||||
if(!isFolder(name)) fileName.setText(name).doChange();
|
||||
}
|
||||
}
|
||||
if(state.action == "selectFolder") {
|
||||
acceptButton.setEnabled(!batched || (batched.size() == 1 && isFolder(batched[0].text())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,11 +165,15 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
|||
optionList.append(ComboButtonItem().setText(option));
|
||||
}
|
||||
optionList.doChange(); //updates response.option to point to the default (first) option
|
||||
fileName.setVisible(state.action == "saveFile").onActivate([&] { accept(); });
|
||||
fileName.setVisible(state.action == "saveFile").onActivate([&] { accept(); }).onChange([&] {
|
||||
auto name = fileName.text();
|
||||
acceptButton.setEnabled(name && !isFolder(name));
|
||||
fileName.setBackgroundColor(acceptButton.enabled() ? Color{} : Color{255, 224, 224});
|
||||
});
|
||||
acceptButton.onActivate([&] { accept(); });
|
||||
if(state.action == "openFile" || state.action == "openFiles" || state.action == "openFolder") acceptButton.setText("Open");
|
||||
if(state.action == "saveFile") acceptButton.setText("Save");
|
||||
if(state.action == "selectFolder") acceptButton.setText("Select");
|
||||
if(state.action.beginsWith("open")) acceptButton.setText("Open");
|
||||
if(state.action.beginsWith("save")) acceptButton.setText("Save");
|
||||
if(state.action.beginsWith("select")) acceptButton.setText("Select");
|
||||
cancelButton.setText("Cancel").onActivate([&] { window.setModal(false); });
|
||||
|
||||
if(!state.filters) state.filters.append("All|*");
|
||||
|
@ -170,26 +201,32 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
|||
path.transform("\\", "/");
|
||||
if((path || Path::root() == "/") && !path.endsWith("/")) path.append("/");
|
||||
pathName.setText(state.path = path);
|
||||
|
||||
view.reset();
|
||||
|
||||
auto contents = directory::icontents(path);
|
||||
bool folderMode = state.action == "openFolder";
|
||||
|
||||
for(auto content : contents) {
|
||||
if(!content.endsWith("/")) continue;
|
||||
content.trimRight("/");
|
||||
if(folderMode && isMatch(content)) continue;
|
||||
|
||||
bool isFolder = content.endsWith("/");
|
||||
if(isFolder) {
|
||||
content.trimRight("/", 1L);
|
||||
if(state.action == "openFolder" || state.action == "openObject") {
|
||||
if(isMatch(content)) continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
view.append(ListViewItem().setText(content).setIcon(Icon::Emblem::Folder));
|
||||
}
|
||||
|
||||
for(auto content : contents) {
|
||||
if(content.endsWith("/") != folderMode) continue; //file mode shows files; folder mode shows folders
|
||||
content.trimRight("/");
|
||||
bool isFolder = content.endsWith("/");
|
||||
if(isFolder) {
|
||||
content.trimRight("/", 1L);
|
||||
if(state.action != "openFolder" && state.action != "openObject") continue;
|
||||
} else {
|
||||
if(state.action == "openFolder") continue;
|
||||
}
|
||||
if(!isMatch(content)) continue;
|
||||
|
||||
view.append(ListViewItem().setText(content).setIcon(folderMode ? Icon::Action::Open : Icon::Emblem::File));
|
||||
view.append(ListViewItem().setText(content).setIcon(isFolder ? Icon::Action::Open : Icon::Emblem::File));
|
||||
}
|
||||
|
||||
Application::processEvents();
|
||||
|
@ -223,6 +260,13 @@ auto BrowserDialog::openFolder() -> string {
|
|||
return {};
|
||||
}
|
||||
|
||||
auto BrowserDialog::openObject() -> string {
|
||||
state.action = "openObject";
|
||||
if(!state.title) state.title = "Open Object";
|
||||
if(auto result = _run()) return result.left();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto BrowserDialog::option() -> string {
|
||||
return response.option;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ struct BrowserDialog {
|
|||
|
||||
BrowserDialog();
|
||||
auto openFile() -> string; //one existing file
|
||||
auto openFiles() -> string_vector; //any existing files or folders
|
||||
auto openFiles() -> string_vector; //any existing files
|
||||
auto openFolder() -> string; //one existing folder
|
||||
auto openObject() -> string; //one existing file or folder
|
||||
auto option() -> string;
|
||||
auto saveFile() -> string; //one file
|
||||
auto selected() -> string_vector;
|
||||
|
|
|
@ -10,11 +10,13 @@ GtkSelectionData* data, unsigned type, unsigned timestamp, pCanvas* p) -> void {
|
|||
p->self().doDrop(paths);
|
||||
}
|
||||
|
||||
//GTK3
|
||||
static auto Canvas_draw(GtkWidget* widget, cairo_t* context, pCanvas* p) -> signed {
|
||||
p->_onDraw(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
//GTK2
|
||||
static auto Canvas_expose(GtkWidget* widget, GdkEventExpose* event, pCanvas* p) -> signed {
|
||||
p->_onExpose(event);
|
||||
return true;
|
||||
|
@ -116,7 +118,8 @@ auto pCanvas::update() -> void {
|
|||
}
|
||||
|
||||
auto pCanvas::_onDraw(cairo_t* context) -> void {
|
||||
#if HIRO_GTK==3
|
||||
if(!surface) return;
|
||||
|
||||
int sx = 0, sy = 0, dx = 0, dy = 0;
|
||||
int width = surfaceWidth, height = surfaceHeight;
|
||||
auto geometry = pSizable::state().geometry;
|
||||
|
@ -137,41 +140,19 @@ auto pCanvas::_onDraw(cairo_t* context) -> void {
|
|||
dy = 0;
|
||||
}
|
||||
|
||||
//TODO: support non-zero sx,sy
|
||||
gdk_cairo_set_source_pixbuf(context, surface, dx, dy);
|
||||
cairo_set_source_rgba(context, 0.0, 0.0, 0.0, 0.0);
|
||||
cairo_paint(context);
|
||||
gdk_cairo_set_source_pixbuf(context, surface, dx - sx, dy - sy);
|
||||
cairo_rectangle(context, dx, dy, width, height);
|
||||
cairo_paint(context);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
|
||||
#if HIRO_GTK==2
|
||||
if(surface == nullptr) return;
|
||||
if(!surface) return;
|
||||
|
||||
int sx = 0, sy = 0, dx = 0, dy = 0;
|
||||
int width = surfaceWidth;
|
||||
int height = surfaceHeight;
|
||||
auto geometry = pSizable::state().geometry;
|
||||
|
||||
if(width <= geometry.width()) {
|
||||
sx = 0;
|
||||
dx = (geometry.width() - width) / 2;
|
||||
} else {
|
||||
sx = (width - geometry.width()) / 2;
|
||||
dx = 0;
|
||||
width = geometry.width();
|
||||
}
|
||||
|
||||
if(height <= geometry.height()) {
|
||||
sy = 0;
|
||||
dy = (geometry.height() - height) / 2;
|
||||
} else {
|
||||
sy = (height - geometry.height()) / 2;
|
||||
dy = 0;
|
||||
height = geometry.height();
|
||||
}
|
||||
|
||||
gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, sx, sy, dx, dy, width, height, GDK_RGB_DITHER_NONE, 0, 0);
|
||||
#endif
|
||||
cairo_t* context = gdk_cairo_create(gtk_widget_get_window(gtkWidget));
|
||||
_onDraw(context);
|
||||
cairo_destroy(context);
|
||||
}
|
||||
|
||||
auto pCanvas::_rasterize() -> void {
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#include <nall/encode/base64.hpp>
|
||||
#include <nall/encode/html.hpp>
|
||||
#include <nall/encode/url.hpp>
|
||||
#include <nall/encode/zip.hpp>
|
||||
#include <nall/hash/crc16.hpp>
|
||||
#include <nall/hash/crc32.hpp>
|
||||
#include <nall/hash/crc64.hpp>
|
||||
|
|
Loading…
Reference in New Issue