From 510d13398e4e78a26c4d36763fe6bf83ef009019 Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Fri, 20 Feb 2026 00:04:55 +0000 Subject: [PATCH 1/3] Try to render like chihiro in 640x480 --- src/lindbergh/resolution.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lindbergh/resolution.c b/src/lindbergh/resolution.c index 94b5fd0..79687b5 100644 --- a/src/lindbergh/resolution.c +++ b/src/lindbergh/resolution.c @@ -200,6 +200,11 @@ void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) height = 480; } } + + // Force proper 4:3 rendering at 640x480 to match Chihiro Version of game + if(addr == (void *)0x804d8cb && width == 640) { width = 800; x = x - 40;} + if(addr == (void *)0x80f4082 && width == 640) { width = 800; x = x - 40;} + if(addr == (void *)0x80fff23 && width == 640) { width = 800;} } else if (gId == SEGA_RACE_TV) { @@ -295,7 +300,7 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width, { void (*_glTexImage2D)(unsigned int target, int level, int internalformat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels) = dlsym(RTLD_NEXT, "glTexImage2D"); - + if (gGrp == GROUP_OUTRUN) { if (gWidth != 800 || gHeight != 480) @@ -316,6 +321,12 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width, height = 480; } } + + // Force proper 4:3 rendering at 640x480 to match Chihiro Version of game + if(width == 640) + { + width = 800; + } } _glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); } From 81d91852270f37479e3d665975b270542c7da53f Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Tue, 24 Feb 2026 21:47:19 +0000 Subject: [PATCH 2/3] Fix outrun 4:3 in a different way@ --- src/lindbergh/blitStretching.c | 53 +++++++++++++++++++++++++++++----- src/lindbergh/sdlCalls.c | 13 +++++++-- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/lindbergh/blitStretching.c b/src/lindbergh/blitStretching.c index acd1796..2c4b69c 100644 --- a/src/lindbergh/blitStretching.c +++ b/src/lindbergh/blitStretching.c @@ -22,6 +22,8 @@ extern SDL_Window *sdlWindow; int blitWidth = 0; int blitHeight = 0; +int gameIsOutrunChihiroMode = 0; + int fboInitialized = false; GLuint fboId = 0; GLuint fboTextureId = 0; @@ -36,6 +38,30 @@ void initBlitting() blitSetWidthandHeightSize(); blitInitializeFbo(); dest.gameScale = 1.0f; + + if(gGrp == GROUP_OUTRUN && getConfig()->width == 640) + gameIsOutrunChihiroMode = 1; +} + +/** + * If the game is outrun in 640x480 mode we force the game to write to out temp framebuffer directly + * to avoid the framebuffer of the window being too small and the edges getting clipped off. So we + * hook glBindFramebuffer and if the game tries to bind the default framebuffer, we bind our FBO instead and + * set the viewport to 800x480 so the game renders to our FBO. Then in blitStretch we copy from our FBO to the + * screen with the correct aspect ratio. + */ +void glBindFramebufferEXT(GLenum target, GLuint framebuffer) { + void (*_glBindFramebufferEXT)(GLenum target, GLuint framebuffer) = dlsym(RTLD_NEXT, "glBindFramebufferEXT"); + + if(target == GL_FRAMEBUFFER_EXT && framebuffer == 0 && fboInitialized && gameIsOutrunChihiroMode) + { + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + glViewport(0, 0, 800, 480); + glClear(GL_COLOR_BUFFER_BIT); + return; + } + + _glBindFramebufferEXT(target, framebuffer); } void blitSetWidthandHeightSize() @@ -121,14 +147,18 @@ void blitStretch() if (fboInitialized && fboId > 0 && fboTextureId > 0 && sdlWindow) { SDL_GetWindowSizeInPixels(sdlWindow, &drawableW, &drawableH); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); - glBlitFramebuffer(0, 0, blitWidth, blitHeight, 0, 0, blitWidth, blitHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - float gameAspect = (float)blitWidth / (float)blitHeight; float windowAspect = (float)drawableW / (float)drawableH; + float gameAspect = 4.0 / 3.0; + + if(!gameIsOutrunChihiroMode) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); + glBlitFramebuffer(0, 0, blitWidth, blitHeight, 0, 0, blitWidth, blitHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + gameAspect = (float)blitWidth / (float)blitHeight; + } + dest.W = drawableW; dest.H = drawableH; @@ -142,7 +172,7 @@ void blitStretch() dest.H = (GLsizei)(drawableW / gameAspect); dest.Y = (drawableH - dest.H) / 2; } - + glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); @@ -151,7 +181,14 @@ void blitStretch() glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glReadBuffer(GL_COLOR_ATTACHMENT0); - glBlitFramebuffer(0, 0, blitWidth, blitHeight, dest.X, dest.Y, dest.X + dest.W, dest.Y + dest.H, GL_COLOR_BUFFER_BIT, GL_LINEAR); + if(gameIsOutrunChihiroMode) + { + glBlitFramebuffer(80, 0, blitWidth - 80, blitHeight, dest.X, dest.Y, dest.X + dest.W, dest.Y + dest.H, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + else + { + glBlitFramebuffer(0, 0, blitWidth, blitHeight, dest.X, dest.Y, dest.X + dest.W, dest.Y + dest.H, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/src/lindbergh/sdlCalls.c b/src/lindbergh/sdlCalls.c index 2556969..197907c 100644 --- a/src/lindbergh/sdlCalls.c +++ b/src/lindbergh/sdlCalls.c @@ -130,11 +130,18 @@ void startSDL(int *argcp, char **argv) strcat(sdlGameTitle, getGameName()); + int windowWidth = gWidth; + + if(gGrp == GROUP_OUTRUN && getConfig()->width == 640) + { + gWidth = 800; + } + // Hacky way to make AxA games render the characters properly if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) sdlWindow = SDL_CreateWindow(sdlGameTitle, 1024, 768, windowFlags); else - sdlWindow = SDL_CreateWindow(sdlGameTitle, gWidth, gHeight, windowFlags); + sdlWindow = SDL_CreateWindow(sdlGameTitle, windowWidth, gHeight, windowFlags); if (!sdlWindow) { @@ -172,11 +179,11 @@ void startSDL(int *argcp, char **argv) // If games are any of the LGJ or Primeval Hunt, we prevent the window to resize. if ((gGrp == GROUP_LGJ || gId == PRIMEVAL_HUNT) && !getConfig()->fullscreen) - SDL_SetWindowMaximumSize(sdlWindow, gWidth, gHeight); + SDL_SetWindowMaximumSize(sdlWindow, windowWidth, gHeight); else if (gGrp != GROUP_LGJ) SDL_SetWindowMaximumSize(sdlWindow, displayMode->w, displayMode->h); - SDL_SetWindowMinimumSize(sdlWindow, gWidth, gHeight); + SDL_SetWindowMinimumSize(sdlWindow, windowWidth, gHeight); // Hacky way to make AxA games render the characters properly if (gId == QUIZ_AXA || gId == QUIZ_AXA_LIVE) From 87dccf51a42b1d071807d531b809db345b7c7463 Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Tue, 24 Feb 2026 21:51:31 +0000 Subject: [PATCH 3/3] Undo changes to resolution --- src/lindbergh/resolution.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/lindbergh/resolution.c b/src/lindbergh/resolution.c index 79687b5..94b5fd0 100644 --- a/src/lindbergh/resolution.c +++ b/src/lindbergh/resolution.c @@ -200,11 +200,6 @@ void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) height = 480; } } - - // Force proper 4:3 rendering at 640x480 to match Chihiro Version of game - if(addr == (void *)0x804d8cb && width == 640) { width = 800; x = x - 40;} - if(addr == (void *)0x80f4082 && width == 640) { width = 800; x = x - 40;} - if(addr == (void *)0x80fff23 && width == 640) { width = 800;} } else if (gId == SEGA_RACE_TV) { @@ -300,7 +295,7 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width, { void (*_glTexImage2D)(unsigned int target, int level, int internalformat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels) = dlsym(RTLD_NEXT, "glTexImage2D"); - + if (gGrp == GROUP_OUTRUN) { if (gWidth != 800 || gHeight != 480) @@ -321,12 +316,6 @@ void glTexImage2D(unsigned int target, int level, int internalformat, int width, height = 480; } } - - // Force proper 4:3 rendering at 640x480 to match Chihiro Version of game - if(width == 640) - { - width = 800; - } } _glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); }