Compare commits

...
Author SHA1 Message Date
cambragol f3403c07f7 Merge branch 'main' into stretching-for-800x500-widescreen-mode 2025-06-22 20:33:24 +09:00
cambragol 8c44106688 Merge branch 'main' into stretching-for-800x500-widescreen-mode 2025-06-21 12:02:50 +09:00
cambragol 5248e138f6 removed Scale_2x
Removed scale_2x function, as it no longer worked nor was needed.
Fixed backing up of gPreseverAspect in guserAspectPreference - used to restore users aspect preference, when returning from the main game screen - that window overrides users aspect preferences.
2025-06-16 14:34:32 +09:00
cambragol 0076894414 First pass on widescreen mode
Adds stretching for full and aspect ratio, set from f2_res (for now)
Allows different content to be stretched/displayed uniformly (to mix 640 and 800 content smoothly)
Adds resizeContent which lets renderPresent know how big(wide) the content being stretched is. A bool also reads/restores users aspect ratio setting (because main game area forces aspect ratio)
Adds small tweak to endgame.cc that smooths the transition after slideshow to the credits movie (just keeps the screen black)
2025-06-15 17:50:06 +09:00
8 changed files with 175 additions and 28 deletions
+6 -1
View File
@@ -213,6 +213,9 @@ void endgamePlaySlideshow()
if (endgameEndingSlideshowWindowInit() == -1) {
return;
}
restoreUserAspectPreference();
resizeContent(640, 480);
for (int index = 0; index < gEndgameEndingsLength; index++) {
EndgameEnding* ending = &(gEndgameEndings[index]);
@@ -226,6 +229,8 @@ void endgamePlaySlideshow()
}
}
}
resizeContent(screenGetWidth(), screenGetHeight(), true);
endgameEndingSlideshowWindowFree();
}
@@ -648,7 +653,7 @@ static void endgameEndingSlideshowWindowFree()
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
colorPaletteLoad("color.pal");
paletteFadeTo(_cmap);
//paletteFadeTo(_cmap); // cutting this prevents the momentary return to the location map
colorCycleEnable();
+6
View File
@@ -177,6 +177,7 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int fl
debugPrint(">init_options_menu\n");
if (!gIsMapper && skipOpeningMovies < 2) {
resizeContent(640,480);
showSplash();
}
@@ -1195,6 +1196,9 @@ static void gameFreeGlobalVars()
static void showHelp()
{
ScopedGameMode gm(GameMode::kHelp);
restoreUserAspectPreference();
resizeContent(640,480);
bool isoWasEnabled = isoDisable();
gameMouseObjectsHide();
@@ -1268,6 +1272,8 @@ static void showHelp()
if (isoWasEnabled) {
isoEnable();
}
resizeContent(screenGetWidth(),screenGetHeight(), true);
}
// 0x4440B8
+4
View File
@@ -169,6 +169,9 @@ int gameMoviePlay(int movie, int flags)
paletteFadeTo(gPaletteBlack);
gGameMovieFaded = true;
}
restoreUserAspectPreference();
resizeContent(640,480);
int gameMovieWindowX = (screenGetWidth() - GAME_MOVIE_WINDOW_WIDTH) / 2;
int gameMovieWindowY = (screenGetHeight() - GAME_MOVIE_WINDOW_HEIGHT) / 2;
@@ -287,6 +290,7 @@ int gameMoviePlay(int movie, int flags)
float b = (float)(Color2RGB(oldTextColor) & 0x1F) * flt_50352A;
windowSetTextColor(r, g, b);
}
resizeContent(screenGetWidth(), screenGetHeight(), true);
windowDestroy(win);
+2
View File
@@ -2694,6 +2694,8 @@ static int _PrepLoad(File* stream)
// 0x47F4C8
static int _EndLoad(File* stream)
{
resizeContent(screenGetWidth(), screenGetHeight(), true);
wmMapMusicStart();
dudeSetName(_LSData[_slot_cursor].characterName);
interfaceBarRefresh();
+7
View File
@@ -85,6 +85,7 @@ int falloutMain(int argc, char** argv)
int skipOpeningMovies;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SKIP_OPENING_MOVIES_KEY, &skipOpeningMovies);
if (skipOpeningMovies < 1) {
resizeContent(640, 480);
gameMoviePlay(MOVIE_IPLOGO, GAME_MOVIE_FADE_IN);
gameMoviePlay(MOVIE_INTRO, 0);
gameMoviePlay(MOVIE_CREDITS, 0);
@@ -364,6 +365,9 @@ static void showDeath()
artCacheFlush();
colorCycleDisable();
gameMouseSetCursor(MOUSE_CURSOR_NONE);
restoreUserAspectPreference();
resizeContent(640, 480);
bool oldCursorIsHidden = cursorIsHidden();
if (oldCursorIsHidden) {
@@ -490,6 +494,9 @@ static void showDeath()
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
colorCycleEnable();
resizeContent(800, 500);
}
// 0x4814A8
+125 -22
View File
@@ -20,6 +20,14 @@ namespace fallout {
static bool createRenderer(int width, int height);
static void destroyRenderer();
bool gStretchEnabled = false;
static bool gPreserveAspect = true; // used internally for stretching
static bool gUserAspectPreference = true; // store user preference for restore
bool gHighQuality = true; // maybe false by default
static int gContentWidth = 800;
static int gContentHeight = 500;
// screen rect
Rect _scr_size;
@@ -99,11 +107,15 @@ void _zero_vid_mem()
}
}
void restoreUserAspectPreference()
{
gPreserveAspect = gUserAspectPreference;
}
// 0x4CAE1C
int _GNW95_init_mode_ex(int width, int height, int bpp)
{
bool fullscreen = true;
int scale = 1;
Config resolutionConfig;
if (configInit(&resolutionConfig)) {
@@ -123,16 +135,20 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
fullscreen = !windowed;
}
int scaleValue;
if (configGetInt(&resolutionConfig, "MAIN", "SCALE_2X", &scaleValue)) {
scale = scaleValue + 1; // 0 = 1x, 1 = 2x
// Only allow scaling if resulting game resolution is >= 640x480
if ((width / scale) < 640 || (height / scale) < 480) {
scale = 1;
} else {
width /= scale;
height /= scale;
}
bool stretch;
if (configGetBool(&resolutionConfig, "MAIN", "STRETCH", &stretch)) {
gStretchEnabled = stretch;
}
bool aspect;
if (configGetBool(&resolutionConfig, "MAIN", "ASPECT", &aspect)) {
gPreserveAspect = aspect;
gUserAspectPreference = aspect; // back up the user preference for restoring after using game screen
}
bool quality;
if (configGetBool(&resolutionConfig, "MAIN", "QUALITY", &quality)) {
gHighQuality = quality;
}
configGetBool(&resolutionConfig, "IFACE", "IFACE_BAR_MODE", &gInterfaceBarMode);
@@ -145,7 +161,6 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
f2_res_defaults["MAIN"]["SCR_WIDTH"] = "640";
f2_res_defaults["MAIN"]["SCR_HEIGHT"] = "480";
f2_res_defaults["MAIN"]["WINDOWED"] = "0";
f2_res_defaults["MAIN"]["SCALE_2X"] = "0";
f2_res_defaults["IFACE"]["IFACE_BAR_MODE"] = "0";
f2_res_defaults["IFACE"]["IFACE_BAR_WIDTH"] = "0";
f2_res_defaults["IFACE"]["IFACE_BAR_SIDE_ART"] = "0";
@@ -159,7 +174,7 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
configFree(&resolutionConfig);
}
if (_GNW95_init_window(width, height, fullscreen, scale) == -1) {
if (_GNW95_init_window(width, height, fullscreen) == -1) {
return -1;
}
@@ -196,7 +211,7 @@ int _init_vesa_mode(int width, int height)
}
// 0x4CAEDC
int _GNW95_init_window(int width, int height, bool fullscreen, int scale)
int _GNW95_init_window(int width, int height, bool fullscreen)
{
if (gSdlWindow == nullptr) {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
@@ -204,13 +219,17 @@ int _GNW95_init_window(int width, int height, bool fullscreen, int scale)
Uint32 windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI;
if (fullscreen) {
windowFlags |= SDL_WINDOW_FULLSCREEN;
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * scale, height * scale, windowFlags);
gSdlWindow = SDL_CreateWindow(gProgramWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, windowFlags);
if (gSdlWindow == nullptr) {
return -1;
}
int actualWidth = 0, actualHeight = 0;
SDL_GetWindowSize(gSdlWindow, &actualWidth, &actualHeight);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, gHighQuality ? "1" : "0");
if (!createRenderer(width, height)) {
destroyRenderer();
@@ -387,10 +406,6 @@ static bool createRenderer(int width, int height)
return false;
}
if (SDL_RenderSetLogicalSize(gSdlRenderer, width, height) != 0) {
return false;
}
gSdlTexture = SDL_CreateTexture(gSdlRenderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (gSdlTexture == nullptr) {
return false;
@@ -433,11 +448,99 @@ void handleWindowSizeChanged()
createRenderer(screenGetWidth(), screenGetHeight());
}
void resizeContent(int width, int height)
{
gContentWidth = width;
gContentHeight = height;
renderPresent();
}
void resizeContent(int width, int height, bool preserveAspect)
{
gContentWidth = width;
gContentHeight = height;
gPreserveAspect = preserveAspect;
renderPresent();
}
void renderPresent()
{
SDL_UpdateTexture(gSdlTexture, nullptr, gSdlTextureSurface->pixels, gSdlTextureSurface->pitch);
// Get physical pixel size of the window (DPI-aware)
int renderW, renderH;
SDL_GetRendererOutputSize(gSdlRenderer, &renderW, &renderH);
// Get logical window size
int windowW, windowH;
SDL_GetWindowSize(gSdlWindow, &windowW, &windowH);
float scaleX = (float)renderW / windowW;
float scaleY = (float)renderH / windowH;
// Get logical rendering size fallback
int logicalW, logicalH;
SDL_RenderGetLogicalSize(gSdlRenderer, &logicalW, &logicalH);
if (logicalW == 0) logicalW = gContentWidth;
if (logicalH == 0) logicalH = gContentHeight;
SDL_Rect srcRect, destRect;
if (gStretchEnabled) {
int contentOffsetX = (gSdlTextureSurface->w - gContentWidth) / 2;
int contentOffsetY = (gSdlTextureSurface->h - gContentHeight) / 2;
srcRect = {
contentOffsetX,
contentOffsetY,
gContentWidth,
gContentHeight
};
if (gPreserveAspect) {
float contentAspect = (float)gContentWidth / gContentHeight;
float windowAspect = (float)renderW / renderH;
if (windowAspect > contentAspect) {
destRect.h = renderH;
destRect.w = (int)(renderH * contentAspect);
destRect.x = (renderW - destRect.w) / 2;
destRect.y = 0;
} else {
destRect.w = renderW;
destRect.h = (int)(renderW / contentAspect);
destRect.x = 0;
destRect.y = (renderH - destRect.h) / 2;
}
} else {
// Stretch to fill entire window (without aspect ratio preserved)
destRect = { 0, 0, renderW, renderH };
}
SDL_UpdateTexture(gSdlTexture, &srcRect,
(uint8_t*)gSdlTextureSurface->pixels + contentOffsetY * gSdlTextureSurface->pitch + contentOffsetX * 4,
gSdlTextureSurface->pitch);
} else {
// Stretching disabled — display original size scaled for DPI, centered
srcRect = { 0, 0, gSdlTextureSurface->w, gSdlTextureSurface->h };
destRect = {
(renderW - (int)(gSdlTextureSurface->w * scaleX)) / 2,
(renderH - (int)(gSdlTextureSurface->h * scaleY)) / 2,
(int)(gSdlTextureSurface->w * scaleX),
(int)(gSdlTextureSurface->h * scaleY)
};
SDL_UpdateTexture(gSdlTexture, nullptr,
gSdlTextureSurface->pixels,
gSdlTextureSurface->pitch);
}
SDL_SetRenderDrawColor(gSdlRenderer, 0, 0, 255, 255);
SDL_RenderClear(gSdlRenderer);
SDL_RenderCopy(gSdlRenderer, gSdlTexture, nullptr, nullptr);
SDL_RenderCopy(gSdlRenderer, gSdlTexture, &srcRect, &destRect);
SDL_RenderPresent(gSdlRenderer);
}
+4 -1
View File
@@ -11,6 +11,9 @@ namespace fallout {
extern Rect _scr_size;
extern void (*_scr_blit)(unsigned char* src, int src_pitch, int a3, int src_x, int src_y, int src_width, int src_height, int dest_x, int dest_y);
extern void (*_zero_mem)();
void resizeContent(int width, int height);
void resizeContent(int width, int height, bool preserveAspect);
void restoreUserAspectPreference(void);
extern SDL_Window* gSdlWindow;
extern SDL_Surface* gSdlSurface;
@@ -31,7 +34,7 @@ void _get_start_mode_();
void _zero_vid_mem();
int _GNW95_init_mode_ex(int width, int height, int bpp);
int _init_vesa_mode(int width, int height);
int _GNW95_init_window(int width, int height, bool fullscreen, int scale);
int _GNW95_init_window(int width, int height, bool fullscreen);
int directDrawInit(int width, int height, int bpp);
void directDrawFree();
void directDrawSetPaletteInRange(unsigned char* a1, int a2, int a3);
+21 -4
View File
@@ -2994,6 +2994,9 @@ static int wmWorldMapFunc(int a1)
ScopedGameMode gm(GameMode::kWorldmap);
wmFadeOut();
restoreUserAspectPreference();
resizeContent(800, 500);
if (wmInterfaceInit() == -1) {
wmInterfaceExit();
@@ -3132,6 +3135,9 @@ static int wmWorldMapFunc(int a1)
}
wmFadeOut();
resizeContent(screenGetWidth(), screenGetHeight(), true);
mapLoadById(wmGenData.encounterMapId);
}
break;
@@ -3187,6 +3193,9 @@ static int wmWorldMapFunc(int a1)
}
wmFadeOut();
resizeContent(screenGetWidth(), screenGetHeight(), true);
mapLoadById(map);
break;
}
@@ -3225,6 +3234,9 @@ static int wmWorldMapFunc(int a1)
}
wmFadeOut();
resizeContent(screenGetWidth(), screenGetHeight(), true);
mapLoadById(map);
}
}
@@ -3373,6 +3385,9 @@ static int wmRndEncounterOccurred()
}
wmFadeOut();
resizeContent(640, 480);
mapLoadById(MAP_IN_GAME_MOVIE1);
return 1;
}
@@ -3388,14 +3403,16 @@ static int wmRndEncounterOccurred()
wmMatchAreaContainingMapIdx(wmForceEncounterMapId, &(wmGenData.currentCarAreaId));
}
}
resizeContent(screenGetWidth(), screenGetHeight(), true);
// For unknown reason fadeout and blinking icon are mutually exclusive.
if ((wmForceEncounterFlags & ENCOUNTER_FLAG_FADEOUT) != 0) {
wmFadeOut();
} else if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_ICON) == 0) {
//if ((wmForceEncounterFlags & ENCOUNTER_FLAG_FADEOUT) != 0) {
// wmFadeOut();
//} else if ((wmForceEncounterFlags & ENCOUNTER_FLAG_NO_ICON) == 0) {
bool special = (wmForceEncounterFlags & ENCOUNTER_FLAG_ICON_SP) != 0;
wmBlinkRndEncounterIcon(special);
}
//}
mapLoadById(wmForceEncounterMapId);