diff --git a/README.md b/README.md index 78f737cd..41dc6c09 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,99 @@ -bsnes-hd -======== -bsnes-hd (formally "HD Mode 7 mod, for bsnes") is a fork of bsnes (currently 107r3) that adds HD video features, such as: -- HD Mode 7, rendering the rotated or pseudo perspective backgrounds at higher resolutions -- Widescreen Mode 7, as well as widescreen for other backgrounds and sprites with significant limitations -- more to come -Further documentation coming soon +# bsnes-hd + + + +## What is it? + +bsnes-hd (formally "*HD Mode 7 mod, for bsnes*") is a fork of bsnes (currently 107r3) that adds HD video features, such as: + +### HD Mode 7 + +Rendering the rotated, scaled or pseudo perspective backgrounds at higher resolutions. This does not involve new custom imagery or upscaling algorithms. It is a higher resolution version of the process the SNES uses. + +### Widescreen + +Mostly for Mode 7 scenes, but also works for some other scenes/games here and there. Some setting tweaking required... + +### more to come + +For example higher color depth and interpolation for the colors blending that help the sense of depth. + +### plus some none-HD related features + +like the ability to disable background layers, sprites and window effects for screenshots for wallpapers or soft crop to zoom in, leaving maps or static art off the sides of the screen. + + + +## Settings + +all in Settings / Emulator + +### Scale + +The resolution Mode 7 is rendered at. Higher setting drastically increase CPU usage. "*disable*" uses the classic Mode 7 code, disabling some of the following features. + +### Perspective correction + +Whether and how pseudo 3D perspective are optimized, avoiding limitations of SNES integer math. +- **off**: disable correction +- **on**: enable correction +- **auto**(default): enable correction, but try to detect when it breaks the image and automatically disable it +- **wide**(default)/**medium**/**narrow**: the distance between the lines used for the interpolation (and also for the detection, if enabled). Use "*wide*" unless it causes issues. Only currently known game that requires this is "*Tales of Phantasia*", requiring "*narrow*". + +### Supersampling + +Allows basing every resulting pixel on the average of multiple pixels. At 1x scale it allows using the higher precision at the original resolution, e.g. for use with CRT-shaders. At higher resolutions it is basically expensive anti-aliasing. Keep in mind that to estimate the performance impact you have to multiply(!) this value with the "*scale*" value. + +### Keep Mosaics non-HD + +When unchecked mosaic effects are ignored on Mode 7 backgrounds. When checked the affected lines fall back to classic Mode 7. + +### Widescreen + +Enables experimental widescreen (works best for Mode 7 scenes, rest is hit and miss) and sets its AR. The actual AR are slightly wider than the selection, use some "*soft crop*" "*left*" and "*right*" for the ideal fullscreen experience. "*HDcrop*" at 5x scale results in a width of 1920, which (combined with "*soft crop*" "*top*" and "*bottom*" at 10, output/center and shader/none) results in perfect 1080p fullscreen. + +### BG1/BG2/BG2/BG4 + +Settings for the background layers: +- **off**: no widescreen (e.g. for HUDs) +- **on**: widescreen enabled +- **< xy**/**> xy**: widescreen enabled above/below a certain scanline (for backgrounds that contain HUDs and backgrounds). +- **crop**: do not draw the left- and right-most 8 pixels (next to the widescreen areas) of the background (for backgrounds blanking some edge pixels, leaving black lines in widescreen, e.g. "*Top Gear 2*"). +- **cAuto**: same as "*crop*", except for 2 differences. Only black pixel are not rendered. And lines of black pixel starting in the left crop area are removed entirely, e.g. for "*Super Mario Kart*". +- **dis.**: Disable the background entirely, e.g. to remove HUDs for screenshots for wallpapers. + +### Sprites + +- **normal**: Sprites are rendered if they are at least partially in the classic content area. So they can only partially reach into the widescreen areas. +- **unsafe**: Sprites are rendered, even if they are entirely in a widescreen area. Works fine for a view games, like "*Super Mario World*", causes artifacts in many. +- **disab.**: Disable sprites entirely, e.g. to remove characters or HUD elements for screenshots for wallpapers. + +### Ignore window + +- **none**: Apply window effects normally. Widescreen pixels are treated like the nearest non-widescreen pixel. +- **outside**: "*outside*" window effects are ignored, i.e. all pixels are treated like they are at the "*fallback x-coordinate*" for this purpose. This allows Mode 7 widescreen in "*Final Fantasy III*" +- **outside&alw**: Same as outside, but also for "*always*" window effects. +- **all**: Same as outside, but for all window effects. Can for example be used to remove the shadow and meter from "*F-Zero*" for screenshots for wallpapers (requires a "*fallback x-coordinate*" other than 128). + +### Fallback x-coordinate + +The x-coordinate used as fallback for "*ignore window*" + +### Soft crop + +Allows widths at the four edges of the image to be declared uninteresting. So when sizing and placing the image, especially in fullscreen, they can overflow the screen, allowing the significant parts of the image to be larger. So to focus on the top part of "*Super Mario Kart*" set "*bottom*" to 130 and "*top*" to 0. Further tweaking based on your monitor AR and liking may be required. +- **none**: disable soft crop +- **center**: enable soft crop for output/center +- **scale**: enable soft crop for output/center and output/scale + +### Top/Bottom/Left/Right + +The soft crop widths for all four edges. + + + +## Help Wanted + +### Converting C++ Rendering Code to Shaders for Optimization + +Currently all the HD rendering is running on the CPU cores. This will have to be moved to the GPU to achieve usable performance. I'd really appreciate any help with that, even if it's just to get started. Please open an issue if you want to contribute, or contact me on Reddit. diff --git a/bsnes/obj/DO_NOT_DELETE_THIS_FOLDER b/bsnes/obj/DO_NOT_DELETE_THIS_FOLDER new file mode 100644 index 00000000..37081481 --- /dev/null +++ b/bsnes/obj/DO_NOT_DELETE_THIS_FOLDER @@ -0,0 +1 @@ +DO_NOT_DELETE_THIS_FOLDER \ No newline at end of file diff --git a/bsnes/out/DO_NOT_DELETE_THIS_FOLDER b/bsnes/out/DO_NOT_DELETE_THIS_FOLDER new file mode 100644 index 00000000..37081481 --- /dev/null +++ b/bsnes/out/DO_NOT_DELETE_THIS_FOLDER @@ -0,0 +1 @@ +DO_NOT_DELETE_THIS_FOLDER \ No newline at end of file diff --git a/bsnes/sfc/interface/configuration.cpp b/bsnes/sfc/interface/configuration.cpp index 7e4ef70f..4b340849 100644 --- a/bsnes/sfc/interface/configuration.cpp +++ b/bsnes/sfc/interface/configuration.cpp @@ -19,14 +19,20 @@ auto Configuration::process(Markup::Node document, bool load) -> void { bind(boolean, "Hacks/PPU/Fast", hacks.ppu.fast); bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit); bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale); - bind(boolean, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective); + bind(natural, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective); bind(natural, "Hacks/PPU/Mode7/Widescreen", hacks.ppu.mode7.widescreen); bind(natural, "Hacks/PPU/Mode7/Wsbg1", hacks.ppu.mode7.wsbg1); bind(natural, "Hacks/PPU/Mode7/Wsbg2", hacks.ppu.mode7.wsbg2); bind(natural, "Hacks/PPU/Mode7/Wsbg3", hacks.ppu.mode7.wsbg3); bind(natural, "Hacks/PPU/Mode7/Wsbg4", hacks.ppu.mode7.wsbg4); - bind(boolean, "Hacks/PPU/Mode7/Wsobj", hacks.ppu.mode7.wsobj); - bind(boolean, "Hacks/PPU/Mode7/Igwin", hacks.ppu.mode7.igwin); + bind(natural, "Hacks/PPU/Mode7/Wsobj", hacks.ppu.mode7.wsobj); + bind(natural, "Hacks/PPU/Mode7/Igwin", hacks.ppu.mode7.igwin); + bind(natural, "Hacks/PPU/Mode7/Igwinx", hacks.ppu.mode7.igwinx); + bind(natural, "Hacks/PPU/Mode7/UnintrMode", hacks.ppu.mode7.unintrMode); + bind(natural, "Hacks/PPU/Mode7/UnintrTop", hacks.ppu.mode7.unintrTop); + bind(natural, "Hacks/PPU/Mode7/UnintrBottom", hacks.ppu.mode7.unintrBottom); + bind(natural, "Hacks/PPU/Mode7/UnintrLeft", hacks.ppu.mode7.unintrLeft); + bind(natural, "Hacks/PPU/Mode7/UnintrRight", hacks.ppu.mode7.unintrRight); bind(natural, "Hacks/PPU/Mode7/Supersample", hacks.ppu.mode7.supersample); bind(boolean, "Hacks/PPU/Mode7/Mosaic", hacks.ppu.mode7.mosaic); bind(boolean, "Hacks/DSP/Fast", hacks.dsp.fast); diff --git a/bsnes/sfc/interface/configuration.hpp b/bsnes/sfc/interface/configuration.hpp index bc70f542..df55c591 100644 --- a/bsnes/sfc/interface/configuration.hpp +++ b/bsnes/sfc/interface/configuration.hpp @@ -30,14 +30,20 @@ struct Configuration { bool noSpriteLimit = true; struct Mode7 { uint scale = 2; - bool perspective = true; + uint perspective = 1; uint widescreen = 72; uint wsbg1 = 1; uint wsbg2 = 1; uint wsbg3 = 1; uint wsbg4 = 1; - bool wsobj = false; - bool igwin = false; + uint wsobj = 0; + uint igwin = 0; + uint igwinx = 128; + uint unintrMode = 1; + uint unintrTop = 10; + uint unintrBottom = 10; + uint unintrLeft = 20; + uint unintrRight = 20; uint supersample = 1; bool mosaic = false; } mode7; diff --git a/bsnes/sfc/ppu-fast/background.cpp b/bsnes/sfc/ppu-fast/background.cpp index 715612f8..d90a3ae8 100644 --- a/bsnes/sfc/ppu-fast/background.cpp +++ b/bsnes/sfc/ppu-fast/background.cpp @@ -3,6 +3,23 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source) if(self.tileMode == TileMode::Mode7) return renderMode7(self, source); if(self.tileMode == TileMode::Inactive) return; + uint wsConf = ppufast.wsbg(source); + if(wsConf == 14) { + return; + } + bool autoCrop = false; + int ws = (int)ppufast.widescreen(); + if(ws > 0) { + if(wsConf == 12) { + ws = -8; + } else if(wsConf == 13) { + ws = 0; + autoCrop = true; + } else if(wsConf == 0 || (wsConf != 1 && (((wsConf % 2) != 0) == (y < (((int)(wsConf / 2)) * 40))))) { + ws = 0; + } + } + array windowAbove; array windowBelow; renderWindow(self.window, self.window.aboveEnable, windowAbove); @@ -37,14 +54,7 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source) uint mosaicPriority = 0; uint mosaicColor = 0; - int ws = (int)ppufast.widescreen(); - if(ws > 0) { - uint wsConf = ppufast.wsbg(source); - if(wsConf == 0 || (wsConf != 1 && (((wsConf % 2) != 0) == (y < (((int)(wsConf / 2)) * 40))))) { - ws = 0; - } - } - + bool lastCropped = false; int x = 0 - (hscroll & 7) - ws; while(x < (width + ws)) { uint hoffset = x + hscroll; @@ -102,7 +112,8 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source) mosaicColor = cgram[paletteIndex + mosaicPalette]; } } - if(!mosaicPalette) continue; + if(!mosaicPalette) { lastCropped = false; continue; }; + if(autoCrop && (lastCropped || x < 8 || x > 255-8) && mosaicColor == 0) { lastCropped = true; continue; } else { lastCropped = false; } if(!hires) { if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotAbove(x, source, mosaicPriority, mosaicColor); diff --git a/bsnes/sfc/ppu-fast/line.cpp b/bsnes/sfc/ppu-fast/line.cpp index f7c627c9..175c5fee 100644 --- a/bsnes/sfc/ppu-fast/line.cpp +++ b/bsnes/sfc/ppu-fast/line.cpp @@ -2,12 +2,19 @@ uint PPUfast::Line::start = 0; uint PPUfast::Line::count = 0; auto PPUfast::Line::flush() -> void { - if(ppufast.hdPerspective()) { + uint perspCorMode = ppufast.hdPerspective(); + if(perspCorMode > 0) { #define isLineMode7(l) (l.io.bg1.tileMode == TileMode::Mode7 \ && !l.io.displayDisable && (l.io.bg1.aboveEnable || l.io.bg1.belowEnable)) ppufast.ind = 0; bool state = false; uint y; + int offsPart = 8; + if(perspCorMode == 2 || perspCorMode == 5) { + offsPart = 6; + } else if(perspCorMode == 3 || perspCorMode == 6) { + offsPart = 4; + } for(y = 0; y < Line::count; y++) { if(state != isLineMode7(ppufast.lines[Line::start + y])) { state = !state; @@ -15,7 +22,7 @@ auto PPUfast::Line::flush() -> void { ppufast.starts[ppufast.ind] = ppufast.lines[Line::start + y].y; } else { ppufast.ends[ppufast.ind] = ppufast.lines[Line::start + y].y - 1; - int offs = (ppufast.ends[ppufast.ind] - ppufast.starts[ppufast.ind]) / 8; + int offs = (ppufast.ends[ppufast.ind] - ppufast.starts[ppufast.ind]) / offsPart; ppufast.startsp[ppufast.ind] = ppufast.starts[ppufast.ind] + offs; ppufast.endsp[ppufast.ind] = ppufast.ends[ppufast.ind] - offs; ppufast.ind++; @@ -25,12 +32,79 @@ auto PPUfast::Line::flush() -> void { #undef isLineMode7 if(state) { ppufast.ends[ppufast.ind] = ppufast.lines[Line::start + y].y - 1; - int offs = (ppufast.ends[ppufast.ind] - ppufast.starts[ppufast.ind]) / 8; + int offs = (ppufast.ends[ppufast.ind] - ppufast.starts[ppufast.ind]) / offsPart; ppufast.startsp[ppufast.ind] = ppufast.starts[ppufast.ind] + offs; ppufast.endsp[ppufast.ind] = ppufast.ends[ppufast.ind] - offs; ppufast.ind++; } - } + + if(perspCorMode < 4) { + for(int i = 0; i < ppufast.ind; i++) { + int la = -1; + int lb = -1; + int lc = -1; + int ld = -1; + bool abd= false; + bool bbd= false; + bool cbd= false; + bool dbd= false; + bool ab= false; + bool bb= false; + bool cb= false; + bool db= false; + for(y = ppufast.startsp[i]; y <= ppufast.endsp[i]; y++) { + int a = ((int)((int16)(ppufast.lines[y].io.mode7.a))); + int b = ((int)((int16)(ppufast.lines[y].io.mode7.b))); + int c = ((int)((int16)(ppufast.lines[y].io.mode7.c))); + int d = ((int)((int16)(ppufast.lines[y].io.mode7.d))); + if(la > 0 && a > 0 && a != la) { + if(!abd) { + abd = true; + ab = a > la; + } else if(ab != a > la) { + ppufast.startsp[i] = -1; + ppufast.endsp[i] = -1; + break; + } + } + if(lb > 0 && b > 0 && b != lb) { + if(!bbd) { + bbd = true; + bb = b > lb; + } else if(bb != b > lb) { + ppufast.startsp[i] = -1; + ppufast.endsp[i] = -1; + break; + } + } + if(lc > 0 && c > 0 && c != lc) { + if(!cbd) { + cbd = true; + cb = c > lc; + } else if(cb != c > lc) { + ppufast.startsp[i] = -1; + ppufast.endsp[i] = -1; + break; + } + } + if(ld > 0 && d > 0 && d != ld) { + if(!dbd) { + dbd = true; + db = d > ld; + } else if(db != d > ld) { + ppufast.startsp[i] = -1; + ppufast.endsp[i] = -1; + break; + } + } + la = a; + lb = b; + lc = c; + ld = d; + } + } + } + } if(Line::count) { #pragma omp parallel for if(Line::count >= 8) @@ -70,10 +144,11 @@ auto PPUfast::Line::render() -> void { } renderBackground(io.bg1, Source::BG1); - renderBackground(io.bg2, Source::BG2); + if(!io.extbg) renderBackground(io.bg2, Source::BG2); renderBackground(io.bg3, Source::BG3); renderBackground(io.bg4, Source::BG4); renderObject(io.obj); + if(io.extbg) renderBackground(io.bg2, Source::BG2); renderWindow(io.col.window, io.col.window.aboveMask, windowAbove); renderWindow(io.col.window, io.col.window.belowMask, windowBelow); diff --git a/bsnes/sfc/ppu-fast/mode7hd.cpp b/bsnes/sfc/ppu-fast/mode7hd.cpp index 754d1853..f09c7707 100644 --- a/bsnes/sfc/ppu-fast/mode7hd.cpp +++ b/bsnes/sfc/ppu-fast/mode7hd.cpp @@ -3,7 +3,9 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) -> const uint sampScale = ppufast.hdSupersample(); const uint scale = ppufast.hdScale() * sampScale; - uint sampTmp[(256+2*ppufast.widescreen()) * 4 * scale/sampScale] = {}; + int sampSize = sampScale < 2 ? 0 : (256+2*ppufast.widescreen()) * 4 * scale/sampScale; + uint *sampTmp = new uint[sampSize]; + memory::fill(sampTmp, sampSize); Pixel pixel; Pixel* above = &this->above[0]; @@ -15,7 +17,7 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) -> #define isLineMode7(n) (ppufast.lines[n].io.bg1.tileMode == TileMode::Mode7 && !ppufast.lines[n].io.displayDisable && ( \ (ppufast.lines[n].io.bg1.aboveEnable || ppufast.lines[n].io.bg1.belowEnable) \ )) - if(ppufast.hdPerspective()) { + if(ppufast.hdPerspective() > 0) { for(int i = 0; i < ppufast.ind; i++) { if(y >= ppufast.starts[i] && y <= ppufast.ends[i]) { y_a = ppufast.startsp[i]; @@ -145,6 +147,7 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) -> } } } + delete[] sampTmp; } //interpolation and extrapolation diff --git a/bsnes/sfc/ppu-fast/object.cpp b/bsnes/sfc/ppu-fast/object.cpp index da93f80f..f5e5e407 100644 --- a/bsnes/sfc/ppu-fast/object.cpp +++ b/bsnes/sfc/ppu-fast/object.cpp @@ -1,5 +1,6 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void { if(!self.aboveEnable && !self.belowEnable) return; + if(ppufast.wsobj() == 2) return; array windowAbove; array windowBelow; @@ -28,7 +29,7 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void { item.height = heights[self.baseSize]; } - if(!ppufast.wsobj() + if(ppufast.wsobj() == 0 && object.x > 256 && object.x + item.width - 1 < 512) continue; uint height = item.height >> self.interlace; if((y >= object.y && y < object.y + height) diff --git a/bsnes/sfc/ppu-fast/ppu.cpp b/bsnes/sfc/ppu-fast/ppu.cpp index 50268438..f7444bd0 100644 --- a/bsnes/sfc/ppu-fast/ppu.cpp +++ b/bsnes/sfc/ppu-fast/ppu.cpp @@ -21,7 +21,7 @@ auto PPUfast::hires() const -> bool { return latch.hires; } auto PPUfast::hd() const -> bool { return latch.hd; } auto PPUfast::ss() const -> bool { return latch.ss; } auto PPUfast::hdScale() const -> uint { return configuration.hacks.ppu.mode7.scale; } -auto PPUfast::hdPerspective() const -> bool { return configuration.hacks.ppu.mode7.perspective; } +auto PPUfast::hdPerspective() const -> uint { return configuration.hacks.ppu.mode7.perspective; } auto PPUfast::hdSupersample() const -> uint { return configuration.hacks.ppu.mode7.supersample; } auto PPUfast::hdMosaic() const -> bool { return configuration.hacks.ppu.mode7.mosaic; } auto PPUfast::widescreen() const -> uint { return configuration.hacks.ppu.mode7.widescreen; } // 64 / 0 #widescreenextension @@ -32,11 +32,12 @@ auto PPUfast::wsbg(uint bg) const -> uint { if (bg == Source::BG4) return configuration.hacks.ppu.mode7.wsbg4; return 0; } -auto PPUfast::wsobj() const -> bool { return configuration.hacks.ppu.mode7.wsobj; } +auto PPUfast::wsobj() const -> uint { return configuration.hacks.ppu.mode7.wsobj; } auto PPUfast::winXad(uint x, bool bel) const -> uint { - return (configuration.hacks.ppu.mode7.igwin - && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 2)) - ? 127 : (x < 0 ? 0 : (x > 255 ? 255 : x)); + return (configuration.hacks.ppu.mode7.igwin != 0 && (configuration.hacks.ppu.mode7.igwin >= 3 + || configuration.hacks.ppu.mode7.igwin >= 2 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 0) + || configuration.hacks.ppu.mode7.igwin >= 1 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 2))) + ? configuration.hacks.ppu.mode7.igwinx : (x < 0 ? 0 : (x > 255 ? 255 : x)); } PPUfast::PPUfast() { diff --git a/bsnes/sfc/ppu-fast/ppu.hpp b/bsnes/sfc/ppu-fast/ppu.hpp index 87d0f5be..f7e4d746 100644 --- a/bsnes/sfc/ppu-fast/ppu.hpp +++ b/bsnes/sfc/ppu-fast/ppu.hpp @@ -13,12 +13,12 @@ struct PPUfast : Thread, PPUcounter { alwaysinline auto hd() const -> bool; alwaysinline auto ss() const -> bool; alwaysinline auto hdScale() const -> uint; - alwaysinline auto hdPerspective() const -> bool; + alwaysinline auto hdPerspective() const -> uint; alwaysinline auto hdSupersample() const -> uint; alwaysinline auto hdMosaic() const -> bool; alwaysinline auto widescreen() const -> uint; alwaysinline auto wsbg(uint bg) const -> uint; - alwaysinline auto wsobj() const -> bool; + alwaysinline auto wsobj() const -> uint; alwaysinline auto winXad(uint x, bool bel) const -> uint; //ppu.cpp diff --git a/bsnes/target-bsnes/presentation/presentation.cpp b/bsnes/target-bsnes/presentation/presentation.cpp index a78216f1..2e21cabd 100644 --- a/bsnes/target-bsnes/presentation/presentation.cpp +++ b/bsnes/target-bsnes/presentation/presentation.cpp @@ -253,9 +253,16 @@ auto Presentation::resizeViewport() -> void { uint layoutWidth = viewportLayout.geometry().width(); uint layoutHeight = viewportLayout.geometry().height(); - uint widescreen = settings.emulator.hack.ppu.mode7.scale > 0 ? settings.emulator.hack.ppu.mode7.widescreen : 0; // 64 / 0 #widescreenextension + uint widescreen = settings.emulator.hack.ppu.mode7.scale > 0 ? settings.emulator.hack.ppu.mode7.widescreen : 0; + uint unintrMode = settings.emulator.hack.ppu.mode7.unintrMode; + uint unintrTop = settings.emulator.hack.ppu.mode7.unintrTop; + uint unintrBottom = settings.emulator.hack.ppu.mode7.unintrBottom; + uint unintrLeft = settings.emulator.hack.ppu.mode7.unintrLeft; + uint unintrRight = settings.emulator.hack.ppu.mode7.unintrRight; uint width = (256+2*widescreen) * (settings.video.aspectCorrection && !widescreen ? 8.0 / 7.0 : 1.0); uint height = (settings.video.overscan ? 240.0 : 224.0); + while(((int)width) - ((int)unintrLeft) - ((int)unintrRight) <= 10) { if (unintrLeft > 0) unintrLeft--; if (unintrRight > 0) unintrRight--; } + while(((int)height) - ((int)unintrTop) - ((int)unintrBottom) <= 10) { if (unintrTop > 0) unintrTop--; if (unintrBottom > 0) unintrBottom--; } uint viewportWidth, viewportHeight; if(visible() && !fullScreen()) { @@ -274,14 +281,14 @@ auto Presentation::resizeViewport() -> void { if(!video) return; if(settings.video.output == "Center") { - uint widthMultiplier = layoutWidth / (width - 20); - uint heightMultiplier = layoutHeight / (height - 10); // allow the loss of ~8 lines so 1080p can be 5x scale + uint widthMultiplier = layoutWidth / (width - (unintrMode < 1 ? 0 : (unintrLeft + unintrRight))); + uint heightMultiplier = layoutHeight / (height - (unintrMode < 1 ? 0 : (unintrTop + unintrBottom))); uint multiplier = min(widthMultiplier, heightMultiplier); viewportWidth = width * multiplier; viewportHeight = height * multiplier; } else if(settings.video.output == "Scale") { - double widthMultiplier = (double)layoutWidth / width; - double heightMultiplier = (double)layoutHeight / height; + double widthMultiplier = (double)layoutWidth / (width - (unintrMode < 2 ? 0 : (unintrLeft + unintrRight))); + double heightMultiplier = (double)layoutHeight / (height - (unintrMode < 2 ? 0 : (unintrTop + unintrBottom))); double multiplier = min(widthMultiplier, heightMultiplier); viewportWidth = width * multiplier; viewportHeight = height * multiplier; @@ -293,9 +300,11 @@ auto Presentation::resizeViewport() -> void { //center viewport within viewportLayout by use of viewportLayout padding int paddingWidth = layoutWidth - viewportWidth; int paddingHeight = layoutHeight - viewportHeight; + int paddingLeft = paddingWidth >= 0 ? paddingWidth / 2 : (1.0 * paddingWidth / (unintrLeft + unintrRight) * unintrLeft); + int paddingTop = paddingHeight >= 0 ? paddingHeight / 2 : (1.0 * paddingHeight / (unintrTop + unintrBottom) * unintrTop); viewportLayout.setPadding({ - paddingWidth / 2, paddingHeight / 2, - paddingWidth - paddingWidth / 2, paddingHeight - paddingHeight / 2 + paddingLeft, paddingTop, + paddingWidth - paddingLeft, paddingHeight - paddingTop }); } diff --git a/bsnes/target-bsnes/program/hacks.cpp b/bsnes/target-bsnes/program/hacks.cpp index 6fa796ea..eec21592 100644 --- a/bsnes/target-bsnes/program/hacks.cpp +++ b/bsnes/target-bsnes/program/hacks.cpp @@ -20,6 +20,12 @@ auto Program::hackCompatibility() -> void { emulator->configure("Hacks/PPU/Mode7/Wsbg4", settings.emulator.hack.ppu.mode7.wsbg4); emulator->configure("Hacks/PPU/Mode7/Wsobj", settings.emulator.hack.ppu.mode7.wsobj); emulator->configure("Hacks/PPU/Mode7/Igwin", settings.emulator.hack.ppu.mode7.igwin); + emulator->configure("Hacks/PPU/Mode7/Igwinx", settings.emulator.hack.ppu.mode7.igwinx); + emulator->configure("Hacks/PPU/Mode7/UnintrMode", settings.emulator.hack.ppu.mode7.unintrMode); + emulator->configure("Hacks/PPU/Mode7/UnintrTop", settings.emulator.hack.ppu.mode7.unintrTop); + emulator->configure("Hacks/PPU/Mode7/UnintrBottom", settings.emulator.hack.ppu.mode7.unintrBottom); + emulator->configure("Hacks/PPU/Mode7/UnintrLeft", settings.emulator.hack.ppu.mode7.unintrLeft); + emulator->configure("Hacks/PPU/Mode7/UnintrRight", settings.emulator.hack.ppu.mode7.unintrRight); emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); emulator->configure("Hacks/DSP/Fast", fastDSP); diff --git a/bsnes/target-bsnes/settings/emulator.cpp b/bsnes/target-bsnes/settings/emulator.cpp index a79c3c65..0e8404ca 100644 --- a/bsnes/target-bsnes/settings/emulator.cpp +++ b/bsnes/target-bsnes/settings/emulator.cpp @@ -4,6 +4,7 @@ auto EmulatorSettings::create() -> void { layout.setPadding(5_sx); + hacksNote.setForegroundColor({224, 0, 0}).setText("Note: some hack setting changes do not take effect until after reloading games or restarting the emulator."); optionsLabel.setText("Options").setFont(Font().setBold()); inputFocusLabel.setText("When focus is lost:"); pauseEmulation.setText("Pause emulation").onActivate([&] { @@ -35,10 +36,30 @@ auto EmulatorSettings::create() -> void { autoLoadStateOnLoad.setText("Auto-resume on load").setChecked(settings.emulator.autoLoadStateOnLoad).onToggle([&] { settings.emulator.autoLoadStateOnLoad = autoLoadStateOnLoad.checked(); }); - optionsSpacer.setColor({192, 192, 192}); - + dspLabel.setText("DSP (audio)").setFont(Font().setBold()); + fastDSP.setText("Fast mode").setChecked(settings.emulator.hack.dsp.fast).onToggle([&] { + settings.emulator.hack.dsp.fast = fastDSP.checked(); + emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast); + }); + cubicInterpolation.setText("Cubic interpolation").setChecked(settings.emulator.hack.dsp.cubic).onToggle([&] { + settings.emulator.hack.dsp.cubic = cubicInterpolation.checked(); + emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); + }); + coprocessorLabel.setText("Coprocessors").setFont(Font().setBold()); + coprocessorsDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessors.delayedSync).onToggle([&] { + settings.emulator.hack.coprocessors.delayedSync = coprocessorsDelayedSyncOption.checked(); + }); + coprocessorsHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessors.hle).onToggle([&] { + settings.emulator.hack.coprocessors.hle = coprocessorsHLEOption.checked(); + }); + superFXLabel.setText("SuperFX clock speed:"); + superFXValue.setAlignment(0.5); + superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] { + settings.emulator.hack.fastSuperFX = superFXClock.position() * 10 + 100; + superFXValue.setText({settings.emulator.hack.fastSuperFX, "%"}); + }).doChange(); ppuLabel.setText("PPU (video)").setFont(Font().setBold()); - fastPPU.setText("Fast mode").setChecked(settings.emulator.hack.ppu.fast).onToggle([&] { + fastPPU.setText("Fast mode [required for HD Mode 7 / bsnes-hd]").setChecked(settings.emulator.hack.ppu.fast).onToggle([&] { settings.emulator.hack.ppu.fast = fastPPU.checked(); if(!fastPPU.checked()) { noSpriteLimit.setEnabled(false).setChecked(false).doToggle(); @@ -49,7 +70,7 @@ auto EmulatorSettings::create() -> void { noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); }); - mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold()); + mode7Label.setText("HD Mode 7 / bsnes-hd").setFont(Font().setBold()); mode7ScaleLabel.setText("Scale:"); mode7Scale.append(ComboButtonItem().setText("disable").setProperty("multiplier", 0)); mode7Scale.append(ComboButtonItem().setText("1x 224p").setProperty("multiplier", 1)); @@ -69,14 +90,22 @@ auto EmulatorSettings::create() -> void { emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale); presentation.resizeViewport(); }); - mode7Perspective.setText("Perspective correction").setChecked(settings.emulator.hack.ppu.mode7.perspective).onToggle([&] { - settings.emulator.hack.ppu.mode7.perspective = mode7Perspective.checked(); + mode7PerspectiveLabel.setText("Perspective correction:"); + mode7Perspective.append(ComboButtonItem().setText("off").setProperty("mode", 0)); + mode7Perspective.append(ComboButtonItem().setText("auto/wide").setProperty("mode", 1)); + mode7Perspective.append(ComboButtonItem().setText("auto/medium").setProperty("mode", 2)); + mode7Perspective.append(ComboButtonItem().setText("auto/narrow").setProperty("mode", 3)); + mode7Perspective.append(ComboButtonItem().setText("on/wide").setProperty("mode", 4)); + mode7Perspective.append(ComboButtonItem().setText("on/medium").setProperty("mode", 5)); + mode7Perspective.append(ComboButtonItem().setText("on/narrow").setProperty("mode", 6)); + for(uint n = 0; n < 7; n++) { + if(mode7Perspective.item(n).property("mode").natural() == settings.emulator.hack.ppu.mode7.perspective) + mode7Perspective.item(n).setSelected(); + } + mode7Perspective.onChange([&] { + settings.emulator.hack.ppu.mode7.perspective = mode7Perspective.selected().property("mode").natural(); emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective); }); - mode7Mosaic.setText("keep Mosaic").setChecked(settings.emulator.hack.ppu.mode7.mosaic).onToggle([&] { - settings.emulator.hack.ppu.mode7.mosaic = mode7Mosaic.checked(); - emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); - }); mode7SupersampleLabel.setText("Supersampling:"); mode7Supersample.append(ComboButtonItem().setText("none").setProperty("sss", 1)); mode7Supersample.append(ComboButtonItem().setText("2x").setProperty("sss", 2)); @@ -94,9 +123,9 @@ auto EmulatorSettings::create() -> void { settings.emulator.hack.ppu.mode7.supersample = mode7Supersample.selected().property("sss").natural(); emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); }); - igwin.setText("ignore outside window").setChecked(settings.emulator.hack.ppu.mode7.igwin).onToggle([&] { - settings.emulator.hack.ppu.mode7.igwin = igwin.checked(); - emulator->configure("Hacks/PPU/Mode7/Igwin", settings.emulator.hack.ppu.mode7.igwin); + mode7Mosaic.setText("Keep Mosaics non-HD").setChecked(settings.emulator.hack.ppu.mode7.mosaic).onToggle([&] { + settings.emulator.hack.ppu.mode7.mosaic = mode7Mosaic.checked(); + emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); }); mode7WidescreenLabel.setText("Widescreen:"); mode7Widescreen.append(ComboButtonItem().setText(" none ").setProperty("adval", 0)); @@ -129,7 +158,10 @@ auto EmulatorSettings::create() -> void { wsBG1.append(ComboButtonItem().setText(">160").setProperty("wsbgmode", 9)); wsBG1.append(ComboButtonItem().setText("<200").setProperty("wsbgmode", 10)); wsBG1.append(ComboButtonItem().setText(">200").setProperty("wsbgmode", 11)); - for(uint n = 0; n < 12; n++) { + wsBG1.append(ComboButtonItem().setText("crop").setProperty("wsbgmode", 12)); + wsBG1.append(ComboButtonItem().setText("cAut").setProperty("wsbgmode", 13)); + wsBG1.append(ComboButtonItem().setText("dis.").setProperty("wsbgmode", 14)); + for(uint n = 0; n <= 14; n++) { if(wsBG1.item(n).property("wsbgmode").natural() == settings.emulator.hack.ppu.mode7.wsbg1) wsBG1.item(n).setSelected(); } @@ -150,7 +182,10 @@ auto EmulatorSettings::create() -> void { wsBG2.append(ComboButtonItem().setText(">160").setProperty("wsbgmode", 9)); wsBG2.append(ComboButtonItem().setText("<200").setProperty("wsbgmode", 10)); wsBG2.append(ComboButtonItem().setText(">200").setProperty("wsbgmode", 11)); - for(uint n = 0; n < 12; n++) { + wsBG2.append(ComboButtonItem().setText("crop").setProperty("wsbgmode", 12)); + wsBG2.append(ComboButtonItem().setText("cAut").setProperty("wsbgmode", 13)); + wsBG2.append(ComboButtonItem().setText("dis.").setProperty("wsbgmode", 14)); + for(uint n = 0; n <= 14; n++) { if(wsBG2.item(n).property("wsbgmode").natural() == settings.emulator.hack.ppu.mode7.wsbg2) wsBG2.item(n).setSelected(); } @@ -171,7 +206,10 @@ auto EmulatorSettings::create() -> void { wsBG3.append(ComboButtonItem().setText(">160").setProperty("wsbgmode", 9)); wsBG3.append(ComboButtonItem().setText("<200").setProperty("wsbgmode", 10)); wsBG3.append(ComboButtonItem().setText(">200").setProperty("wsbgmode", 11)); - for(uint n = 0; n < 12; n++) { + wsBG3.append(ComboButtonItem().setText("crop").setProperty("wsbgmode", 12)); + wsBG3.append(ComboButtonItem().setText("cAut").setProperty("wsbgmode", 13)); + wsBG3.append(ComboButtonItem().setText("dis.").setProperty("wsbgmode", 14)); + for(uint n = 0; n <= 14; n++) { if(wsBG3.item(n).property("wsbgmode").natural() == settings.emulator.hack.ppu.mode7.wsbg3) wsBG3.item(n).setSelected(); } @@ -192,7 +230,10 @@ auto EmulatorSettings::create() -> void { wsBG4.append(ComboButtonItem().setText(">160").setProperty("wsbgmode", 9)); wsBG4.append(ComboButtonItem().setText("<200").setProperty("wsbgmode", 10)); wsBG4.append(ComboButtonItem().setText(">200").setProperty("wsbgmode", 11)); - for(uint n = 0; n < 12; n++) { + wsBG4.append(ComboButtonItem().setText("crop").setProperty("wsbgmode", 12)); + wsBG4.append(ComboButtonItem().setText("cAut").setProperty("wsbgmode", 13)); + wsBG4.append(ComboButtonItem().setText("dis.").setProperty("wsbgmode", 14)); + for(uint n = 0; n <= 14; n++) { if(wsBG4.item(n).property("wsbgmode").natural() == settings.emulator.hack.ppu.mode7.wsbg4) wsBG4.item(n).setSelected(); } @@ -200,47 +241,182 @@ auto EmulatorSettings::create() -> void { settings.emulator.hack.ppu.mode7.wsbg4 = wsBG4.selected().property("wsbgmode").natural(); emulator->configure("Hacks/PPU/Mode7/Wsbg4", settings.emulator.hack.ppu.mode7.wsbg4); }); - wsObj.setText("unsafe sprites").setChecked(settings.emulator.hack.ppu.mode7.wsobj).onToggle([&] { - settings.emulator.hack.ppu.mode7.wsobj = wsObj.checked(); + wsObjLabel.setText("Sprites:"); + wsObj.append(ComboButtonItem().setText("normal").setProperty("mode", 0)); + wsObj.append(ComboButtonItem().setText("unsafe").setProperty("mode", 1)); + wsObj.append(ComboButtonItem().setText("disab.").setProperty("mode", 2)); + for(uint n = 0; n < 3; n++) { + if(wsObj.item(n).property("mode").natural() == settings.emulator.hack.ppu.mode7.igwin) + wsObj.item(n).setSelected(); + } + wsObj.onChange([&] { + settings.emulator.hack.ppu.mode7.wsobj = wsObj.selected().property("mode").natural(); emulator->configure("Hacks/PPU/Mode7/Wsobj", settings.emulator.hack.ppu.mode7.wsobj); }); - dspLabel.setText("DSP (audio)").setFont(Font().setBold()); - fastDSP.setText("Fast mode").setChecked(settings.emulator.hack.dsp.fast).onToggle([&] { - settings.emulator.hack.dsp.fast = fastDSP.checked(); - emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast); + igwinLabel.setText("Ignore window:"); + igwin.append(ComboButtonItem().setText("none").setProperty("mode", 0)); + igwin.append(ComboButtonItem().setText("outside").setProperty("mode", 1)); + igwin.append(ComboButtonItem().setText("outs&alw").setProperty("mode", 2)); + igwin.append(ComboButtonItem().setText("all").setProperty("mode", 3)); + for(uint n = 0; n < 4; n++) { + if(igwin.item(n).property("mode").natural() == settings.emulator.hack.ppu.mode7.igwin) + igwin.item(n).setSelected(); + } + igwin.onChange([&] { + settings.emulator.hack.ppu.mode7.igwin = igwin.selected().property("mode").natural(); + emulator->configure("Hacks/PPU/Mode7/Igwin", settings.emulator.hack.ppu.mode7.igwin); }); - cubicInterpolation.setText("Cubic interpolation").setChecked(settings.emulator.hack.dsp.cubic).onToggle([&] { - settings.emulator.hack.dsp.cubic = cubicInterpolation.checked(); - emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); + igwinxLabel.setText("Fallback x-coordinate:"); + igwinx.append(ComboButtonItem().setText(" 40").setProperty("col", 40)); + igwinx.append(ComboButtonItem().setText(" 88").setProperty("col", 88)); + igwinx.append(ComboButtonItem().setText("128").setProperty("col", 128)); + igwinx.append(ComboButtonItem().setText("168").setProperty("col", 168)); + igwinx.append(ComboButtonItem().setText("216").setProperty("col", 216)); + for(uint n = 0; n < 5; n++) { + if(igwinx.item(n).property("col").natural() == settings.emulator.hack.ppu.mode7.igwinx) + igwinx.item(n).setSelected(); + } + igwinx.onChange([&] { + settings.emulator.hack.ppu.mode7.igwinx = igwinx.selected().property("col").natural(); + emulator->configure("Hacks/PPU/Mode7/Igwinx", settings.emulator.hack.ppu.mode7.igwinx); }); - coprocessorLabel.setText("Coprocessors").setFont(Font().setBold()); - coprocessorsDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessors.delayedSync).onToggle([&] { - settings.emulator.hack.coprocessors.delayedSync = coprocessorsDelayedSyncOption.checked(); + unintrModeLabel.setText("Soft crop:"); + unintrMode.append(ComboButtonItem().setText(" none ").setProperty("mode", 0)); + unintrMode.append(ComboButtonItem().setText("center").setProperty("mode", 1)); + unintrMode.append(ComboButtonItem().setText(" scale").setProperty("mode", 2)); + for(uint n = 0; n < 3; n++) { + if(unintrMode.item(n).property("mode").natural() == settings.emulator.hack.ppu.mode7.unintrMode) + unintrMode.item(n).setSelected(); + } + unintrMode.onChange([&] { + settings.emulator.hack.ppu.mode7.unintrMode = unintrMode.selected().property("mode").natural(); + emulator->configure("Hacks/PPU/Mode7/UnintrMode", settings.emulator.hack.ppu.mode7.unintrMode); }); - coprocessorsHLEOption.setText("Prefer HLE").setChecked(settings.emulator.hack.coprocessors.hle).onToggle([&] { - settings.emulator.hack.coprocessors.hle = coprocessorsHLEOption.checked(); + + unintrTopLabel.setText("Top:"); + unintrTop.append(ComboButtonItem().setText(" 0").setProperty("marg", 0)); + unintrTop.append(ComboButtonItem().setText(" 10").setProperty("marg", 10)); + unintrTop.append(ComboButtonItem().setText(" 20").setProperty("marg", 20)); + unintrTop.append(ComboButtonItem().setText(" 30").setProperty("marg", 30)); + unintrTop.append(ComboButtonItem().setText(" 40").setProperty("marg", 40)); + unintrTop.append(ComboButtonItem().setText(" 50").setProperty("marg", 50)); + unintrTop.append(ComboButtonItem().setText(" 60").setProperty("marg", 60)); + unintrTop.append(ComboButtonItem().setText(" 70").setProperty("marg", 70)); + unintrTop.append(ComboButtonItem().setText(" 80").setProperty("marg", 80)); + unintrTop.append(ComboButtonItem().setText(" 90").setProperty("marg", 90)); + unintrTop.append(ComboButtonItem().setText("100").setProperty("marg", 100)); + unintrTop.append(ComboButtonItem().setText("110").setProperty("marg", 110)); + unintrTop.append(ComboButtonItem().setText("120").setProperty("marg", 120)); + unintrTop.append(ComboButtonItem().setText("130").setProperty("marg", 130)); + unintrTop.append(ComboButtonItem().setText("140").setProperty("marg", 140)); + unintrTop.append(ComboButtonItem().setText("150").setProperty("marg", 150)); + for(uint n = 0; n < 17; n++) { + if(unintrTop.item(n).property("marg").natural() == settings.emulator.hack.ppu.mode7.unintrTop) + unintrTop.item(n).setSelected(); + } + unintrTop.onChange([&] { + settings.emulator.hack.ppu.mode7.unintrTop = unintrTop.selected().property("marg").natural(); + emulator->configure("Hacks/PPU/Mode7/UnintrTop", settings.emulator.hack.ppu.mode7.unintrTop); }); - superFXLabel.setText("SuperFX clock speed:"); - superFXValue.setAlignment(0.5); - superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] { - settings.emulator.hack.fastSuperFX = superFXClock.position() * 10 + 100; - superFXValue.setText({settings.emulator.hack.fastSuperFX, "%"}); - }).doChange(); - hacksNote.setForegroundColor({224, 0, 0}).setText("Note: some hack setting changes do not take effect until after reloading games."); + + unintrBottomLabel.setText("Bottom:"); + unintrBottom.append(ComboButtonItem().setText(" 0").setProperty("marg", 0)); + unintrBottom.append(ComboButtonItem().setText(" 10").setProperty("marg", 10)); + unintrBottom.append(ComboButtonItem().setText(" 20").setProperty("marg", 20)); + unintrBottom.append(ComboButtonItem().setText(" 30").setProperty("marg", 30)); + unintrBottom.append(ComboButtonItem().setText(" 40").setProperty("marg", 40)); + unintrBottom.append(ComboButtonItem().setText(" 50").setProperty("marg", 50)); + unintrBottom.append(ComboButtonItem().setText(" 60").setProperty("marg", 60)); + unintrBottom.append(ComboButtonItem().setText(" 70").setProperty("marg", 70)); + unintrBottom.append(ComboButtonItem().setText(" 80").setProperty("marg", 80)); + unintrBottom.append(ComboButtonItem().setText(" 90").setProperty("marg", 90)); + unintrBottom.append(ComboButtonItem().setText("100").setProperty("marg", 100)); + unintrBottom.append(ComboButtonItem().setText("110").setProperty("marg", 110)); + unintrBottom.append(ComboButtonItem().setText("120").setProperty("marg", 120)); + unintrBottom.append(ComboButtonItem().setText("130").setProperty("marg", 130)); + unintrBottom.append(ComboButtonItem().setText("140").setProperty("marg", 140)); + unintrBottom.append(ComboButtonItem().setText("150").setProperty("marg", 150)); + for(uint n = 0; n < 17; n++) { + if(unintrBottom.item(n).property("marg").natural() == settings.emulator.hack.ppu.mode7.unintrBottom) + unintrBottom.item(n).setSelected(); + } + unintrBottom.onChange([&] { + settings.emulator.hack.ppu.mode7.unintrBottom = unintrBottom.selected().property("marg").natural(); + emulator->configure("Hacks/PPU/Mode7/UnintrBottom", settings.emulator.hack.ppu.mode7.unintrBottom); + }); + + unintrLeftLabel.setText("Left:"); + unintrLeft.append(ComboButtonItem().setText(" 0").setProperty("marg", 0)); + unintrLeft.append(ComboButtonItem().setText(" 10").setProperty("marg", 10)); + unintrLeft.append(ComboButtonItem().setText(" 20").setProperty("marg", 20)); + unintrLeft.append(ComboButtonItem().setText(" 30").setProperty("marg", 30)); + unintrLeft.append(ComboButtonItem().setText(" 40").setProperty("marg", 40)); + unintrLeft.append(ComboButtonItem().setText(" 50").setProperty("marg", 50)); + unintrLeft.append(ComboButtonItem().setText(" 60").setProperty("marg", 60)); + unintrLeft.append(ComboButtonItem().setText(" 70").setProperty("marg", 70)); + unintrLeft.append(ComboButtonItem().setText(" 80").setProperty("marg", 80)); + unintrLeft.append(ComboButtonItem().setText(" 90").setProperty("marg", 90)); + unintrLeft.append(ComboButtonItem().setText("100").setProperty("marg", 100)); + unintrLeft.append(ComboButtonItem().setText("110").setProperty("marg", 110)); + unintrLeft.append(ComboButtonItem().setText("120").setProperty("marg", 120)); + unintrLeft.append(ComboButtonItem().setText("130").setProperty("marg", 130)); + unintrLeft.append(ComboButtonItem().setText("140").setProperty("marg", 140)); + unintrLeft.append(ComboButtonItem().setText("150").setProperty("marg", 150)); + for(uint n = 0; n < 17; n++) { + if(unintrLeft.item(n).property("marg").natural() == settings.emulator.hack.ppu.mode7.unintrLeft) + unintrLeft.item(n).setSelected(); + } + unintrLeft.onChange([&] { + settings.emulator.hack.ppu.mode7.unintrLeft = unintrLeft.selected().property("marg").natural(); + emulator->configure("Hacks/PPU/Mode7/UnintrLeft", settings.emulator.hack.ppu.mode7.unintrLeft); + }); + + unintrRightLabel.setText("Right:"); + unintrRight.append(ComboButtonItem().setText(" 0").setProperty("marg", 0)); + unintrRight.append(ComboButtonItem().setText(" 10").setProperty("marg", 10)); + unintrRight.append(ComboButtonItem().setText(" 20").setProperty("marg", 20)); + unintrRight.append(ComboButtonItem().setText(" 30").setProperty("marg", 30)); + unintrRight.append(ComboButtonItem().setText(" 40").setProperty("marg", 40)); + unintrRight.append(ComboButtonItem().setText(" 50").setProperty("marg", 50)); + unintrRight.append(ComboButtonItem().setText(" 60").setProperty("marg", 60)); + unintrRight.append(ComboButtonItem().setText(" 70").setProperty("marg", 70)); + unintrRight.append(ComboButtonItem().setText(" 80").setProperty("marg", 80)); + unintrRight.append(ComboButtonItem().setText(" 90").setProperty("marg", 90)); + unintrRight.append(ComboButtonItem().setText("100").setProperty("marg", 100)); + unintrRight.append(ComboButtonItem().setText("110").setProperty("marg", 110)); + unintrRight.append(ComboButtonItem().setText("120").setProperty("marg", 120)); + unintrRight.append(ComboButtonItem().setText("130").setProperty("marg", 130)); + unintrRight.append(ComboButtonItem().setText("140").setProperty("marg", 140)); + unintrRight.append(ComboButtonItem().setText("150").setProperty("marg", 150)); + for(uint n = 0; n < 17; n++) { + if(unintrRight.item(n).property("marg").natural() == settings.emulator.hack.ppu.mode7.unintrRight) + unintrRight.item(n).setSelected(); + } + unintrRight.onChange([&] { + settings.emulator.hack.ppu.mode7.unintrRight = unintrRight.selected().property("marg").natural(); + emulator->configure("Hacks/PPU/Mode7/UnintrRight", settings.emulator.hack.ppu.mode7.unintrRight); + }); + } auto EmulatorSettings::updateConfiguration() -> void { emulator->configure("Hacks/PPU/Fast", fastPPU.checked()); emulator->configure("Hacks/PPU/NoSpriteLimit", noSpriteLimit.checked()); emulator->configure("Hacks/PPU/Mode7/Scale", mode7Scale.selected().property("multiplier").natural()); - emulator->configure("Hacks/PPU/Mode7/Perspective", mode7Perspective.checked()); + emulator->configure("Hacks/PPU/Mode7/Perspective", mode7Perspective.property("mode").natural()); emulator->configure("Hacks/PPU/Mode7/Widescreen", mode7Widescreen.property("addval").natural()); emulator->configure("Hacks/PPU/Mode7/Wsbg1", wsBG1.property("wsbgmode").natural()); emulator->configure("Hacks/PPU/Mode7/Wsbg2", wsBG2.property("wsbgmode").natural()); emulator->configure("Hacks/PPU/Mode7/Wsbg3", wsBG3.property("wsbgmode").natural()); emulator->configure("Hacks/PPU/Mode7/Wsbg4", wsBG4.property("wsbgmode").natural()); - emulator->configure("Hacks/PPU/Mode7/Wsobj", wsObj.checked()); - emulator->configure("Hacks/PPU/Mode7/Igwin", igwin.checked()); + emulator->configure("Hacks/PPU/Mode7/Wsobj", wsObj.property("mode").natural()); + emulator->configure("Hacks/PPU/Mode7/Igwin", igwin.property("mode").natural()); + emulator->configure("Hacks/PPU/Mode7/Igwinx", igwin.property("col").natural()); + emulator->configure("Hacks/PPU/Mode7/UnintrMode", igwin.property("mode").natural()); + emulator->configure("Hacks/PPU/Mode7/UnintrTop", igwin.property("marg").natural()); + emulator->configure("Hacks/PPU/Mode7/UnintrBottom", igwin.property("marg").natural()); + emulator->configure("Hacks/PPU/Mode7/UnintrLeft", igwin.property("marg").natural()); + emulator->configure("Hacks/PPU/Mode7/UnintrRight", igwin.property("marg").natural()); emulator->configure("Hacks/PPU/Mode7/Supersample", mode7Supersample.property("sss").natural()); emulator->configure("Hacks/PPU/Mode7/Mosaic", mode7Mosaic.checked()); emulator->configure("Hacks/DSP/Fast", fastDSP.checked()); diff --git a/bsnes/target-bsnes/settings/settings.cpp b/bsnes/target-bsnes/settings/settings.cpp index f9126719..64fd0125 100644 --- a/bsnes/target-bsnes/settings/settings.cpp +++ b/bsnes/target-bsnes/settings/settings.cpp @@ -98,14 +98,21 @@ auto Settings::process(bool load) -> void { bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast); bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit); bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale); - bind(boolean, "Emulator/Hack/PPU/Mode7/Perspective", emulator.hack.ppu.mode7.perspective); + bind(natural, "Emulator/Hack/PPU/Mode7/Widescreen", emulator.hack.ppu.mode7.widescreen); + bind(natural, "Emulator/Hack/PPU/Mode7/Perspective", emulator.hack.ppu.mode7.perspective); bind(natural, "Emulator/Hack/PPU/Mode7/Widescreen", emulator.hack.ppu.mode7.widescreen); bind(natural, "Emulator/Hack/PPU/Mode7/Wsbg1", emulator.hack.ppu.mode7.wsbg1); bind(natural, "Emulator/Hack/PPU/Mode7/Wsbg2", emulator.hack.ppu.mode7.wsbg2); bind(natural, "Emulator/Hack/PPU/Mode7/Wsbg3", emulator.hack.ppu.mode7.wsbg3); bind(natural, "Emulator/Hack/PPU/Mode7/Wsbg4", emulator.hack.ppu.mode7.wsbg4); - bind(boolean, "Emulator/Hack/PPU/Mode7/Wsobj", emulator.hack.ppu.mode7.wsobj); - bind(boolean, "Emulator/Hack/PPU/Mode7/Igwin", emulator.hack.ppu.mode7.igwin); + bind(natural, "Emulator/Hack/PPU/Mode7/Wsobj", emulator.hack.ppu.mode7.wsobj); + bind(natural, "Emulator/Hack/PPU/Mode7/Igwin", emulator.hack.ppu.mode7.igwin); + bind(natural, "Emulator/Hack/PPU/Mode7/Igwinx", emulator.hack.ppu.mode7.igwinx); + bind(natural, "Emulator/Hack/PPU/Mode7/UnintrMode", emulator.hack.ppu.mode7.unintrMode); + bind(natural, "Emulator/Hack/PPU/Mode7/UnintrTop", emulator.hack.ppu.mode7.unintrTop); + bind(natural, "Emulator/Hack/PPU/Mode7/UnintrBottom", emulator.hack.ppu.mode7.unintrBottom); + bind(natural, "Emulator/Hack/PPU/Mode7/UnintrLeft", emulator.hack.ppu.mode7.unintrLeft); + bind(natural, "Emulator/Hack/PPU/Mode7/UnintrRight", emulator.hack.ppu.mode7.unintrRight); bind(natural, "Emulator/Hack/PPU/Mode7/Supersample", emulator.hack.ppu.mode7.supersample); bind(boolean, "Emulator/Hack/PPU/Mode7/Mosaic", emulator.hack.ppu.mode7.mosaic); bind(boolean, "Emulator/Hack/DSP/Fast", emulator.hack.dsp.fast); diff --git a/bsnes/target-bsnes/settings/settings.hpp b/bsnes/target-bsnes/settings/settings.hpp index 40a89a08..bb2588e6 100644 --- a/bsnes/target-bsnes/settings/settings.hpp +++ b/bsnes/target-bsnes/settings/settings.hpp @@ -80,7 +80,7 @@ struct Settings : Markup::Node { bool noSpriteLimit = true; struct Mode7 { uint scale = 2; - bool perspective = true; + uint perspective = 1; uint supersample = 1; bool mosaic = false; uint widescreen = 72; @@ -88,8 +88,14 @@ struct Settings : Markup::Node { uint wsbg2 = 1; uint wsbg3 = 1; uint wsbg4 = 1; - bool wsobj = false; - bool igwin = false; + uint wsobj = 0; + uint igwin = 0; + uint igwinx = 128; + uint unintrMode = 1; + uint unintrTop = 10; + uint unintrBottom = 10; + uint unintrLeft = 20; + uint unintrRight = 20; } mode7; } ppu; struct DSP { @@ -256,6 +262,7 @@ struct EmulatorSettings : TabFrameItem { public: VerticalLayout layout{this}; + Label hacksNote{&layout, Size{~0, 0}}; Label optionsLabel{&layout, Size{~0, 0}, 2}; HorizontalLayout inputFocusLayout{&layout, Size{~0, 0}}; Label inputFocusLabel{&inputFocusLayout, Size{0, 0}}; @@ -264,11 +271,21 @@ struct EmulatorSettings : TabFrameItem { RadioLabel allowInput{&inputFocusLayout, Size{0, 0}}; Group inputFocusGroup{&pauseEmulation, &blockInput, &allowInput}; CheckLabel warnOnUnverifiedGames{&layout, Size{~0, 0}}; - CheckLabel autoSaveMemory{&layout, Size{~0, 0}}; HorizontalLayout autoStateLayout{&layout, Size{~0, 0}}; + CheckLabel autoSaveMemory{&autoStateLayout, Size{~0, 0}}; CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}}; CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}}; - Canvas optionsSpacer{&layout, Size{~0, 1}}; + Label dspLabel{&layout, Size{~0, 0}, 2}; + HorizontalLayout dspLayout{&layout, Size{~0, 0}}; + CheckLabel fastDSP{&dspLayout, Size{0, 0}}; + CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}}; + Label coprocessorLabel{&layout, Size{~0, 0}, 2}; + HorizontalLayout coprocessorsLayout{&layout, Size{~0, 0}}; + Label superFXLabel{&coprocessorsLayout, Size{0, 0}}; + Label superFXValue{&coprocessorsLayout, Size{50_sx, 0}}; + HorizontalSlider superFXClock{&coprocessorsLayout, Size{~0, 0}}; + CheckLabel coprocessorsDelayedSyncOption{&coprocessorsLayout, Size{0, 0}}; + CheckLabel coprocessorsHLEOption{&coprocessorsLayout, Size{0, 0}}; Label ppuLabel{&layout, Size{~0, 0}, 2}; HorizontalLayout ppuLayout{&layout, Size{~0, 0}}; CheckLabel fastPPU{&ppuLayout, Size{0, 0}}; @@ -277,11 +294,11 @@ struct EmulatorSettings : TabFrameItem { HorizontalLayout mode7Layout{&layout, Size{~0, 0}}; Label mode7ScaleLabel{&mode7Layout, Size{0, 0}}; ComboButton mode7Scale{&mode7Layout, Size{0, 0}}; - CheckLabel mode7Perspective{&mode7Layout, Size{0, 0}}; - CheckLabel mode7Mosaic{&mode7Layout, Size{0, 0}}; + Label mode7PerspectiveLabel{&mode7Layout, Size{0, 0}}; + ComboButton mode7Perspective{&mode7Layout, Size{0, 0}}; Label mode7SupersampleLabel{&mode7Layout, Size{0, 0}}; ComboButton mode7Supersample{&mode7Layout, Size{0, 0}}; - CheckLabel igwin{&mode7Layout, Size{0, 0}}; + CheckLabel mode7Mosaic{&mode7Layout, Size{0, 0}}; HorizontalLayout widescreenLayout{&layout, Size{~0, 0}}; Label mode7WidescreenLabel{&widescreenLayout, Size{0, 0}}; ComboButton mode7Widescreen{&widescreenLayout, Size{0, 0}}; @@ -293,20 +310,24 @@ struct EmulatorSettings : TabFrameItem { ComboButton wsBG3{&widescreenLayout, Size{0, 0}}; Label wsBG4Label{&widescreenLayout, Size{0, 0}}; ComboButton wsBG4{&widescreenLayout, Size{0, 0}}; - CheckLabel wsObj{&widescreenLayout, Size{0, 0}}; - Label dspLabel{&layout, Size{~0, 0}, 2}; - HorizontalLayout dspLayout{&layout, Size{~0, 0}}; - CheckLabel fastDSP{&dspLayout, Size{0, 0}}; - CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}}; - Label coprocessorLabel{&layout, Size{~0, 0}, 2}; - HorizontalLayout coprocessorsLayout{&layout, Size{~0, 0}}; - CheckLabel coprocessorsDelayedSyncOption{&coprocessorsLayout, Size{0, 0}}; - CheckLabel coprocessorsHLEOption{&coprocessorsLayout, Size{0, 0}}; - HorizontalLayout superFXLayout{&layout, Size{~0, 0}}; - Label superFXLabel{&superFXLayout, Size{0, 0}}; - Label superFXValue{&superFXLayout, Size{50_sx, 0}}; - HorizontalSlider superFXClock{&superFXLayout, Size{~0, 0}}; - Label hacksNote{&layout, Size{~0, 0}}; + Label wsObjLabel{&widescreenLayout, Size{0, 0}}; + ComboButton wsObj{&widescreenLayout, Size{0, 0}}; + HorizontalLayout viewModLayout{&layout, Size{~0, 0}}; + Label igwinLabel{&viewModLayout, Size{0, 0}}; + ComboButton igwin{&viewModLayout, Size{0, 0}}; + Label igwinxLabel{&viewModLayout, Size{0, 0}}; + ComboButton igwinx{&viewModLayout, Size{0, 0}}; + HorizontalLayout unintrLayout{&layout, Size{~0, 0}}; + Label unintrModeLabel{&unintrLayout, Size{0, 0}}; + ComboButton unintrMode{&unintrLayout, Size{0, 0}}; + Label unintrTopLabel{&unintrLayout, Size{0, 0}}; + ComboButton unintrTop{&unintrLayout, Size{0, 0}}; + Label unintrBottomLabel{&unintrLayout, Size{0, 0}}; + ComboButton unintrBottom{&unintrLayout, Size{0, 0}}; + Label unintrLeftLabel{&unintrLayout, Size{0, 0}}; + ComboButton unintrLeft{&unintrLayout, Size{0, 0}}; + Label unintrRightLabel{&unintrLayout, Size{0, 0}}; + ComboButton unintrRight{&unintrLayout, Size{0, 0}}; }; struct DriverSettings : TabFrameItem {