mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r45 release.
byuu says: Changelog: - sfc/ppu-fast: added hires mode 7 option (doubles the sampling rate of mode 7 pixels to reduce aliasing) - sfc/ppu-fast: fixed mode 7 horizontal screen flip [hex_usr] - bsnes: added capture screenshot function and path selection - for now, it saves as BMP. I need a deflate implementation that won't add an external dependency for PNG - the output resolution is from the emulator: (256 or 512)x(240 or 480 minus overscan cropping if enabled) - it captures the NEXT output frame, not the current one ... but it may be wise to change this behavior - it'd be a problem if the core were to exit and an image was captured halfway through frame rendering - bsnes: recovery state renamed to undo state - bsnes: added manifest viewer tool - bsnes: mention if game has been verified or not on the status bar message at load time - bsnes, nall: fixed a few missing function return values [SuperMikeMan] - bsnes: guard more strongly against failure to load games to avoid crashes - hiro, ruby: various fixes for macOS [Sintendo] - hiro/Windows: paint on `WM_ERASEBKGND` to prevent status bar flickering at startup - icarus: SPC7110 heuristics fixes [hex_usr] Errata: - sfc/ppu-fast: remove debug hires mode7 force disable comment from PPU::power() [The `WM_ERASEBKGND` fix was already present in the 106r44 public beta -Ed.]
This commit is contained in:
parent
40a5fbe605
commit
372e9ef42b
|
@ -13,7 +13,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.44";
|
static const string Version = "106.45";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
|
|
@ -235,6 +235,7 @@ auto Interface::cheatSet(const string_vector& list) -> void {
|
||||||
auto Interface::cap(const string& name) -> bool {
|
auto Interface::cap(const string& name) -> bool {
|
||||||
if(name == "Fast PPU") return true;
|
if(name == "Fast PPU") return true;
|
||||||
if(name == "Fast PPU/No Sprite Limit") return true;
|
if(name == "Fast PPU/No Sprite Limit") return true;
|
||||||
|
if(name == "Fast PPU/Hires Mode 7") return true;
|
||||||
if(name == "Fast DSP") return true;
|
if(name == "Fast DSP") return true;
|
||||||
if(name == "Mode") return true;
|
if(name == "Mode") return true;
|
||||||
if(name == "Blur Emulation") return true;
|
if(name == "Blur Emulation") return true;
|
||||||
|
@ -244,14 +245,9 @@ auto Interface::cap(const string& name) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::get(const string& name) -> any {
|
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 PPU") return settings.fastPPU;
|
||||||
if(name == "Fast PPU/No Sprite Limit") return settings.fastPPUNoSpriteLimit;
|
if(name == "Fast PPU/No Sprite Limit") return settings.fastPPUNoSpriteLimit;
|
||||||
|
if(name == "Fast PPU/Hires Mode 7") return settings.fastPPUHiresMode7;
|
||||||
if(name == "Fast DSP") return settings.fastDSP;
|
if(name == "Fast DSP") return settings.fastDSP;
|
||||||
if(name == "Blur Emulation") return settings.blurEmulation;
|
if(name == "Blur Emulation") return settings.blurEmulation;
|
||||||
if(name == "Color Emulation") return settings.colorEmulation;
|
if(name == "Color Emulation") return settings.colorEmulation;
|
||||||
|
@ -268,6 +264,10 @@ auto Interface::set(const string& name, const any& value) -> bool {
|
||||||
settings.fastPPUNoSpriteLimit = value.get<bool>();
|
settings.fastPPUNoSpriteLimit = value.get<bool>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if(name == "Fast PPU/Hires Mode 7" && value.is<bool>()) {
|
||||||
|
settings.fastPPUHiresMode7 = value.get<bool>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if(name == "Fast DSP" && value.is<bool>()) {
|
if(name == "Fast DSP" && value.is<bool>()) {
|
||||||
settings.fastDSP = value.get<bool>();
|
settings.fastDSP = value.get<bool>();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -69,6 +69,7 @@ struct Interface : Emulator::Interface {
|
||||||
struct Settings {
|
struct Settings {
|
||||||
bool fastPPU = false;
|
bool fastPPU = false;
|
||||||
bool fastPPUNoSpriteLimit = false;
|
bool fastPPUNoSpriteLimit = false;
|
||||||
|
bool fastPPUHiresMode7 = false;
|
||||||
bool fastDSP = false;
|
bool fastDSP = false;
|
||||||
|
|
||||||
bool blurEmulation = true;
|
bool blurEmulation = true;
|
||||||
|
|
|
@ -23,9 +23,10 @@ auto PPU::Line::render() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||||
|
bool hiresMode7 = io.bgMode == 7 && settings.fastPPUHiresMode7;
|
||||||
auto aboveColor = cgram[0];
|
auto aboveColor = cgram[0];
|
||||||
auto belowColor = hires ? cgram[0] : io.col.fixedColor;
|
auto belowColor = hires ? cgram[0] : io.col.fixedColor;
|
||||||
for(uint x : range(256)) {
|
for(uint x : range(256 << hiresMode7)) {
|
||||||
above[x] = {Source::COL, 0, aboveColor};
|
above[x] = {Source::COL, 0, aboveColor};
|
||||||
below[x] = {Source::COL, 0, belowColor};
|
below[x] = {Source::COL, 0, belowColor};
|
||||||
}
|
}
|
||||||
|
@ -39,7 +40,11 @@ auto PPU::Line::render() -> void {
|
||||||
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
|
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
|
||||||
|
|
||||||
auto luma = io.displayBrightness << 15;
|
auto luma = io.displayBrightness << 15;
|
||||||
if(width == 256) for(uint x : range(width)) {
|
if(hiresMode7) for(uint x : range(512)) {
|
||||||
|
auto Above = above[x >> 1].source >= Source::OBJ1 ? above[x >> 1] : above[x >> 1 | (x & 1 ? 256 : 0)];
|
||||||
|
auto Below = below[x >> 1].source >= Source::OBJ1 ? below[x >> 1] : below[x >> 1 | (x & 1 ? 256 : 0)];
|
||||||
|
*output++ = luma | pixel(x >> 1, Above, Below);
|
||||||
|
} else if(width == 256) for(uint x : range(256)) {
|
||||||
*output++ = luma | pixel(x, above[x], below[x]);
|
*output++ = luma | pixel(x, above[x], below[x]);
|
||||||
} else if(!hires) for(uint x : range(256)) {
|
} else if(!hires) for(uint x : range(256)) {
|
||||||
auto color = luma | pixel(x, above[x], below[x]);
|
auto color = luma | pixel(x, above[x], below[x]);
|
||||||
|
|
|
@ -25,6 +25,7 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
||||||
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
||||||
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
||||||
|
|
||||||
|
if(!settings.fastPPUHiresMode7) {
|
||||||
for(int X : range(256)) {
|
for(int X : range(256)) {
|
||||||
int x = !io.mode7.hflip ? X : 255 - X;
|
int x = !io.mode7.hflip ? X : 255 - X;
|
||||||
int pixelX = originX + a * x >> 8;
|
int pixelX = originX + a * x >> 8;
|
||||||
|
@ -34,10 +35,8 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
||||||
bool outOfBounds = (pixelX | pixelY) & ~1023;
|
bool outOfBounds = (pixelX | pixelY) & ~1023;
|
||||||
uint15 tileAddress = tileY * 128 + tileX;
|
uint15 tileAddress = tileY * 128 + tileX;
|
||||||
uint15 paletteAddress = ((pixelY & 7) << 3) + (pixelX & 7);
|
uint15 paletteAddress = ((pixelY & 7) << 3) + (pixelX & 7);
|
||||||
uint8 tile = ppu.vram[tileAddress].byte(0);
|
uint8 tile = io.mode7.repeat == 3 && outOfBounds ? 0 : ppu.vram[tileAddress].byte(0);
|
||||||
if(io.mode7.repeat == 3 && outOfBounds) tile = 0;
|
uint8 palette = io.mode7.repeat == 2 && outOfBounds ? 0 : ppu.vram[paletteAddress + (tile << 6)].byte(1);
|
||||||
uint8 palette = ppu.vram[paletteAddress + (tile << 6)].byte(1);
|
|
||||||
if(io.mode7.repeat == 2 && outOfBounds) palette = 0;
|
|
||||||
|
|
||||||
uint priority;
|
uint priority;
|
||||||
if(source == Source::BG1) {
|
if(source == Source::BG1) {
|
||||||
|
@ -59,7 +58,46 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
||||||
}
|
}
|
||||||
if(!mosaicPalette) continue;
|
if(!mosaicPalette) continue;
|
||||||
|
|
||||||
if(self.aboveEnable && !windowAbove[x]) plotAbove(x, source, mosaicPriority, mosaicColor);
|
if(self.aboveEnable && !windowAbove[X]) plotAbove(X, source, mosaicPriority, mosaicColor);
|
||||||
if(self.belowEnable && !windowBelow[x]) plotBelow(x, source, mosaicPriority, mosaicColor);
|
if(self.belowEnable && !windowBelow[X]) plotBelow(X, source, mosaicPriority, mosaicColor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//emulator enhancement option: render 512 pixels instead of 256 pixels of horizontal resolution
|
||||||
|
//note: this mode is impossible on real hardware due to needing 512 above+below pixels
|
||||||
|
for(int X : range(512)) {
|
||||||
|
int x = !io.mode7.hflip ? X : 511 - X;
|
||||||
|
int pixelX = 2 * originX + a * x >> 9;
|
||||||
|
int pixelY = 2 * originY + c * x >> 9;
|
||||||
|
int tileX = pixelX >> 3 & 127;
|
||||||
|
int tileY = pixelY >> 3 & 127;
|
||||||
|
bool outOfBounds = (pixelX | pixelY) & ~1023;
|
||||||
|
uint15 tileAddress = tileY * 128 + tileX;
|
||||||
|
uint15 paletteAddress = ((pixelY & 7) << 3) + (pixelX & 7);
|
||||||
|
uint8 tile = io.mode7.repeat == 3 && outOfBounds ? 0 : ppu.vram[tileAddress].byte(0);
|
||||||
|
uint8 palette = io.mode7.repeat == 2 && outOfBounds ? 0 : ppu.vram[paletteAddress + (tile << 6)].byte(1);
|
||||||
|
|
||||||
|
uint priority;
|
||||||
|
if(source == Source::BG1) {
|
||||||
|
priority = self.priority[0];
|
||||||
|
} else if(source == Source::BG2) {
|
||||||
|
priority = self.priority[palette >> 7];
|
||||||
|
palette &= 0x7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!self.mosaicEnable || !io.mosaicSize || --mosaicCounter == 0) {
|
||||||
|
mosaicCounter = (1 + io.mosaicSize) << 1;
|
||||||
|
mosaicPalette = palette;
|
||||||
|
mosaicPriority = priority;
|
||||||
|
if(io.col.directColor && source == Source::BG1) {
|
||||||
|
mosaicColor = directColor(0, palette);
|
||||||
|
} else {
|
||||||
|
mosaicColor = cgram[palette];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!mosaicPalette) continue;
|
||||||
|
|
||||||
|
if(self.aboveEnable && !windowAbove[X >> 1]) plotAbove(X >> 1 | (X & 1 ? 256 : 0), source, mosaicPriority, mosaicColor);
|
||||||
|
if(self.belowEnable && !windowBelow[X >> 1]) plotBelow(X >> 1 | (X & 1 ? 256 : 0), source, mosaicPriority, mosaicColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ auto PPU::scanline() -> void {
|
||||||
|
|
||||||
if(vcounter() > 0 && vcounter() < vdisp()) {
|
if(vcounter() > 0 && vcounter() < vdisp()) {
|
||||||
latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||||
|
latch.hires |= io.bgMode == 7 && settings.fastPPUHiresMode7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vcounter() == vdisp() && !io.displayDisable) {
|
if(vcounter() == vdisp() && !io.displayDisable) {
|
||||||
|
@ -103,6 +104,7 @@ auto PPU::load(Markup::Node node) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::power(bool reset) -> void {
|
auto PPU::power(bool reset) -> void {
|
||||||
|
//settings.fastPPUHiresMode7=false;
|
||||||
create(Enter, system.cpuFrequency());
|
create(Enter, system.cpuFrequency());
|
||||||
PPUcounter::reset();
|
PPUcounter::reset();
|
||||||
memory::fill<uint32>(output, 512 * 480);
|
memory::fill<uint32>(output, 512 * 480);
|
||||||
|
|
|
@ -295,8 +295,8 @@ public:
|
||||||
array<ObjectItem[128]> items; //32 on real hardware
|
array<ObjectItem[128]> items; //32 on real hardware
|
||||||
array<ObjectTile[128]> tiles; //34 on real hardware; 1024 max (128 * 64-width tiles)
|
array<ObjectTile[128]> tiles; //34 on real hardware; 1024 max (128 * 64-width tiles)
|
||||||
|
|
||||||
array<Pixel[256]> above;
|
array<Pixel[512]> above; //256 on real hardware
|
||||||
array<Pixel[256]> below;
|
array<Pixel[512]> below; //512 for hires mode 7
|
||||||
|
|
||||||
array<bool[256]> windowAbove;
|
array<bool[256]> windowAbove;
|
||||||
array<bool[256]> windowBelow;
|
array<bool[256]> windowBelow;
|
||||||
|
|
|
@ -27,6 +27,10 @@ auto InputManager::bindHotkeys() -> void {
|
||||||
program->showMessage({"Selected state slot ", stateSlot});
|
program->showMessage({"Selected state slot ", stateSlot});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
hotkeys.append(InputHotkey("Capture Screenshot").onPress([] {
|
||||||
|
presentation->captureScreenshot.doActivate();
|
||||||
|
}));
|
||||||
|
|
||||||
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
||||||
video->setBlocking(false);
|
video->setBlocking(false);
|
||||||
audio->setBlocking(false);
|
audio->setBlocking(false);
|
||||||
|
|
|
@ -139,7 +139,7 @@ Presentation::Presentation() {
|
||||||
}
|
}
|
||||||
loadState.append(MenuSeparator());
|
loadState.append(MenuSeparator());
|
||||||
loadState.append(MenuItem().setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] {
|
loadState.append(MenuItem().setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] {
|
||||||
program->loadState("quick/recovery");
|
program->loadState("quick/undo");
|
||||||
}));
|
}));
|
||||||
speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
|
speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
|
||||||
speedSlowest.setText("Slowest (50%)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
|
speedSlowest.setText("Slowest (50%)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
|
||||||
|
@ -150,8 +150,13 @@ Presentation::Presentation() {
|
||||||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||||
if(pauseEmulation.checked()) audio->clear();
|
if(pauseEmulation.checked()) audio->clear();
|
||||||
});
|
});
|
||||||
|
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
|
||||||
|
if(program->paused()) program->showMessage("The next video frame will be captured");
|
||||||
|
program->captureScreenshot = true;
|
||||||
|
});
|
||||||
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow->show(0); });
|
||||||
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow->show(1); });
|
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow->show(1); });
|
||||||
|
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow->show(2); });
|
||||||
|
|
||||||
helpMenu.setText("Help");
|
helpMenu.setText("Help");
|
||||||
documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] {
|
documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] {
|
||||||
|
|
|
@ -74,9 +74,11 @@ struct Presentation : Window {
|
||||||
MenuRadioItem speedFastest{&speedMenu};
|
MenuRadioItem speedFastest{&speedMenu};
|
||||||
Group speedGroup{&speedSlowest, &speedSlow, &speedNormal, &speedFast, &speedFastest};
|
Group speedGroup{&speedSlowest, &speedSlow, &speedNormal, &speedFast, &speedFastest};
|
||||||
MenuCheckItem pauseEmulation{&toolsMenu};
|
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||||
|
MenuItem captureScreenshot{&toolsMenu};
|
||||||
MenuSeparator toolsSeparatorB{&toolsMenu};
|
MenuSeparator toolsSeparatorB{&toolsMenu};
|
||||||
MenuItem cheatEditor{&toolsMenu};
|
MenuItem cheatEditor{&toolsMenu};
|
||||||
MenuItem stateManager{&toolsMenu};
|
MenuItem stateManager{&toolsMenu};
|
||||||
|
MenuItem manifestViewer{&toolsMenu};
|
||||||
Menu helpMenu{&menuBar};
|
Menu helpMenu{&menuBar};
|
||||||
MenuItem documentation{&helpMenu};
|
MenuItem documentation{&helpMenu};
|
||||||
MenuSeparator helpSeparator{&helpMenu};
|
MenuSeparator helpSeparator{&helpMenu};
|
||||||
|
|
|
@ -7,6 +7,7 @@ auto Program::load() -> void {
|
||||||
Emulator::audio.reset(2, audio->frequency());
|
Emulator::audio.reset(2, audio->frequency());
|
||||||
if(emulator->load(media.id)) {
|
if(emulator->load(media.id)) {
|
||||||
gameQueue = {};
|
gameQueue = {};
|
||||||
|
captureScreenshot = false;
|
||||||
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
|
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
|
||||||
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
|
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
|
||||||
//once MessageDialog functions, add an "Always" option
|
//once MessageDialog functions, add an "Always" option
|
||||||
|
@ -22,9 +23,12 @@ auto Program::load() -> void {
|
||||||
hackCompatibility();
|
hackCompatibility();
|
||||||
emulator->power();
|
emulator->power();
|
||||||
if(settingsWindow->advanced.autoLoadStateOnLoad.checked()) {
|
if(settingsWindow->advanced.autoLoadStateOnLoad.checked()) {
|
||||||
program->loadState("quick/recovery");
|
program->loadState("quick/undo");
|
||||||
}
|
}
|
||||||
showMessage(!appliedPatch() ? "Game loaded" : "Game loaded and patch applied");
|
showMessage({
|
||||||
|
verified() ? "Verified" : "Unverified", " game loaded",
|
||||||
|
appliedPatch() ? " and patch applied" : ""
|
||||||
|
});
|
||||||
presentation->setTitle(emulator->title());
|
presentation->setTitle(emulator->title());
|
||||||
presentation->resetSystem.setEnabled(true);
|
presentation->resetSystem.setEnabled(true);
|
||||||
presentation->unloadGame.setEnabled(true);
|
presentation->unloadGame.setEnabled(true);
|
||||||
|
@ -35,6 +39,7 @@ auto Program::load() -> void {
|
||||||
presentation->resizeViewport();
|
presentation->resizeViewport();
|
||||||
toolsWindow->cheatEditor.loadCheats();
|
toolsWindow->cheatEditor.loadCheats();
|
||||||
toolsWindow->stateManager.loadStates();
|
toolsWindow->stateManager.loadStates();
|
||||||
|
toolsWindow->manifestViewer.loadManifest();
|
||||||
|
|
||||||
string locations = superFamicom.location;
|
string locations = superFamicom.location;
|
||||||
if(auto location = gameBoy.location) locations.append("|", location);
|
if(auto location = gameBoy.location) locations.append("|", location);
|
||||||
|
@ -63,12 +68,13 @@ auto Program::loadFile(string location) -> vector<uint8_t> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
} else {
|
} else {
|
||||||
return file::read(location);
|
return file::read(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::loadSuperFamicom(string location) -> void {
|
auto Program::loadSuperFamicom(string location) -> bool {
|
||||||
string manifest;
|
string manifest;
|
||||||
vector<uint8_t> rom;
|
vector<uint8_t> rom;
|
||||||
|
|
||||||
|
@ -84,6 +90,7 @@ auto Program::loadSuperFamicom(string location) -> void {
|
||||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||||
rom = loadFile(location);
|
rom = loadFile(location);
|
||||||
}
|
}
|
||||||
|
if(rom.size() < 0x8000) return false;
|
||||||
|
|
||||||
//assume ROM and IPS agree on whether a copier header is present
|
//assume ROM and IPS agree on whether a copier header is present
|
||||||
superFamicom.patched = applyPatchIPS(rom, location);
|
superFamicom.patched = applyPatchIPS(rom, location);
|
||||||
|
@ -130,9 +137,11 @@ auto Program::loadSuperFamicom(string location) -> void {
|
||||||
memory::copy(&superFamicom.firmware[0], &rom[offset], size);
|
memory::copy(&superFamicom.firmware[0], &rom[offset], size);
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::loadGameBoy(string location) -> void {
|
auto Program::loadGameBoy(string location) -> bool {
|
||||||
string manifest;
|
string manifest;
|
||||||
vector<uint8_t> rom;
|
vector<uint8_t> rom;
|
||||||
|
|
||||||
|
@ -143,6 +152,7 @@ auto Program::loadGameBoy(string location) -> void {
|
||||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||||
rom = loadFile(location);
|
rom = loadFile(location);
|
||||||
}
|
}
|
||||||
|
if(rom.size() < 0x4000) return false;
|
||||||
|
|
||||||
gameBoy.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
gameBoy.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||||
auto heuristics = Heuristics::GameBoy(rom, location);
|
auto heuristics = Heuristics::GameBoy(rom, location);
|
||||||
|
@ -164,9 +174,10 @@ auto Program::loadGameBoy(string location) -> void {
|
||||||
gameBoy.location = location;
|
gameBoy.location = location;
|
||||||
|
|
||||||
gameBoy.program = rom;
|
gameBoy.program = rom;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::loadBSMemory(string location) -> void {
|
auto Program::loadBSMemory(string location) -> bool {
|
||||||
string manifest;
|
string manifest;
|
||||||
vector<uint8_t> rom;
|
vector<uint8_t> rom;
|
||||||
|
|
||||||
|
@ -178,6 +189,7 @@ auto Program::loadBSMemory(string location) -> void {
|
||||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||||
rom = loadFile(location);
|
rom = loadFile(location);
|
||||||
}
|
}
|
||||||
|
if(rom.size() < 0x8000) return false;
|
||||||
|
|
||||||
bsMemory.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
bsMemory.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||||
auto heuristics = Heuristics::BSMemory(rom, location);
|
auto heuristics = Heuristics::BSMemory(rom, location);
|
||||||
|
@ -193,9 +205,10 @@ auto Program::loadBSMemory(string location) -> void {
|
||||||
bsMemory.location = location;
|
bsMemory.location = location;
|
||||||
|
|
||||||
bsMemory.program = rom;
|
bsMemory.program = rom;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::loadSufamiTurboA(string location) -> void {
|
auto Program::loadSufamiTurboA(string location) -> bool {
|
||||||
string manifest;
|
string manifest;
|
||||||
vector<uint8_t> rom;
|
vector<uint8_t> rom;
|
||||||
|
|
||||||
|
@ -206,6 +219,7 @@ auto Program::loadSufamiTurboA(string location) -> void {
|
||||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||||
rom = loadFile(location);
|
rom = loadFile(location);
|
||||||
}
|
}
|
||||||
|
if(rom.size() < 0x20000) return false;
|
||||||
|
|
||||||
sufamiTurboA.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
sufamiTurboA.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||||
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
||||||
|
@ -221,9 +235,10 @@ auto Program::loadSufamiTurboA(string location) -> void {
|
||||||
sufamiTurboA.location = location;
|
sufamiTurboA.location = location;
|
||||||
|
|
||||||
sufamiTurboA.program = rom;
|
sufamiTurboA.program = rom;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::loadSufamiTurboB(string location) -> void {
|
auto Program::loadSufamiTurboB(string location) -> bool {
|
||||||
string manifest;
|
string manifest;
|
||||||
vector<uint8_t> rom;
|
vector<uint8_t> rom;
|
||||||
|
|
||||||
|
@ -234,6 +249,7 @@ auto Program::loadSufamiTurboB(string location) -> void {
|
||||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||||
rom = loadFile(location);
|
rom = loadFile(location);
|
||||||
}
|
}
|
||||||
|
if(rom.size() < 0x20000) return false;
|
||||||
|
|
||||||
sufamiTurboB.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
sufamiTurboB.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||||
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
||||||
|
@ -249,6 +265,7 @@ auto Program::loadSufamiTurboB(string location) -> void {
|
||||||
sufamiTurboB.location = location;
|
sufamiTurboB.location = location;
|
||||||
|
|
||||||
sufamiTurboB.program = rom;
|
sufamiTurboB.program = rom;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::save() -> void {
|
auto Program::save() -> void {
|
||||||
|
@ -268,7 +285,7 @@ auto Program::unload() -> void {
|
||||||
toolsWindow->cheatEditor.saveCheats();
|
toolsWindow->cheatEditor.saveCheats();
|
||||||
toolsWindow->setVisible(false);
|
toolsWindow->setVisible(false);
|
||||||
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
|
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
|
||||||
saveRecoveryState();
|
saveUndoState();
|
||||||
}
|
}
|
||||||
emulator->unload();
|
emulator->unload();
|
||||||
showMessage("Game unloaded");
|
showMessage("Game unloaded");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
auto Program::hackCompatibility() -> void {
|
auto Program::hackCompatibility() -> void {
|
||||||
bool fastPPU = settingsWindow->advanced.fastPPUOption.checked();
|
bool fastPPU = settingsWindow->advanced.fastPPUOption.checked();
|
||||||
bool fastPPUNoSpriteLimit = settingsWindow->advanced.noSpriteLimit.checked();
|
bool fastPPUNoSpriteLimit = settingsWindow->advanced.noSpriteLimit.checked();
|
||||||
|
bool fastPPUHiresMode7 = settingsWindow->advanced.hiresMode7.checked();
|
||||||
bool fastDSP = settingsWindow->advanced.fastDSPOption.checked();
|
bool fastDSP = settingsWindow->advanced.fastDSPOption.checked();
|
||||||
|
|
||||||
auto label = superFamicom.label;
|
auto label = superFamicom.label;
|
||||||
|
@ -10,6 +11,7 @@ auto Program::hackCompatibility() -> void {
|
||||||
|
|
||||||
emulator->set("Fast PPU", fastPPU);
|
emulator->set("Fast PPU", fastPPU);
|
||||||
emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit);
|
emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit);
|
||||||
|
emulator->set("Fast PPU/Hires Mode 7", fastPPUHiresMode7);
|
||||||
emulator->set("Fast DSP", fastDSP);
|
emulator->set("Fast DSP", fastDSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <nall/encode/bmp.hpp>
|
||||||
#include <icarus/heuristics/heuristics.hpp>
|
#include <icarus/heuristics/heuristics.hpp>
|
||||||
#include <icarus/heuristics/heuristics.cpp>
|
#include <icarus/heuristics/heuristics.cpp>
|
||||||
#include <icarus/heuristics/super-famicom.cpp>
|
#include <icarus/heuristics/super-famicom.cpp>
|
||||||
|
@ -117,10 +118,11 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
||||||
}
|
}
|
||||||
if(inode::exists(superFamicom.location)) {
|
if(inode::exists(superFamicom.location)) {
|
||||||
settings["Path/Recent/SuperFamicom"].setValue(Location::dir(superFamicom.location));
|
settings["Path/Recent/SuperFamicom"].setValue(Location::dir(superFamicom.location));
|
||||||
loadSuperFamicom(superFamicom.location);
|
if(loadSuperFamicom(superFamicom.location)) {
|
||||||
return {id, dialog.option()};
|
return {id, dialog.option()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(id == 2 && name == "Game Boy" && type == "gb") {
|
if(id == 2 && name == "Game Boy" && type == "gb") {
|
||||||
if(gameQueue) {
|
if(gameQueue) {
|
||||||
|
@ -133,10 +135,11 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
||||||
}
|
}
|
||||||
if(inode::exists(gameBoy.location)) {
|
if(inode::exists(gameBoy.location)) {
|
||||||
settings["Path/Recent/GameBoy"].setValue(Location::dir(gameBoy.location));
|
settings["Path/Recent/GameBoy"].setValue(Location::dir(gameBoy.location));
|
||||||
loadGameBoy(gameBoy.location);
|
if(loadGameBoy(gameBoy.location)) {
|
||||||
return {id, dialog.option()};
|
return {id, dialog.option()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(id == 3 && name == "BS Memory" && type == "bs") {
|
if(id == 3 && name == "BS Memory" && type == "bs") {
|
||||||
if(gameQueue) {
|
if(gameQueue) {
|
||||||
|
@ -149,10 +152,11 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
||||||
}
|
}
|
||||||
if(inode::exists(bsMemory.location)) {
|
if(inode::exists(bsMemory.location)) {
|
||||||
settings["Path/Recent/BSMemory"].setValue(Location::dir(bsMemory.location));
|
settings["Path/Recent/BSMemory"].setValue(Location::dir(bsMemory.location));
|
||||||
loadBSMemory(bsMemory.location);
|
if(loadBSMemory(bsMemory.location)) {
|
||||||
return {id, dialog.option()};
|
return {id, dialog.option()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(id == 4 && name == "Sufami Turbo" && type == "st") {
|
if(id == 4 && name == "Sufami Turbo" && type == "st") {
|
||||||
if(gameQueue) {
|
if(gameQueue) {
|
||||||
|
@ -165,10 +169,11 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
||||||
}
|
}
|
||||||
if(inode::exists(sufamiTurboA.location)) {
|
if(inode::exists(sufamiTurboA.location)) {
|
||||||
settings["Path/Recent/SufamiTurboA"].setValue(Location::dir(sufamiTurboA.location));
|
settings["Path/Recent/SufamiTurboA"].setValue(Location::dir(sufamiTurboA.location));
|
||||||
loadSufamiTurboA(sufamiTurboA.location);
|
if(loadSufamiTurboA(sufamiTurboA.location)) {
|
||||||
return {id, dialog.option()};
|
return {id, dialog.option()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(id == 5 && name == "Sufami Turbo" && type == "st") {
|
if(id == 5 && name == "Sufami Turbo" && type == "st") {
|
||||||
if(gameQueue) {
|
if(gameQueue) {
|
||||||
|
@ -181,10 +186,11 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
||||||
}
|
}
|
||||||
if(inode::exists(sufamiTurboB.location)) {
|
if(inode::exists(sufamiTurboB.location)) {
|
||||||
settings["Path/Recent/SufamiTurboB"].setValue(Location::dir(sufamiTurboB.location));
|
settings["Path/Recent/SufamiTurboB"].setValue(Location::dir(sufamiTurboB.location));
|
||||||
loadSufamiTurboB(sufamiTurboB.location);
|
if(loadSufamiTurboB(sufamiTurboB.location)) {
|
||||||
return {id, dialog.option()};
|
return {id, dialog.option()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -199,6 +205,15 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
||||||
if(height == 480) data += 16 * pitch, height -= 32;
|
if(height == 480) data += 16 * pitch, height -= 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(captureScreenshot) {
|
||||||
|
captureScreenshot = false;
|
||||||
|
if(auto filename = screenshotPath()) {
|
||||||
|
if(Encode::BMP::create(filename, (const uint32_t*)data, pitch << 2, width, height, false)) {
|
||||||
|
showMessage({"Captured screenshot [", Location::file(filename), "]"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(video->lock(output, length, width, height)) {
|
if(video->lock(output, length, width, height)) {
|
||||||
length >>= 2;
|
length >>= 2;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,12 @@ auto Program::path(string type, string location, string extension) -> string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(type == "Screenshots") {
|
||||||
|
if(auto path = settings["Path/Screenshots"].text()) {
|
||||||
|
pathname = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {pathname, prefix, suffix};
|
return {pathname, prefix, suffix};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,3 +68,19 @@ auto Program::statePath() -> string {
|
||||||
return path("States", location, ".bsz");
|
return path("States", location, ".bsz");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Program::screenshotPath() -> string {
|
||||||
|
if(!emulator->loaded()) return "";
|
||||||
|
auto location = gamePath();
|
||||||
|
if(location.endsWith("/")) {
|
||||||
|
directory::create({location, "bsnes/screenshots/"});
|
||||||
|
location = {location, "bsnes/screenshots/capture"};
|
||||||
|
} else {
|
||||||
|
location = path("Screenshots", location);
|
||||||
|
}
|
||||||
|
for(uint n : range(1, 999)) {
|
||||||
|
string filename = {location, ".", pad(n, 3, '0'), ".bmp"};
|
||||||
|
if(!file::exists(filename)) return filename;
|
||||||
|
}
|
||||||
|
return {location, ".000.bmp"};
|
||||||
|
}
|
||||||
|
|
|
@ -67,10 +67,7 @@ auto Program::main() -> void {
|
||||||
inputManager->poll();
|
inputManager->poll();
|
||||||
inputManager->pollHotkeys();
|
inputManager->pollHotkeys();
|
||||||
|
|
||||||
if(!emulator->loaded()
|
if(paused()) {
|
||||||
|| presentation->pauseEmulation.checked()
|
|
||||||
|| (!focused() && settingsWindow->input.pauseEmulation.checked())
|
|
||||||
) {
|
|
||||||
audio->clear();
|
audio->clear();
|
||||||
usleep(20 * 1000);
|
usleep(20 * 1000);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,11 +15,11 @@ struct Program : Emulator::Platform {
|
||||||
//game.cpp
|
//game.cpp
|
||||||
auto load() -> void;
|
auto load() -> void;
|
||||||
auto loadFile(string location) -> vector<uint8_t>;
|
auto loadFile(string location) -> vector<uint8_t>;
|
||||||
auto loadSuperFamicom(string location) -> void;
|
auto loadSuperFamicom(string location) -> bool;
|
||||||
auto loadGameBoy(string location) -> void;
|
auto loadGameBoy(string location) -> bool;
|
||||||
auto loadBSMemory(string location) -> void;
|
auto loadBSMemory(string location) -> bool;
|
||||||
auto loadSufamiTurboA(string location) -> void;
|
auto loadSufamiTurboA(string location) -> bool;
|
||||||
auto loadSufamiTurboB(string location) -> void;
|
auto loadSufamiTurboB(string location) -> bool;
|
||||||
auto save() -> void;
|
auto save() -> void;
|
||||||
auto reset() -> void;
|
auto reset() -> void;
|
||||||
auto unload() -> void;
|
auto unload() -> void;
|
||||||
|
@ -44,12 +44,13 @@ struct Program : Emulator::Platform {
|
||||||
auto gamePath() -> string;
|
auto gamePath() -> string;
|
||||||
auto cheatPath() -> string;
|
auto cheatPath() -> string;
|
||||||
auto statePath() -> string;
|
auto statePath() -> string;
|
||||||
|
auto screenshotPath() -> string;
|
||||||
|
|
||||||
//states.cpp
|
//states.cpp
|
||||||
auto managedStates() -> string_vector;
|
auto managedStates() -> string_vector;
|
||||||
auto loadState(string filename) -> bool;
|
auto loadState(string filename) -> bool;
|
||||||
auto saveState(string filename) -> bool;
|
auto saveState(string filename) -> bool;
|
||||||
auto saveRecoveryState() -> bool;
|
auto saveUndoState() -> bool;
|
||||||
auto removeState(string filename) -> bool;
|
auto removeState(string filename) -> bool;
|
||||||
auto renameState(string from, string to) -> bool;
|
auto renameState(string from, string to) -> bool;
|
||||||
|
|
||||||
|
@ -76,6 +77,7 @@ struct Program : Emulator::Platform {
|
||||||
auto showMessage(string text) -> void;
|
auto showMessage(string text) -> void;
|
||||||
auto showFrameRate(string text) -> void;
|
auto showFrameRate(string text) -> void;
|
||||||
auto updateStatus() -> void;
|
auto updateStatus() -> void;
|
||||||
|
auto paused() -> bool;
|
||||||
auto focused() -> bool;
|
auto focused() -> bool;
|
||||||
|
|
||||||
//patch.cpp
|
//patch.cpp
|
||||||
|
@ -120,6 +122,7 @@ public:
|
||||||
} sufamiTurboA, sufamiTurboB;
|
} sufamiTurboA, sufamiTurboB;
|
||||||
|
|
||||||
string_vector gameQueue;
|
string_vector gameQueue;
|
||||||
|
boolean captureScreenshot;
|
||||||
|
|
||||||
uint64 autoSaveTime;
|
uint64 autoSaveTime;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ auto Program::loadState(string filename) -> bool {
|
||||||
if(gamePath().endsWith("/")) {
|
if(gamePath().endsWith("/")) {
|
||||||
string location = {statePath(), filename, ".bst"};
|
string location = {statePath(), filename, ".bst"};
|
||||||
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
||||||
if(filename != "quick/recovery") saveRecoveryState();
|
if(filename != "quick/undo") saveUndoState();
|
||||||
memory = file::read(location);
|
memory = file::read(location);
|
||||||
} else {
|
} else {
|
||||||
string location = {filename, ".bst"};
|
string location = {filename, ".bst"};
|
||||||
|
@ -87,12 +87,13 @@ auto Program::saveState(string filename) -> bool {
|
||||||
return showMessage({"Saved [", prefix, "]"}), true;
|
return showMessage({"Saved [", prefix, "]"}), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::saveRecoveryState() -> bool {
|
auto Program::saveUndoState() -> bool {
|
||||||
auto statusTime = this->statusTime;
|
auto statusTime = this->statusTime;
|
||||||
auto statusMessage = this->statusMessage;
|
auto statusMessage = this->statusMessage;
|
||||||
saveState("quick/recovery");
|
auto result = saveState("quick/undo");
|
||||||
this->statusTime = statusTime;
|
this->statusTime = statusTime;
|
||||||
this->statusMessage = statusMessage;
|
this->statusMessage = statusMessage;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Program::removeState(string filename) -> bool {
|
auto Program::removeState(string filename) -> bool {
|
||||||
|
|
|
@ -31,6 +31,13 @@ auto Program::updateStatus() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Program::paused() -> bool {
|
||||||
|
if(!emulator->loaded()) return true;
|
||||||
|
if(presentation->pauseEmulation.checked()) return true;
|
||||||
|
if(!focused() && settingsWindow->input.pauseEmulation.checked()) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto Program::focused() -> bool {
|
auto Program::focused() -> bool {
|
||||||
//exclusive mode creates its own top-level window: presentation window will not have focus
|
//exclusive mode creates its own top-level window: presentation window will not have focus
|
||||||
if(video && video->exclusive()) return true;
|
if(video && video->exclusive()) return true;
|
||||||
|
|
|
@ -15,7 +15,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"Do you wish to proceed with the video driver change now anyway?"
|
"Do you wish to proceed with the video driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
program->save();
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveUndoState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->updateVideoDriver();
|
program->updateVideoDriver();
|
||||||
|
@ -34,7 +34,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"Do you wish to proceed with the audio driver change now anyway?"
|
"Do you wish to proceed with the audio driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
program->save();
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveUndoState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->updateAudioDriver();
|
program->updateAudioDriver();
|
||||||
|
@ -53,7 +53,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
"Do you wish to proceed with the input driver change now anyway?"
|
"Do you wish to proceed with the input driver change now anyway?"
|
||||||
).setParent(*settingsWindow).question() == "Yes") {
|
).setParent(*settingsWindow).question() == "Yes") {
|
||||||
program->save();
|
program->save();
|
||||||
program->saveRecoveryState();
|
program->saveUndoState();
|
||||||
settings["Crashed"].setValue(true);
|
settings["Crashed"].setValue(true);
|
||||||
settings.save();
|
settings.save();
|
||||||
program->updateInputDriver();
|
program->updateInputDriver();
|
||||||
|
@ -86,23 +86,29 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
settings["Emulator/Hack/FastPPU"].setValue(fastPPUOption.checked());
|
settings["Emulator/Hack/FastPPU"].setValue(fastPPUOption.checked());
|
||||||
if(!fastPPUOption.checked()) {
|
if(!fastPPUOption.checked()) {
|
||||||
noSpriteLimit.setEnabled(false).setChecked(false).doToggle();
|
noSpriteLimit.setEnabled(false).setChecked(false).doToggle();
|
||||||
|
hiresMode7.setEnabled(false).setChecked(false).doToggle();
|
||||||
} else {
|
} else {
|
||||||
noSpriteLimit.setEnabled(true);
|
noSpriteLimit.setEnabled(true);
|
||||||
|
hiresMode7.setEnabled(true);
|
||||||
}
|
}
|
||||||
}).doToggle();
|
}).doToggle();
|
||||||
noSpriteLimit.setText("No sprite limit").setChecked(settings["Emulator/Hack/FastPPU/NoSpriteLimit"].boolean()).onToggle([&] {
|
noSpriteLimit.setText("No sprite limit").setChecked(settings["Emulator/Hack/FastPPU/NoSpriteLimit"].boolean()).onToggle([&] {
|
||||||
settings["Emulator/Hack/FastPPU/NoSpriteLimit"].setValue(noSpriteLimit.checked());
|
settings["Emulator/Hack/FastPPU/NoSpriteLimit"].setValue(noSpriteLimit.checked());
|
||||||
});
|
});
|
||||||
|
hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] {
|
||||||
|
settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked());
|
||||||
|
emulator->set("Fast PPU/Hires Mode 7", hiresMode7.checked());
|
||||||
|
});
|
||||||
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] {
|
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] {
|
||||||
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
|
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
|
||||||
});
|
});
|
||||||
superFXLabel.setText("SuperFX Clock Speed:");
|
superFXLabel.setText("SuperFX clock speed:");
|
||||||
superFXValue.setAlignment(0.5);
|
superFXValue.setAlignment(0.5);
|
||||||
superFXClock.setLength(71).setPosition((settings["Emulator/Hack/FastSuperFX"].natural() - 100) / 10).onChange([&] {
|
superFXClock.setLength(71).setPosition((settings["Emulator/Hack/FastSuperFX"].natural() - 100) / 10).onChange([&] {
|
||||||
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
|
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
|
||||||
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
|
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
|
||||||
}).doChange();
|
}).doChange();
|
||||||
hacksNote.setForegroundColor({224, 0, 0}).setText("Note: hack setting changes do not take effect until after reloading games.");
|
hacksNote.setForegroundColor({224, 0, 0}).setText("Note: some hack setting changes do not take effect until after reloading games.");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AdvancedSettings::updateVideoDriver() -> void {
|
auto AdvancedSettings::updateVideoDriver() -> void {
|
||||||
|
|
|
@ -26,7 +26,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
});
|
});
|
||||||
|
|
||||||
effectsLabel.setFont(Font().setBold()).setText("Effects");
|
effectsLabel.setFont(Font().setBold()).setText("Effects");
|
||||||
skewLabel.setText("Skew:");
|
skewLabel.setAlignment(1.0).setText("Skew:");
|
||||||
skewValue.setAlignment(0.5);
|
skewValue.setAlignment(0.5);
|
||||||
skewSlider.setLength(10001).setPosition(settings["Audio/Skew"].integer() + 5000).onChange([&] {
|
skewSlider.setLength(10001).setPosition(settings["Audio/Skew"].integer() + 5000).onChange([&] {
|
||||||
string value = {skewSlider.position() > 5000 ? "+" : "", (int)skewSlider.position() - 5000};
|
string value = {skewSlider.position() > 5000 ? "+" : "", (int)skewSlider.position() - 5000};
|
||||||
|
@ -34,7 +34,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
skewValue.setText(value);
|
skewValue.setText(value);
|
||||||
program->updateAudioFrequency();
|
program->updateAudioFrequency();
|
||||||
}).doChange();
|
}).doChange();
|
||||||
volumeLabel.setText("Volume:");
|
volumeLabel.setAlignment(1.0).setText("Volume:");
|
||||||
volumeValue.setAlignment(0.5);
|
volumeValue.setAlignment(0.5);
|
||||||
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] {
|
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] {
|
||||||
string value = {volumeSlider.position(), "%"};
|
string value = {volumeSlider.position(), "%"};
|
||||||
|
@ -42,7 +42,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
volumeValue.setText(value);
|
volumeValue.setText(value);
|
||||||
program->updateAudioEffects();
|
program->updateAudioEffects();
|
||||||
}).doChange();
|
}).doChange();
|
||||||
balanceLabel.setText("Balance:");
|
balanceLabel.setAlignment(1.0).setText("Balance:");
|
||||||
balanceValue.setAlignment(0.5);
|
balanceValue.setAlignment(0.5);
|
||||||
balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] {
|
balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] {
|
||||||
string value = {balanceSlider.position(), "%"};
|
string value = {balanceSlider.position(), "%"};
|
||||||
|
|
|
@ -3,14 +3,14 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
setText("Input");
|
setText("Input");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
defocusLabel.setText("When Focus is Lost:");
|
defocusLabel.setText("When focus is lost:");
|
||||||
pauseEmulation.setText("Pause Emulation").onActivate([&] {
|
pauseEmulation.setText("Pause emulation").onActivate([&] {
|
||||||
settings["Input/Defocus"].setValue("Pause");
|
settings["Input/Defocus"].setValue("Pause");
|
||||||
});
|
});
|
||||||
blockInput.setText("Block Input").onActivate([&] {
|
blockInput.setText("Block input").onActivate([&] {
|
||||||
settings["Input/Defocus"].setValue("Block");
|
settings["Input/Defocus"].setValue("Block");
|
||||||
});
|
});
|
||||||
allowInput.setText("Allow Input").onActivate([&] {
|
allowInput.setText("Allow input").onActivate([&] {
|
||||||
settings["Input/Defocus"].setValue("Allow");
|
settings["Input/Defocus"].setValue("Allow");
|
||||||
});
|
});
|
||||||
if(settings["Input/Defocus"].text() == "Pause") pauseEmulation.setChecked();
|
if(settings["Input/Defocus"].text() == "Pause") pauseEmulation.setChecked();
|
||||||
|
|
|
@ -3,7 +3,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
setText("Paths");
|
setText("Paths");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
gamesLabel.setText("Games:");
|
|
||||||
|
gamesLabel.setAlignment(1.0).setText("Games:");
|
||||||
gamesPath.setEditable(false);
|
gamesPath.setEditable(false);
|
||||||
gamesAssign.setText("Assign ...").onActivate([&] {
|
gamesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
@ -15,7 +16,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
settings["Path/Games"].setValue("");
|
settings["Path/Games"].setValue("");
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
});
|
});
|
||||||
patchesLabel.setText("Patches:");
|
|
||||||
|
patchesLabel.setAlignment(1.0).setText("Patches:");
|
||||||
patchesPath.setEditable(false);
|
patchesPath.setEditable(false);
|
||||||
patchesAssign.setText("Assign ...").onActivate([&] {
|
patchesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
@ -27,7 +29,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
settings["Path/Patches"].setValue("");
|
settings["Path/Patches"].setValue("");
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
});
|
});
|
||||||
savesLabel.setText("Saves:");
|
|
||||||
|
savesLabel.setAlignment(1.0).setText("Saves:");
|
||||||
savesPath.setEditable(false);
|
savesPath.setEditable(false);
|
||||||
savesAssign.setText("Assign ...").onActivate([&] {
|
savesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
@ -39,7 +42,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
settings["Path/Saves"].setValue("");
|
settings["Path/Saves"].setValue("");
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
});
|
});
|
||||||
cheatsLabel.setText("Cheats:");
|
|
||||||
|
cheatsLabel.setAlignment(1.0).setText("Cheats:");
|
||||||
cheatsPath.setEditable(false);
|
cheatsPath.setEditable(false);
|
||||||
cheatsAssign.setText("Assign ...").onActivate([&] {
|
cheatsAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
@ -51,7 +55,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
settings["Path/Cheats"].setValue("");
|
settings["Path/Cheats"].setValue("");
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
});
|
});
|
||||||
statesLabel.setText("States:");
|
|
||||||
|
statesLabel.setAlignment(1.0).setText("States:");
|
||||||
statesPath.setEditable(false);
|
statesPath.setEditable(false);
|
||||||
statesAssign.setText("Assign ...").onActivate([&] {
|
statesAssign.setText("Assign ...").onActivate([&] {
|
||||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
@ -64,6 +69,19 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
screenshotsLabel.setAlignment(1.0).setText("Screenshots:");
|
||||||
|
screenshotsPath.setEditable(false);
|
||||||
|
screenshotsAssign.setText("Assign ...").onActivate([&] {
|
||||||
|
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||||
|
settings["Path/Screenshots"].setValue(location);
|
||||||
|
refreshPaths();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
screenshotsReset.setText("Reset").onActivate([&] {
|
||||||
|
settings["Path/Screenshots"].setValue("");
|
||||||
|
refreshPaths();
|
||||||
|
});
|
||||||
|
|
||||||
refreshPaths();
|
refreshPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,4 +111,9 @@ auto PathSettings::refreshPaths() -> void {
|
||||||
} else {
|
} else {
|
||||||
statesPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
statesPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||||
}
|
}
|
||||||
|
if(auto location = settings["Path/Screenshots"].text()) {
|
||||||
|
screenshotsPath.setText(location).setForegroundColor();
|
||||||
|
} else {
|
||||||
|
screenshotsPath.setText("<same as loaded game>").setForegroundColor({128, 128, 128});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ Settings::Settings() {
|
||||||
set("Path/Saves", "");
|
set("Path/Saves", "");
|
||||||
set("Path/Cheats", "");
|
set("Path/Cheats", "");
|
||||||
set("Path/States", "");
|
set("Path/States", "");
|
||||||
|
set("Path/Screenshots", "");
|
||||||
set("Path/Recent/SuperFamicom", Path::user());
|
set("Path/Recent/SuperFamicom", Path::user());
|
||||||
set("Path/Recent/GameBoy", Path::user());
|
set("Path/Recent/GameBoy", Path::user());
|
||||||
set("Path/Recent/BSMemory", Path::user());
|
set("Path/Recent/BSMemory", Path::user());
|
||||||
|
@ -66,6 +67,7 @@ Settings::Settings() {
|
||||||
set("Emulator/AutoLoadStateOnLoad", false);
|
set("Emulator/AutoLoadStateOnLoad", false);
|
||||||
set("Emulator/Hack/FastPPU", true);
|
set("Emulator/Hack/FastPPU", true);
|
||||||
set("Emulator/Hack/FastPPU/NoSpriteLimit", false);
|
set("Emulator/Hack/FastPPU/NoSpriteLimit", false);
|
||||||
|
set("Emulator/Hack/FastPPU/HiresMode7", false);
|
||||||
set("Emulator/Hack/FastDSP", true);
|
set("Emulator/Hack/FastDSP", true);
|
||||||
set("Emulator/Hack/FastSuperFX", "100%");
|
set("Emulator/Hack/FastSuperFX", "100%");
|
||||||
|
|
||||||
|
|
|
@ -123,30 +123,35 @@ struct PathSettings : TabFrameItem {
|
||||||
public:
|
public:
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
HorizontalLayout gamesLayout{&layout, Size{~0, 0}};
|
HorizontalLayout gamesLayout{&layout, Size{~0, 0}};
|
||||||
Label gamesLabel{&gamesLayout, Size{55, 0}};
|
Label gamesLabel{&gamesLayout, Size{80, 0}};
|
||||||
LineEdit gamesPath{&gamesLayout, Size{~0, 0}};
|
LineEdit gamesPath{&gamesLayout, Size{~0, 0}};
|
||||||
Button gamesAssign{&gamesLayout, Size{80, 0}};
|
Button gamesAssign{&gamesLayout, Size{80, 0}};
|
||||||
Button gamesReset{&gamesLayout, Size{80, 0}};
|
Button gamesReset{&gamesLayout, Size{80, 0}};
|
||||||
HorizontalLayout patchesLayout{&layout, Size{~0, 0}};
|
HorizontalLayout patchesLayout{&layout, Size{~0, 0}};
|
||||||
Label patchesLabel{&patchesLayout, Size{55, 0}};
|
Label patchesLabel{&patchesLayout, Size{80, 0}};
|
||||||
LineEdit patchesPath{&patchesLayout, Size{~0, 0}};
|
LineEdit patchesPath{&patchesLayout, Size{~0, 0}};
|
||||||
Button patchesAssign{&patchesLayout, Size{80, 0}};
|
Button patchesAssign{&patchesLayout, Size{80, 0}};
|
||||||
Button patchesReset{&patchesLayout, Size{80, 0}};
|
Button patchesReset{&patchesLayout, Size{80, 0}};
|
||||||
HorizontalLayout savesLayout{&layout, Size{~0, 0}};
|
HorizontalLayout savesLayout{&layout, Size{~0, 0}};
|
||||||
Label savesLabel{&savesLayout, Size{55, 0}};
|
Label savesLabel{&savesLayout, Size{80, 0}};
|
||||||
LineEdit savesPath{&savesLayout, Size{~0, 0}};
|
LineEdit savesPath{&savesLayout, Size{~0, 0}};
|
||||||
Button savesAssign{&savesLayout, Size{80, 0}};
|
Button savesAssign{&savesLayout, Size{80, 0}};
|
||||||
Button savesReset{&savesLayout, Size{80, 0}};
|
Button savesReset{&savesLayout, Size{80, 0}};
|
||||||
HorizontalLayout cheatsLayout{&layout, Size{~0, 0}};
|
HorizontalLayout cheatsLayout{&layout, Size{~0, 0}};
|
||||||
Label cheatsLabel{&cheatsLayout, Size{55, 0}};
|
Label cheatsLabel{&cheatsLayout, Size{80, 0}};
|
||||||
LineEdit cheatsPath{&cheatsLayout, Size{~0, 0}};
|
LineEdit cheatsPath{&cheatsLayout, Size{~0, 0}};
|
||||||
Button cheatsAssign{&cheatsLayout, Size{80, 0}};
|
Button cheatsAssign{&cheatsLayout, Size{80, 0}};
|
||||||
Button cheatsReset{&cheatsLayout, Size{80, 0}};
|
Button cheatsReset{&cheatsLayout, Size{80, 0}};
|
||||||
HorizontalLayout statesLayout{&layout, Size{~0, 0}};
|
HorizontalLayout statesLayout{&layout, Size{~0, 0}};
|
||||||
Label statesLabel{&statesLayout, Size{55, 0}};
|
Label statesLabel{&statesLayout, Size{80, 0}};
|
||||||
LineEdit statesPath{&statesLayout, Size{~0, 0}};
|
LineEdit statesPath{&statesLayout, Size{~0, 0}};
|
||||||
Button statesAssign{&statesLayout, Size{80, 0}};
|
Button statesAssign{&statesLayout, Size{80, 0}};
|
||||||
Button statesReset{&statesLayout, Size{80, 0}};
|
Button statesReset{&statesLayout, Size{80, 0}};
|
||||||
|
HorizontalLayout screenshotsLayout{&layout, Size{~0, 0}};
|
||||||
|
Label screenshotsLabel{&screenshotsLayout, Size{80, 0}};
|
||||||
|
LineEdit screenshotsPath{&screenshotsLayout, Size{~0, 0}};
|
||||||
|
Button screenshotsAssign{&screenshotsLayout, Size{80, 0}};
|
||||||
|
Button screenshotsReset{&screenshotsLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AdvancedSettings : TabFrameItem {
|
struct AdvancedSettings : TabFrameItem {
|
||||||
|
@ -175,6 +180,7 @@ public:
|
||||||
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
|
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
|
||||||
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
|
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
|
||||||
CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}};
|
CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}};
|
||||||
|
CheckLabel hiresMode7{&fastPPULayout, Size{0, 0}};
|
||||||
CheckLabel fastDSPOption{&layout, Size{~0, 0}};
|
CheckLabel fastDSPOption{&layout, Size{~0, 0}};
|
||||||
HorizontalLayout superFXLayout{&layout, Size{~0, 0}};
|
HorizontalLayout superFXLayout{&layout, Size{~0, 0}};
|
||||||
Label superFXLabel{&superFXLayout, Size{0, 0}};
|
Label superFXLabel{&superFXLayout, Size{0, 0}};
|
||||||
|
|
|
@ -5,7 +5,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
|
|
||||||
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
||||||
luminanceLabel.setText("Luminance:");
|
luminanceLabel.setAlignment(1.0).setText("Luminance:");
|
||||||
luminanceValue.setAlignment(0.5);
|
luminanceValue.setAlignment(0.5);
|
||||||
luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] {
|
luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] {
|
||||||
string value = {luminanceSlider.position(), "%"};
|
string value = {luminanceSlider.position(), "%"};
|
||||||
|
@ -13,7 +13,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
luminanceValue.setText(value);
|
luminanceValue.setText(value);
|
||||||
program->updateVideoPalette();
|
program->updateVideoPalette();
|
||||||
}).doChange();
|
}).doChange();
|
||||||
saturationLabel.setText("Saturation:");
|
saturationLabel.setAlignment(1.0).setText("Saturation:");
|
||||||
saturationValue.setAlignment(0.5);
|
saturationValue.setAlignment(0.5);
|
||||||
saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] {
|
saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] {
|
||||||
string value = {saturationSlider.position(), "%"};
|
string value = {saturationSlider.position(), "%"};
|
||||||
|
@ -21,7 +21,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
saturationValue.setText(value);
|
saturationValue.setText(value);
|
||||||
program->updateVideoPalette();
|
program->updateVideoPalette();
|
||||||
}).doChange();
|
}).doChange();
|
||||||
gammaLabel.setText("Gamma:");
|
gammaLabel.setAlignment(1.0).setText("Gamma:");
|
||||||
gammaValue.setAlignment(0.5);
|
gammaValue.setAlignment(0.5);
|
||||||
gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] {
|
gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] {
|
||||||
string value = {100 + gammaSlider.position(), "%"};
|
string value = {100 + gammaSlider.position(), "%"};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
ManifestViewer::ManifestViewer(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
|
setIcon(Icon::Emblem::Text);
|
||||||
|
setText("Manifest Viewer");
|
||||||
|
|
||||||
|
layout.setMargin(5);
|
||||||
|
manifestView.setEditable(false).setWordWrap(false).setFont(Font().setFamily(Font::Mono));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ManifestViewer::loadManifest() -> void {
|
||||||
|
if(!emulator->loaded()) {
|
||||||
|
manifestView.setText("");
|
||||||
|
verifiedIcon.setIcon({});
|
||||||
|
verifiedLabel.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestView.setText(emulator->manifest());
|
||||||
|
verifiedIcon.setIcon(program->verified() ? Icon::Emblem::Program : Icon::Emblem::Binary);
|
||||||
|
verifiedLabel.setText(program->verified() ? "Verified" : "Unverified");
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#include "../bsnes.hpp"
|
#include "../bsnes.hpp"
|
||||||
#include "cheat-editor.cpp"
|
#include "cheat-editor.cpp"
|
||||||
#include "state-manager.cpp"
|
#include "state-manager.cpp"
|
||||||
|
#include "manifest-viewer.cpp"
|
||||||
unique_pointer<CheatDatabase> cheatDatabase;
|
unique_pointer<CheatDatabase> cheatDatabase;
|
||||||
unique_pointer<CheatWindow> cheatWindow;
|
unique_pointer<CheatWindow> cheatWindow;
|
||||||
unique_pointer<StateWindow> stateWindow;
|
unique_pointer<StateWindow> stateWindow;
|
||||||
|
|
|
@ -107,6 +107,18 @@ public:
|
||||||
Button removeButton{&controlLayout, Size{80, 0}};
|
Button removeButton{&controlLayout, Size{80, 0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ManifestViewer : TabFrameItem {
|
||||||
|
ManifestViewer(TabFrame*);
|
||||||
|
auto loadManifest() -> void;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
TextEdit manifestView{&layout, Size{~0, ~0}};
|
||||||
|
HorizontalLayout verifiedLayout{&layout, Size{~0, 0}};
|
||||||
|
Canvas verifiedIcon{&verifiedLayout, Size{16, 16}};
|
||||||
|
Label verifiedLabel{&verifiedLayout, Size{~0, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
struct ToolsWindow : Window {
|
struct ToolsWindow : Window {
|
||||||
ToolsWindow();
|
ToolsWindow();
|
||||||
auto setVisible(bool visible = true) -> ToolsWindow&;
|
auto setVisible(bool visible = true) -> ToolsWindow&;
|
||||||
|
@ -117,6 +129,7 @@ public:
|
||||||
TabFrame panel{&layout, Size{~0, ~0}};
|
TabFrame panel{&layout, Size{~0, ~0}};
|
||||||
CheatEditor cheatEditor{&panel};
|
CheatEditor cheatEditor{&panel};
|
||||||
StateManager stateManager{&panel};
|
StateManager stateManager{&panel};
|
||||||
|
ManifestViewer manifestViewer{&panel};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unique_pointer<CheatDatabase> cheatDatabase;
|
extern unique_pointer<CheatDatabase> cheatDatabase;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#define decimal decimal_cocoa
|
#include <nall/macos/guard.hpp>
|
||||||
#import <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
#import <Carbon/Carbon.h>
|
#include <Carbon/Carbon.h>
|
||||||
#undef decimal
|
#include <nall/macos/guard.hpp>
|
||||||
|
|
||||||
#include <nall/run.hpp>
|
#include <nall/run.hpp>
|
||||||
|
|
|
@ -156,12 +156,12 @@
|
||||||
| kAuthorizationFlagExtendRights, nullptr);
|
| kAuthorizationFlagExtendRights, nullptr);
|
||||||
if(status == errAuthorizationSuccess) {
|
if(status == errAuthorizationSuccess) {
|
||||||
{ char program[] = "/usr/sbin/spctl";
|
{ char program[] = "/usr/sbin/spctl";
|
||||||
char arguments[] = {"--master-disable", nullptr};
|
char* arguments[] = {"--master-disable", nullptr};
|
||||||
FILE* pipe = nullptr;
|
FILE* pipe = nullptr;
|
||||||
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
||||||
}
|
}
|
||||||
{ char program[] = "/usr/bin/defaults";
|
{ char program[] = "/usr/bin/defaults";
|
||||||
char arguments[] = {"write /Library/Preferences/com.apple.security GKAutoRearm -bool NO"};
|
char* arguments[] = {"write /Library/Preferences/com.apple.security GKAutoRearm -bool NO"};
|
||||||
FILE* pipe = nullptr;
|
FILE* pipe = nullptr;
|
||||||
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
hiro::mWindow* window;
|
hiro::mWindow* window;
|
||||||
NSMenu* menuBar;
|
NSMenu* menuBar;
|
||||||
NSMenu* rootMenu;
|
NSMenu* rootMenu;
|
||||||
NSMenuItem* disableGatekeeperAutoRearm;
|
NSMenuItem* disableGatekeeper;
|
||||||
NSTextField* statusBar;
|
NSTextField* statusBar;
|
||||||
}
|
}
|
||||||
-(id) initWith:(hiro::mWindow&)window;
|
-(id) initWith:(hiro::mWindow&)window;
|
||||||
|
|
|
@ -128,8 +128,10 @@ auto SuperFamicom::manifest() const -> string {
|
||||||
output.append(Memory{}.type("RAM").size(0x800).content("Internal").isVolatile().text());
|
output.append(Memory{}.type("RAM").size(0x800).content("Internal").isVolatile().text());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(board.right() == "EPSONRTC" || board.right() == "SHARPRTC") {
|
if(board.right() == "EPSONRTC") {
|
||||||
output.append(Memory{}.type("RTC").size(0x10).content("Time").text());
|
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Epson").text());
|
||||||
|
} else if(board.right() == "SHARPRTC") {
|
||||||
|
output.append(Memory{}.type("RTC").size(0x10).content("Time").manufacturer("Sharp").text());
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -418,15 +420,18 @@ auto SuperFamicom::romSize() const -> uint {
|
||||||
|
|
||||||
auto SuperFamicom::programRomSize() const -> uint {
|
auto SuperFamicom::programRomSize() const -> uint {
|
||||||
if(board().beginsWith("SPC7110-")) return 0x100000;
|
if(board().beginsWith("SPC7110-")) return 0x100000;
|
||||||
|
if(board().beginsWith("EXSPC7110-")) return 0x100000;
|
||||||
return romSize();
|
return romSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::dataRomSize() const -> uint {
|
auto SuperFamicom::dataRomSize() const -> uint {
|
||||||
if(board().beginsWith("SPC7110-")) return romSize() - 0x100000;
|
if(board().beginsWith("SPC7110-")) return romSize() - 0x100000;
|
||||||
|
if(board().beginsWith("EXSPC7110-")) return 0x500000;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFamicom::expansionRomSize() const -> uint {
|
auto SuperFamicom::expansionRomSize() const -> uint {
|
||||||
|
if(board().beginsWith("EXSPC7110-")) return 0x100000;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
namespace nall { namespace Encode {
|
namespace nall { namespace Encode {
|
||||||
|
|
||||||
struct BMP {
|
struct BMP {
|
||||||
static auto create(const string& filename, const uint32_t* data, unsigned width, unsigned height, bool alpha) -> bool {
|
static auto create(const string& filename, const uint32_t* data, uint pitch, uint width, uint height, bool alpha) -> bool {
|
||||||
file fp{filename, file::mode::write};
|
file fp{filename, file::mode::write};
|
||||||
if(!fp) return false;
|
if(!fp) return false;
|
||||||
|
|
||||||
unsigned bitsPerPixel = alpha ? 32 : 24;
|
uint bitsPerPixel = alpha ? 32 : 24;
|
||||||
unsigned bytesPerPixel = bitsPerPixel / 8;
|
uint bytesPerPixel = bitsPerPixel / 8;
|
||||||
unsigned alignedWidth = width * bytesPerPixel;
|
uint alignedWidth = width * bytesPerPixel;
|
||||||
unsigned paddingLength = 0;
|
uint paddingLength = 0;
|
||||||
unsigned imageSize = alignedWidth * height;
|
uint imageSize = alignedWidth * height;
|
||||||
unsigned fileSize = 0x36 + imageSize;
|
uint fileSize = 0x36 + imageSize;
|
||||||
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||||
|
|
||||||
fp.writel(0x4d42, 2); //signature
|
fp.writel(0x4d42, 2); //signature
|
||||||
|
@ -33,8 +33,9 @@ struct BMP {
|
||||||
fp.writel(0, 4); //palette size
|
fp.writel(0, 4); //palette size
|
||||||
fp.writel(0, 4); //important color count
|
fp.writel(0, 4); //important color count
|
||||||
|
|
||||||
|
pitch >>= 2;
|
||||||
for(auto y : range(height)) {
|
for(auto y : range(height)) {
|
||||||
const uint32_t* p = data + y * width;
|
const uint32_t* p = data + y * pitch;
|
||||||
for(auto x : range(width)) fp.writel(*p++, bytesPerPixel);
|
for(auto x : range(width)) fp.writel(*p++, bytesPerPixel);
|
||||||
if(paddingLength) fp.writel(0, paddingLength);
|
if(paddingLength) fp.writel(0, paddingLength);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef NALL_MACOS_GUARD_HPP
|
||||||
|
#define NALL_MACOS_GUARD_HPP
|
||||||
|
|
||||||
|
#define Boolean CocoaBoolean
|
||||||
|
#define decimal CocoaDecimal
|
||||||
|
#define DEBUG CocoaDebug
|
||||||
|
|
||||||
|
#else
|
||||||
|
#undef NALL_MACOS_GUARD_HPP
|
||||||
|
|
||||||
|
#undef Boolean
|
||||||
|
#undef decimal
|
||||||
|
#undef DEBUG
|
||||||
|
|
||||||
|
#endif
|
|
@ -99,6 +99,7 @@ struct serializer {
|
||||||
|
|
||||||
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
|
template<typename T, uint Size> auto array(nall::array<T[Size]>& array) -> serializer& {
|
||||||
for(auto& value : array) operator()(value);
|
for(auto& value : array) operator()(value);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; }
|
template<typename T> auto operator()(T& value, typename std::enable_if<has_serialize<T>::value>::type* = 0) -> serializer& { value.serialize(*this); return *this; }
|
||||||
|
|
|
@ -18,12 +18,10 @@ using namespace ruby;
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#elif defined(DISPLAY_QUARTZ)
|
#elif defined(DISPLAY_QUARTZ)
|
||||||
#define Boolean CocoaBoolean
|
#include <nall/macos/guard.hpp>
|
||||||
#define decimal CocoaDecimal
|
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
#include <Carbon/Carbon.h>
|
#include <Carbon/Carbon.h>
|
||||||
#undef Boolean
|
#include <nall/macos/guard.hpp>
|
||||||
#undef decimal
|
|
||||||
#elif defined(DISPLAY_WINDOWS)
|
#elif defined(DISPLAY_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue