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 {
|
||||
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 License = "GPLv3";
|
||||
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 {
|
||||
if(name == "Fast PPU") 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 == "Mode") 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 {
|
||||
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/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 == "Blur Emulation") return settings.blurEmulation;
|
||||
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>();
|
||||
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>()) {
|
||||
settings.fastDSP = value.get<bool>();
|
||||
return true;
|
||||
|
|
|
@ -69,6 +69,7 @@ struct Interface : Emulator::Interface {
|
|||
struct Settings {
|
||||
bool fastPPU = false;
|
||||
bool fastPPUNoSpriteLimit = false;
|
||||
bool fastPPUHiresMode7 = false;
|
||||
bool fastDSP = false;
|
||||
|
||||
bool blurEmulation = true;
|
||||
|
|
|
@ -23,9 +23,10 @@ auto PPU::Line::render() -> void {
|
|||
}
|
||||
|
||||
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||
bool hiresMode7 = io.bgMode == 7 && settings.fastPPUHiresMode7;
|
||||
auto aboveColor = cgram[0];
|
||||
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};
|
||||
below[x] = {Source::COL, 0, belowColor};
|
||||
}
|
||||
|
@ -39,7 +40,11 @@ auto PPU::Line::render() -> void {
|
|||
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
|
||||
|
||||
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]);
|
||||
} else if(!hires) for(uint x : range(256)) {
|
||||
auto color = luma | pixel(x, above[x], below[x]);
|
||||
|
|
|
@ -25,41 +25,79 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
|||
renderWindow(self.window, self.window.aboveEnable, windowAbove);
|
||||
renderWindow(self.window, self.window.belowEnable, windowBelow);
|
||||
|
||||
for(int X : range(256)) {
|
||||
int x = !io.mode7.hflip ? X : 255 - X;
|
||||
int pixelX = originX + a * x >> 8;
|
||||
int pixelY = originY + c * x >> 8;
|
||||
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 = ppu.vram[tileAddress].byte(0);
|
||||
if(io.mode7.repeat == 3 && outOfBounds) tile = 0;
|
||||
uint8 palette = ppu.vram[paletteAddress + (tile << 6)].byte(1);
|
||||
if(io.mode7.repeat == 2 && outOfBounds) palette = 0;
|
||||
if(!settings.fastPPUHiresMode7) {
|
||||
for(int X : range(256)) {
|
||||
int x = !io.mode7.hflip ? X : 255 - X;
|
||||
int pixelX = originX + a * x >> 8;
|
||||
int pixelY = originY + c * x >> 8;
|
||||
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 || --mosaicCounter == 0) {
|
||||
mosaicCounter = 1 + io.mosaicSize;
|
||||
mosaicPalette = palette;
|
||||
mosaicPriority = priority;
|
||||
if(io.col.directColor && source == Source::BG1) {
|
||||
mosaicColor = directColor(0, palette);
|
||||
} else {
|
||||
mosaicColor = cgram[palette];
|
||||
uint priority;
|
||||
if(source == Source::BG1) {
|
||||
priority = self.priority[0];
|
||||
} else if(source == Source::BG2) {
|
||||
priority = self.priority[palette >> 7];
|
||||
palette &= 0x7f;
|
||||
}
|
||||
}
|
||||
if(!mosaicPalette) continue;
|
||||
|
||||
if(self.aboveEnable && !windowAbove[x]) plotAbove(x, source, mosaicPriority, mosaicColor);
|
||||
if(self.belowEnable && !windowBelow[x]) plotBelow(x, source, mosaicPriority, mosaicColor);
|
||||
if(!self.mosaicEnable || --mosaicCounter == 0) {
|
||||
mosaicCounter = 1 + io.mosaicSize;
|
||||
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]) plotAbove(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()) {
|
||||
latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||
latch.hires |= io.bgMode == 7 && settings.fastPPUHiresMode7;
|
||||
}
|
||||
|
||||
if(vcounter() == vdisp() && !io.displayDisable) {
|
||||
|
@ -103,6 +104,7 @@ auto PPU::load(Markup::Node node) -> bool {
|
|||
}
|
||||
|
||||
auto PPU::power(bool reset) -> void {
|
||||
//settings.fastPPUHiresMode7=false;
|
||||
create(Enter, system.cpuFrequency());
|
||||
PPUcounter::reset();
|
||||
memory::fill<uint32>(output, 512 * 480);
|
||||
|
|
|
@ -295,8 +295,8 @@ public:
|
|||
array<ObjectItem[128]> items; //32 on real hardware
|
||||
array<ObjectTile[128]> tiles; //34 on real hardware; 1024 max (128 * 64-width tiles)
|
||||
|
||||
array<Pixel[256]> above;
|
||||
array<Pixel[256]> below;
|
||||
array<Pixel[512]> above; //256 on real hardware
|
||||
array<Pixel[512]> below; //512 for hires mode 7
|
||||
|
||||
array<bool[256]> windowAbove;
|
||||
array<bool[256]> windowBelow;
|
||||
|
|
|
@ -27,6 +27,10 @@ auto InputManager::bindHotkeys() -> void {
|
|||
program->showMessage({"Selected state slot ", stateSlot});
|
||||
}));
|
||||
|
||||
hotkeys.append(InputHotkey("Capture Screenshot").onPress([] {
|
||||
presentation->captureScreenshot.doActivate();
|
||||
}));
|
||||
|
||||
hotkeys.append(InputHotkey("Fast Forward").onPress([] {
|
||||
video->setBlocking(false);
|
||||
audio->setBlocking(false);
|
||||
|
|
|
@ -139,7 +139,7 @@ Presentation::Presentation() {
|
|||
}
|
||||
loadState.append(MenuSeparator());
|
||||
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");
|
||||
speedSlowest.setText("Slowest (50%)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
|
||||
|
@ -150,8 +150,13 @@ Presentation::Presentation() {
|
|||
pauseEmulation.setText("Pause Emulation").onToggle([&] {
|
||||
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); });
|
||||
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");
|
||||
documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] {
|
||||
|
|
|
@ -74,9 +74,11 @@ struct Presentation : Window {
|
|||
MenuRadioItem speedFastest{&speedMenu};
|
||||
Group speedGroup{&speedSlowest, &speedSlow, &speedNormal, &speedFast, &speedFastest};
|
||||
MenuCheckItem pauseEmulation{&toolsMenu};
|
||||
MenuItem captureScreenshot{&toolsMenu};
|
||||
MenuSeparator toolsSeparatorB{&toolsMenu};
|
||||
MenuItem cheatEditor{&toolsMenu};
|
||||
MenuItem stateManager{&toolsMenu};
|
||||
MenuItem manifestViewer{&toolsMenu};
|
||||
Menu helpMenu{&menuBar};
|
||||
MenuItem documentation{&helpMenu};
|
||||
MenuSeparator helpSeparator{&helpMenu};
|
||||
|
|
|
@ -7,6 +7,7 @@ auto Program::load() -> void {
|
|||
Emulator::audio.reset(2, audio->frequency());
|
||||
if(emulator->load(media.id)) {
|
||||
gameQueue = {};
|
||||
captureScreenshot = false;
|
||||
if(!verified() && settingsWindow->advanced.warnOnUnverifiedGames.checked()) {
|
||||
//todo: MessageDialog crashes with GTK+; unsure the reason why this happens
|
||||
//once MessageDialog functions, add an "Always" option
|
||||
|
@ -22,9 +23,12 @@ auto Program::load() -> void {
|
|||
hackCompatibility();
|
||||
emulator->power();
|
||||
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->resetSystem.setEnabled(true);
|
||||
presentation->unloadGame.setEnabled(true);
|
||||
|
@ -35,6 +39,7 @@ auto Program::load() -> void {
|
|||
presentation->resizeViewport();
|
||||
toolsWindow->cheatEditor.loadCheats();
|
||||
toolsWindow->stateManager.loadStates();
|
||||
toolsWindow->manifestViewer.loadManifest();
|
||||
|
||||
string locations = superFamicom.location;
|
||||
if(auto location = gameBoy.location) locations.append("|", location);
|
||||
|
@ -63,12 +68,13 @@ auto Program::loadFile(string location) -> vector<uint8_t> {
|
|||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
} else {
|
||||
return file::read(location);
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::loadSuperFamicom(string location) -> void {
|
||||
auto Program::loadSuperFamicom(string location) -> bool {
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
|
@ -84,6 +90,7 @@ auto Program::loadSuperFamicom(string location) -> void {
|
|||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
rom = loadFile(location);
|
||||
}
|
||||
if(rom.size() < 0x8000) return false;
|
||||
|
||||
//assume ROM and IPS agree on whether a copier header is present
|
||||
superFamicom.patched = applyPatchIPS(rom, location);
|
||||
|
@ -130,9 +137,11 @@ auto Program::loadSuperFamicom(string location) -> void {
|
|||
memory::copy(&superFamicom.firmware[0], &rom[offset], size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::loadGameBoy(string location) -> void {
|
||||
auto Program::loadGameBoy(string location) -> bool {
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
|
@ -143,6 +152,7 @@ auto Program::loadGameBoy(string location) -> void {
|
|||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
rom = loadFile(location);
|
||||
}
|
||||
if(rom.size() < 0x4000) return false;
|
||||
|
||||
gameBoy.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||
auto heuristics = Heuristics::GameBoy(rom, location);
|
||||
|
@ -164,9 +174,10 @@ auto Program::loadGameBoy(string location) -> void {
|
|||
gameBoy.location = location;
|
||||
|
||||
gameBoy.program = rom;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::loadBSMemory(string location) -> void {
|
||||
auto Program::loadBSMemory(string location) -> bool {
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
|
@ -178,6 +189,7 @@ auto Program::loadBSMemory(string location) -> void {
|
|||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
rom = loadFile(location);
|
||||
}
|
||||
if(rom.size() < 0x8000) return false;
|
||||
|
||||
bsMemory.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||
auto heuristics = Heuristics::BSMemory(rom, location);
|
||||
|
@ -193,9 +205,10 @@ auto Program::loadBSMemory(string location) -> void {
|
|||
bsMemory.location = location;
|
||||
|
||||
bsMemory.program = rom;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::loadSufamiTurboA(string location) -> void {
|
||||
auto Program::loadSufamiTurboA(string location) -> bool {
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
|
@ -206,6 +219,7 @@ auto Program::loadSufamiTurboA(string location) -> void {
|
|||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
rom = loadFile(location);
|
||||
}
|
||||
if(rom.size() < 0x20000) return false;
|
||||
|
||||
sufamiTurboA.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
||||
|
@ -221,9 +235,10 @@ auto Program::loadSufamiTurboA(string location) -> void {
|
|||
sufamiTurboA.location = location;
|
||||
|
||||
sufamiTurboA.program = rom;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::loadSufamiTurboB(string location) -> void {
|
||||
auto Program::loadSufamiTurboB(string location) -> bool {
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
|
@ -234,6 +249,7 @@ auto Program::loadSufamiTurboB(string location) -> void {
|
|||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
rom = loadFile(location);
|
||||
}
|
||||
if(rom.size() < 0x20000) return false;
|
||||
|
||||
sufamiTurboB.patched = applyPatchIPS(rom, location) || applyPatchBPS(rom, location);
|
||||
auto heuristics = Heuristics::SufamiTurbo(rom, location);
|
||||
|
@ -249,6 +265,7 @@ auto Program::loadSufamiTurboB(string location) -> void {
|
|||
sufamiTurboB.location = location;
|
||||
|
||||
sufamiTurboB.program = rom;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::save() -> void {
|
||||
|
@ -268,7 +285,7 @@ auto Program::unload() -> void {
|
|||
toolsWindow->cheatEditor.saveCheats();
|
||||
toolsWindow->setVisible(false);
|
||||
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
|
||||
saveRecoveryState();
|
||||
saveUndoState();
|
||||
}
|
||||
emulator->unload();
|
||||
showMessage("Game unloaded");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
auto Program::hackCompatibility() -> void {
|
||||
bool fastPPU = settingsWindow->advanced.fastPPUOption.checked();
|
||||
bool fastPPUNoSpriteLimit = settingsWindow->advanced.noSpriteLimit.checked();
|
||||
bool fastPPUHiresMode7 = settingsWindow->advanced.hiresMode7.checked();
|
||||
bool fastDSP = settingsWindow->advanced.fastDSPOption.checked();
|
||||
|
||||
auto label = superFamicom.label;
|
||||
|
@ -10,6 +11,7 @@ auto Program::hackCompatibility() -> void {
|
|||
|
||||
emulator->set("Fast PPU", fastPPU);
|
||||
emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit);
|
||||
emulator->set("Fast PPU/Hires Mode 7", fastPPUHiresMode7);
|
||||
emulator->set("Fast DSP", fastDSP);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <nall/encode/bmp.hpp>
|
||||
#include <icarus/heuristics/heuristics.hpp>
|
||||
#include <icarus/heuristics/heuristics.cpp>
|
||||
#include <icarus/heuristics/super-famicom.cpp>
|
||||
|
@ -117,8 +118,9 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
}
|
||||
if(inode::exists(superFamicom.location)) {
|
||||
settings["Path/Recent/SuperFamicom"].setValue(Location::dir(superFamicom.location));
|
||||
loadSuperFamicom(superFamicom.location);
|
||||
return {id, dialog.option()};
|
||||
if(loadSuperFamicom(superFamicom.location)) {
|
||||
return {id, dialog.option()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,8 +135,9 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
}
|
||||
if(inode::exists(gameBoy.location)) {
|
||||
settings["Path/Recent/GameBoy"].setValue(Location::dir(gameBoy.location));
|
||||
loadGameBoy(gameBoy.location);
|
||||
return {id, dialog.option()};
|
||||
if(loadGameBoy(gameBoy.location)) {
|
||||
return {id, dialog.option()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,8 +152,9 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
}
|
||||
if(inode::exists(bsMemory.location)) {
|
||||
settings["Path/Recent/BSMemory"].setValue(Location::dir(bsMemory.location));
|
||||
loadBSMemory(bsMemory.location);
|
||||
return {id, dialog.option()};
|
||||
if(loadBSMemory(bsMemory.location)) {
|
||||
return {id, dialog.option()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,8 +169,9 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
}
|
||||
if(inode::exists(sufamiTurboA.location)) {
|
||||
settings["Path/Recent/SufamiTurboA"].setValue(Location::dir(sufamiTurboA.location));
|
||||
loadSufamiTurboA(sufamiTurboA.location);
|
||||
return {id, dialog.option()};
|
||||
if(loadSufamiTurboA(sufamiTurboA.location)) {
|
||||
return {id, dialog.option()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,8 +186,9 @@ auto Program::load(uint id, string name, string type, string_vector options) ->
|
|||
}
|
||||
if(inode::exists(sufamiTurboB.location)) {
|
||||
settings["Path/Recent/SufamiTurboB"].setValue(Location::dir(sufamiTurboB.location));
|
||||
loadSufamiTurboB(sufamiTurboB.location);
|
||||
return {id, dialog.option()};
|
||||
if(loadSufamiTurboB(sufamiTurboB.location)) {
|
||||
return {id, dialog.option()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(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)) {
|
||||
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};
|
||||
}
|
||||
|
||||
|
@ -62,3 +68,19 @@ auto Program::statePath() -> string {
|
|||
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->pollHotkeys();
|
||||
|
||||
if(!emulator->loaded()
|
||||
|| presentation->pauseEmulation.checked()
|
||||
|| (!focused() && settingsWindow->input.pauseEmulation.checked())
|
||||
) {
|
||||
if(paused()) {
|
||||
audio->clear();
|
||||
usleep(20 * 1000);
|
||||
return;
|
||||
|
|
|
@ -15,11 +15,11 @@ struct Program : Emulator::Platform {
|
|||
//game.cpp
|
||||
auto load() -> void;
|
||||
auto loadFile(string location) -> vector<uint8_t>;
|
||||
auto loadSuperFamicom(string location) -> void;
|
||||
auto loadGameBoy(string location) -> void;
|
||||
auto loadBSMemory(string location) -> void;
|
||||
auto loadSufamiTurboA(string location) -> void;
|
||||
auto loadSufamiTurboB(string location) -> void;
|
||||
auto loadSuperFamicom(string location) -> bool;
|
||||
auto loadGameBoy(string location) -> bool;
|
||||
auto loadBSMemory(string location) -> bool;
|
||||
auto loadSufamiTurboA(string location) -> bool;
|
||||
auto loadSufamiTurboB(string location) -> bool;
|
||||
auto save() -> void;
|
||||
auto reset() -> void;
|
||||
auto unload() -> void;
|
||||
|
@ -44,12 +44,13 @@ struct Program : Emulator::Platform {
|
|||
auto gamePath() -> string;
|
||||
auto cheatPath() -> string;
|
||||
auto statePath() -> string;
|
||||
auto screenshotPath() -> string;
|
||||
|
||||
//states.cpp
|
||||
auto managedStates() -> string_vector;
|
||||
auto loadState(string filename) -> bool;
|
||||
auto saveState(string filename) -> bool;
|
||||
auto saveRecoveryState() -> bool;
|
||||
auto saveUndoState() -> bool;
|
||||
auto removeState(string filename) -> bool;
|
||||
auto renameState(string from, string to) -> bool;
|
||||
|
||||
|
@ -76,6 +77,7 @@ struct Program : Emulator::Platform {
|
|||
auto showMessage(string text) -> void;
|
||||
auto showFrameRate(string text) -> void;
|
||||
auto updateStatus() -> void;
|
||||
auto paused() -> bool;
|
||||
auto focused() -> bool;
|
||||
|
||||
//patch.cpp
|
||||
|
@ -120,6 +122,7 @@ public:
|
|||
} sufamiTurboA, sufamiTurboB;
|
||||
|
||||
string_vector gameQueue;
|
||||
boolean captureScreenshot;
|
||||
|
||||
uint64 autoSaveTime;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ auto Program::loadState(string filename) -> bool {
|
|||
if(gamePath().endsWith("/")) {
|
||||
string location = {statePath(), filename, ".bst"};
|
||||
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
|
||||
if(filename != "quick/recovery") saveRecoveryState();
|
||||
if(filename != "quick/undo") saveUndoState();
|
||||
memory = file::read(location);
|
||||
} else {
|
||||
string location = {filename, ".bst"};
|
||||
|
@ -87,12 +87,13 @@ auto Program::saveState(string filename) -> bool {
|
|||
return showMessage({"Saved [", prefix, "]"}), true;
|
||||
}
|
||||
|
||||
auto Program::saveRecoveryState() -> bool {
|
||||
auto Program::saveUndoState() -> bool {
|
||||
auto statusTime = this->statusTime;
|
||||
auto statusMessage = this->statusMessage;
|
||||
saveState("quick/recovery");
|
||||
auto result = saveState("quick/undo");
|
||||
this->statusTime = statusTime;
|
||||
this->statusMessage = statusMessage;
|
||||
return result;
|
||||
}
|
||||
|
||||
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 {
|
||||
//exclusive mode creates its own top-level window: presentation window will not have focus
|
||||
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?"
|
||||
).setParent(*settingsWindow).question() == "Yes") {
|
||||
program->save();
|
||||
program->saveRecoveryState();
|
||||
program->saveUndoState();
|
||||
settings["Crashed"].setValue(true);
|
||||
settings.save();
|
||||
program->updateVideoDriver();
|
||||
|
@ -34,7 +34,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
"Do you wish to proceed with the audio driver change now anyway?"
|
||||
).setParent(*settingsWindow).question() == "Yes") {
|
||||
program->save();
|
||||
program->saveRecoveryState();
|
||||
program->saveUndoState();
|
||||
settings["Crashed"].setValue(true);
|
||||
settings.save();
|
||||
program->updateAudioDriver();
|
||||
|
@ -53,7 +53,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
"Do you wish to proceed with the input driver change now anyway?"
|
||||
).setParent(*settingsWindow).question() == "Yes") {
|
||||
program->save();
|
||||
program->saveRecoveryState();
|
||||
program->saveUndoState();
|
||||
settings["Crashed"].setValue(true);
|
||||
settings.save();
|
||||
program->updateInputDriver();
|
||||
|
@ -86,23 +86,29 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Emulator/Hack/FastPPU"].setValue(fastPPUOption.checked());
|
||||
if(!fastPPUOption.checked()) {
|
||||
noSpriteLimit.setEnabled(false).setChecked(false).doToggle();
|
||||
hiresMode7.setEnabled(false).setChecked(false).doToggle();
|
||||
} else {
|
||||
noSpriteLimit.setEnabled(true);
|
||||
hiresMode7.setEnabled(true);
|
||||
}
|
||||
}).doToggle();
|
||||
noSpriteLimit.setText("No sprite limit").setChecked(settings["Emulator/Hack/FastPPU/NoSpriteLimit"].boolean()).onToggle([&] {
|
||||
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([&] {
|
||||
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
|
||||
});
|
||||
superFXLabel.setText("SuperFX Clock Speed:");
|
||||
superFXLabel.setText("SuperFX clock speed:");
|
||||
superFXValue.setAlignment(0.5);
|
||||
superFXClock.setLength(71).setPosition((settings["Emulator/Hack/FastSuperFX"].natural() - 100) / 10).onChange([&] {
|
||||
settings["Emulator/Hack/FastSuperFX"].setValue({superFXClock.position() * 10 + 100, "%"});
|
||||
superFXValue.setText(settings["Emulator/Hack/FastSuperFX"].text());
|
||||
}).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 {
|
||||
|
|
|
@ -26,7 +26,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
});
|
||||
|
||||
effectsLabel.setFont(Font().setBold()).setText("Effects");
|
||||
skewLabel.setText("Skew:");
|
||||
skewLabel.setAlignment(1.0).setText("Skew:");
|
||||
skewValue.setAlignment(0.5);
|
||||
skewSlider.setLength(10001).setPosition(settings["Audio/Skew"].integer() + 5000).onChange([&] {
|
||||
string value = {skewSlider.position() > 5000 ? "+" : "", (int)skewSlider.position() - 5000};
|
||||
|
@ -34,7 +34,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
skewValue.setText(value);
|
||||
program->updateAudioFrequency();
|
||||
}).doChange();
|
||||
volumeLabel.setText("Volume:");
|
||||
volumeLabel.setAlignment(1.0).setText("Volume:");
|
||||
volumeValue.setAlignment(0.5);
|
||||
volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] {
|
||||
string value = {volumeSlider.position(), "%"};
|
||||
|
@ -42,7 +42,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
volumeValue.setText(value);
|
||||
program->updateAudioEffects();
|
||||
}).doChange();
|
||||
balanceLabel.setText("Balance:");
|
||||
balanceLabel.setAlignment(1.0).setText("Balance:");
|
||||
balanceValue.setAlignment(0.5);
|
||||
balanceSlider.setLength(101).setPosition(settings["Audio/Balance"].natural()).onChange([&] {
|
||||
string value = {balanceSlider.position(), "%"};
|
||||
|
|
|
@ -3,14 +3,14 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
setText("Input");
|
||||
|
||||
layout.setMargin(5);
|
||||
defocusLabel.setText("When Focus is Lost:");
|
||||
pauseEmulation.setText("Pause Emulation").onActivate([&] {
|
||||
defocusLabel.setText("When focus is lost:");
|
||||
pauseEmulation.setText("Pause emulation").onActivate([&] {
|
||||
settings["Input/Defocus"].setValue("Pause");
|
||||
});
|
||||
blockInput.setText("Block Input").onActivate([&] {
|
||||
blockInput.setText("Block input").onActivate([&] {
|
||||
settings["Input/Defocus"].setValue("Block");
|
||||
});
|
||||
allowInput.setText("Allow Input").onActivate([&] {
|
||||
allowInput.setText("Allow input").onActivate([&] {
|
||||
settings["Input/Defocus"].setValue("Allow");
|
||||
});
|
||||
if(settings["Input/Defocus"].text() == "Pause") pauseEmulation.setChecked();
|
||||
|
|
|
@ -3,7 +3,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
setText("Paths");
|
||||
|
||||
layout.setMargin(5);
|
||||
gamesLabel.setText("Games:");
|
||||
|
||||
gamesLabel.setAlignment(1.0).setText("Games:");
|
||||
gamesPath.setEditable(false);
|
||||
gamesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
|
@ -15,7 +16,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Games"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
patchesLabel.setText("Patches:");
|
||||
|
||||
patchesLabel.setAlignment(1.0).setText("Patches:");
|
||||
patchesPath.setEditable(false);
|
||||
patchesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
|
@ -27,7 +29,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Patches"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
savesLabel.setText("Saves:");
|
||||
|
||||
savesLabel.setAlignment(1.0).setText("Saves:");
|
||||
savesPath.setEditable(false);
|
||||
savesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
|
@ -39,7 +42,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Saves"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
cheatsLabel.setText("Cheats:");
|
||||
|
||||
cheatsLabel.setAlignment(1.0).setText("Cheats:");
|
||||
cheatsPath.setEditable(false);
|
||||
cheatsAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
|
@ -51,7 +55,8 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
settings["Path/Cheats"].setValue("");
|
||||
refreshPaths();
|
||||
});
|
||||
statesLabel.setText("States:");
|
||||
|
||||
statesLabel.setAlignment(1.0).setText("States:");
|
||||
statesPath.setEditable(false);
|
||||
statesAssign.setText("Assign ...").onActivate([&] {
|
||||
if(auto location = BrowserDialog().setParent(*settingsWindow).selectFolder()) {
|
||||
|
@ -64,6 +69,19 @@ PathSettings::PathSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
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();
|
||||
}
|
||||
|
||||
|
@ -93,4 +111,9 @@ auto PathSettings::refreshPaths() -> void {
|
|||
} else {
|
||||
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/Cheats", "");
|
||||
set("Path/States", "");
|
||||
set("Path/Screenshots", "");
|
||||
set("Path/Recent/SuperFamicom", Path::user());
|
||||
set("Path/Recent/GameBoy", Path::user());
|
||||
set("Path/Recent/BSMemory", Path::user());
|
||||
|
@ -66,6 +67,7 @@ Settings::Settings() {
|
|||
set("Emulator/AutoLoadStateOnLoad", false);
|
||||
set("Emulator/Hack/FastPPU", true);
|
||||
set("Emulator/Hack/FastPPU/NoSpriteLimit", false);
|
||||
set("Emulator/Hack/FastPPU/HiresMode7", false);
|
||||
set("Emulator/Hack/FastDSP", true);
|
||||
set("Emulator/Hack/FastSuperFX", "100%");
|
||||
|
||||
|
|
|
@ -123,30 +123,35 @@ struct PathSettings : TabFrameItem {
|
|||
public:
|
||||
VerticalLayout layout{this};
|
||||
HorizontalLayout gamesLayout{&layout, Size{~0, 0}};
|
||||
Label gamesLabel{&gamesLayout, Size{55, 0}};
|
||||
Label gamesLabel{&gamesLayout, Size{80, 0}};
|
||||
LineEdit gamesPath{&gamesLayout, Size{~0, 0}};
|
||||
Button gamesAssign{&gamesLayout, Size{80, 0}};
|
||||
Button gamesReset{&gamesLayout, Size{80, 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}};
|
||||
Button patchesAssign{&patchesLayout, Size{80, 0}};
|
||||
Button patchesReset{&patchesLayout, Size{80, 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}};
|
||||
Button savesAssign{&savesLayout, Size{80, 0}};
|
||||
Button savesReset{&savesLayout, Size{80, 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}};
|
||||
Button cheatsAssign{&cheatsLayout, Size{80, 0}};
|
||||
Button cheatsReset{&cheatsLayout, Size{80, 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}};
|
||||
Button statesAssign{&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 {
|
||||
|
@ -175,6 +180,7 @@ public:
|
|||
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
|
||||
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
|
||||
CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}};
|
||||
CheckLabel hiresMode7{&fastPPULayout, Size{0, 0}};
|
||||
CheckLabel fastDSPOption{&layout, Size{~0, 0}};
|
||||
HorizontalLayout superFXLayout{&layout, Size{~0, 0}};
|
||||
Label superFXLabel{&superFXLayout, Size{0, 0}};
|
||||
|
|
|
@ -5,7 +5,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
layout.setMargin(5);
|
||||
|
||||
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
|
||||
luminanceLabel.setText("Luminance:");
|
||||
luminanceLabel.setAlignment(1.0).setText("Luminance:");
|
||||
luminanceValue.setAlignment(0.5);
|
||||
luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] {
|
||||
string value = {luminanceSlider.position(), "%"};
|
||||
|
@ -13,7 +13,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
luminanceValue.setText(value);
|
||||
program->updateVideoPalette();
|
||||
}).doChange();
|
||||
saturationLabel.setText("Saturation:");
|
||||
saturationLabel.setAlignment(1.0).setText("Saturation:");
|
||||
saturationValue.setAlignment(0.5);
|
||||
saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] {
|
||||
string value = {saturationSlider.position(), "%"};
|
||||
|
@ -21,7 +21,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
saturationValue.setText(value);
|
||||
program->updateVideoPalette();
|
||||
}).doChange();
|
||||
gammaLabel.setText("Gamma:");
|
||||
gammaLabel.setAlignment(1.0).setText("Gamma:");
|
||||
gammaValue.setAlignment(0.5);
|
||||
gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] {
|
||||
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 "cheat-editor.cpp"
|
||||
#include "state-manager.cpp"
|
||||
#include "manifest-viewer.cpp"
|
||||
unique_pointer<CheatDatabase> cheatDatabase;
|
||||
unique_pointer<CheatWindow> cheatWindow;
|
||||
unique_pointer<StateWindow> stateWindow;
|
||||
|
|
|
@ -107,6 +107,18 @@ public:
|
|||
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 {
|
||||
ToolsWindow();
|
||||
auto setVisible(bool visible = true) -> ToolsWindow&;
|
||||
|
@ -117,6 +129,7 @@ public:
|
|||
TabFrame panel{&layout, Size{~0, ~0}};
|
||||
CheatEditor cheatEditor{&panel};
|
||||
StateManager stateManager{&panel};
|
||||
ManifestViewer manifestViewer{&panel};
|
||||
};
|
||||
|
||||
extern unique_pointer<CheatDatabase> cheatDatabase;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#define decimal decimal_cocoa
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#undef decimal
|
||||
#include <nall/macos/guard.hpp>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <nall/macos/guard.hpp>
|
||||
|
||||
#include <nall/run.hpp>
|
||||
|
|
|
@ -156,12 +156,12 @@
|
|||
| kAuthorizationFlagExtendRights, nullptr);
|
||||
if(status == errAuthorizationSuccess) {
|
||||
{ char program[] = "/usr/sbin/spctl";
|
||||
char arguments[] = {"--master-disable", nullptr};
|
||||
char* arguments[] = {"--master-disable", nullptr};
|
||||
FILE* pipe = nullptr;
|
||||
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
||||
}
|
||||
{ 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;
|
||||
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
hiro::mWindow* window;
|
||||
NSMenu* menuBar;
|
||||
NSMenu* rootMenu;
|
||||
NSMenuItem* disableGatekeeperAutoRearm;
|
||||
NSMenuItem* disableGatekeeper;
|
||||
NSTextField* statusBar;
|
||||
}
|
||||
-(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());
|
||||
}
|
||||
|
||||
if(board.right() == "EPSONRTC" || board.right() == "SHARPRTC") {
|
||||
output.append(Memory{}.type("RTC").size(0x10).content("Time").text());
|
||||
if(board.right() == "EPSONRTC") {
|
||||
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;
|
||||
|
@ -418,15 +420,18 @@ auto SuperFamicom::romSize() const -> uint {
|
|||
|
||||
auto SuperFamicom::programRomSize() const -> uint {
|
||||
if(board().beginsWith("SPC7110-")) return 0x100000;
|
||||
if(board().beginsWith("EXSPC7110-")) return 0x100000;
|
||||
return romSize();
|
||||
}
|
||||
|
||||
auto SuperFamicom::dataRomSize() const -> uint {
|
||||
if(board().beginsWith("SPC7110-")) return romSize() - 0x100000;
|
||||
if(board().beginsWith("EXSPC7110-")) return 0x500000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto SuperFamicom::expansionRomSize() const -> uint {
|
||||
if(board().beginsWith("EXSPC7110-")) return 0x100000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
namespace nall { namespace Encode {
|
||||
|
||||
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};
|
||||
if(!fp) return false;
|
||||
|
||||
unsigned bitsPerPixel = alpha ? 32 : 24;
|
||||
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||
unsigned alignedWidth = width * bytesPerPixel;
|
||||
unsigned paddingLength = 0;
|
||||
unsigned imageSize = alignedWidth * height;
|
||||
unsigned fileSize = 0x36 + imageSize;
|
||||
uint bitsPerPixel = alpha ? 32 : 24;
|
||||
uint bytesPerPixel = bitsPerPixel / 8;
|
||||
uint alignedWidth = width * bytesPerPixel;
|
||||
uint paddingLength = 0;
|
||||
uint imageSize = alignedWidth * height;
|
||||
uint fileSize = 0x36 + imageSize;
|
||||
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||
|
||||
fp.writel(0x4d42, 2); //signature
|
||||
|
@ -33,8 +33,9 @@ struct BMP {
|
|||
fp.writel(0, 4); //palette size
|
||||
fp.writel(0, 4); //important color count
|
||||
|
||||
pitch >>= 2;
|
||||
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);
|
||||
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& {
|
||||
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; }
|
||||
|
|
|
@ -18,12 +18,10 @@ using namespace ruby;
|
|||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#elif defined(DISPLAY_QUARTZ)
|
||||
#define Boolean CocoaBoolean
|
||||
#define decimal CocoaDecimal
|
||||
#include <nall/macos/guard.hpp>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#undef Boolean
|
||||
#undef decimal
|
||||
#include <nall/macos/guard.hpp>
|
||||
#elif defined(DISPLAY_WINDOWS)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue