From 6224af6178ca9ab51350e0ff31dfd3d8dcd95fcc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 2 Jan 2023 11:13:32 +0300 Subject: [PATCH 01/50] Fix help screen issues Closes #179 --- src/game.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/game.cc b/src/game.cc index 1e25c96..60099ac 100644 --- a/src/game.cc +++ b/src/game.cc @@ -1161,6 +1161,10 @@ static void showHelp() bool colorCycleWasEnabled = colorCycleEnabled(); colorCycleDisable(); + // CE: Help screen uses separate color palette which is incompatible with + // colors in other windows. Setup overlay to hide everything. + int overlay = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), 0, WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP); + int helpWindowX = (screenGetWidth() - HELP_SCREEN_WIDTH) / 2; int helpWindowY = (screenGetHeight() - HELP_SCREEN_HEIGHT) / 2; int win = windowCreate(helpWindowX, helpWindowY, HELP_SCREEN_WIDTH, HELP_SCREEN_HEIGHT, 0, WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP); @@ -1172,10 +1176,21 @@ static void showHelp() if (backgroundFrmImage.lock(backgroundFid)) { paletteSetEntries(gPaletteBlack); blitBufferToBuffer(backgroundFrmImage.getData(), HELP_SCREEN_WIDTH, HELP_SCREEN_HEIGHT, HELP_SCREEN_WIDTH, windowBuffer, HELP_SCREEN_WIDTH); - windowShow(win); + colorPaletteLoad("art\\intrface\\helpscrn.pal"); paletteSetEntries(_cmap); + // CE: Fill overlay with darkest color in the palette. It might + // not be completely black, but at least it's uniform. + bufferFill(windowGetBuffer(overlay), + screenGetWidth(), + screenGetHeight(), + screenGetWidth(), + intensityColorTable[_colorTable[0]][0]); + + windowShow(overlay); + windowShow(win); + while (inputGetInput() == -1 && _game_user_wants_to_quit == 0) { sharedFpsLimiter.mark(); renderPresent(); @@ -1195,6 +1210,7 @@ static void showHelp() } } + windowDestroy(overlay); windowDestroy(win); colorPaletteLoad("color.pal"); paletteSetEntries(_cmap); From 812079004c5a12b71726b2988727ac8f949f25b6 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 2 Jan 2023 13:09:03 +0300 Subject: [PATCH 02/50] Make credits fullscreen See #3, #215 --- src/credits.cc | 101 +++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/src/credits.cc b/src/credits.cc index ef46d10..2021ac9 100644 --- a/src/credits.cc +++ b/src/credits.cc @@ -22,8 +22,6 @@ namespace fallout { -#define CREDITS_WINDOW_WIDTH (640) -#define CREDITS_WINDOW_HEIGHT (480) #define CREDITS_WINDOW_SCROLLING_DELAY (38) static bool creditsFileParseNextLine(char* dest, int* font, int* color); @@ -78,39 +76,35 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) mouseShowCursor(); } - int creditsWindowX = (screenGetWidth() - CREDITS_WINDOW_WIDTH) / 2; - int creditsWindowY = (screenGetHeight() - CREDITS_WINDOW_HEIGHT) / 2; - int window = windowCreate(creditsWindowX, creditsWindowY, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_HEIGHT, _colorTable[0], 20); + int windowWidth = screenGetWidth(); + int windowHeight = screenGetHeight(); + int window = windowCreate(0, 0, windowWidth, windowHeight, _colorTable[0], 20); soundContinueAll(); if (window != -1) { unsigned char* windowBuffer = windowGetBuffer(window); if (windowBuffer != NULL) { - unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT); + unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight); if (backgroundBuffer) { soundContinueAll(); - memset(backgroundBuffer, _colorTable[0], CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT); + memset(backgroundBuffer, _colorTable[0], windowWidth * windowHeight); if (backgroundFid != -1) { - CacheEntry* backgroundFrmHandle; - Art* frm = artLock(backgroundFid, &backgroundFrmHandle); - if (frm != NULL) { - int width = artGetWidth(frm, 0, 0); - int height = artGetHeight(frm, 0, 0); - unsigned char* backgroundFrmData = artGetFrameData(frm, 0, 0); - blitBufferToBuffer(backgroundFrmData, - width, - height, - width, - backgroundBuffer + CREDITS_WINDOW_WIDTH * ((CREDITS_WINDOW_HEIGHT - height) / 2) + (CREDITS_WINDOW_WIDTH - width) / 2, - CREDITS_WINDOW_WIDTH); - artUnlock(backgroundFrmHandle); + FrmImage backgroundFrmImage; + if (backgroundFrmImage.lock(backgroundFid)) { + blitBufferToBuffer(backgroundFrmImage.getData(), + backgroundFrmImage.getWidth(), + backgroundFrmImage.getHeight(), + backgroundFrmImage.getWidth(), + backgroundBuffer + windowWidth * ((windowHeight - backgroundFrmImage.getHeight()) / 2) + (windowWidth - backgroundFrmImage.getWidth()) / 2, + windowWidth); + backgroundFrmImage.unlock(); } } - unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT); + unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight); if (intermediateBuffer != NULL) { - memset(intermediateBuffer, 0, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT); + memset(intermediateBuffer, 0, windowWidth * windowHeight); fontSetCurrent(gCreditsWindowTitleFont); int titleFontLineHeight = fontGetLineHeight(); @@ -119,21 +113,20 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) int nameFontLineHeight = fontGetLineHeight(); int lineHeight = nameFontLineHeight + (titleFontLineHeight >= nameFontLineHeight ? titleFontLineHeight - nameFontLineHeight : 0); - int stringBufferSize = CREDITS_WINDOW_WIDTH * lineHeight; + int stringBufferSize = windowWidth * lineHeight; unsigned char* stringBuffer = (unsigned char*)internal_malloc(stringBufferSize); if (stringBuffer != NULL) { blitBufferToBuffer(backgroundBuffer, - CREDITS_WINDOW_WIDTH, - CREDITS_WINDOW_HEIGHT, - CREDITS_WINDOW_WIDTH, + windowWidth, + windowHeight, + windowWidth, windowBuffer, - CREDITS_WINDOW_WIDTH); + windowWidth); windowRefresh(window); paletteFadeTo(_cmap); - unsigned char* v40 = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH; char str[260]; int font; int color; @@ -142,15 +135,15 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) while (creditsFileParseNextLine(str, &font, &color)) { fontSetCurrent(font); - int v19 = fontGetStringWidth(str); - if (v19 >= CREDITS_WINDOW_WIDTH) { + int stringWidth = fontGetStringWidth(str); + if (stringWidth >= windowWidth) { continue; } memset(stringBuffer, 0, stringBufferSize); - fontDrawText(stringBuffer, str, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH, color); + fontDrawText(stringBuffer, str, windowWidth, windowWidth, color); - unsigned char* dest = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH + (CREDITS_WINDOW_WIDTH - v19) / 2; + unsigned char* dest = intermediateBuffer + windowWidth * windowHeight - windowWidth + (windowWidth - stringWidth) / 2; unsigned char* src = stringBuffer; for (int index = 0; index < lineHeight; index++) { sharedFpsLimiter.mark(); @@ -160,22 +153,22 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) break; } - memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH); - memcpy(dest, src, v19); + memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth); + memcpy(dest, src, stringWidth); blitBufferToBuffer(backgroundBuffer, - CREDITS_WINDOW_WIDTH, - CREDITS_WINDOW_HEIGHT, - CREDITS_WINDOW_WIDTH, + windowWidth, + windowHeight, + windowWidth, windowBuffer, - CREDITS_WINDOW_WIDTH); + windowWidth); blitBufferToBufferTrans(intermediateBuffer, - CREDITS_WINDOW_WIDTH, - CREDITS_WINDOW_HEIGHT, - CREDITS_WINDOW_WIDTH, + windowWidth, + windowHeight, + windowWidth, windowBuffer, - CREDITS_WINDOW_WIDTH); + windowWidth); while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) { } @@ -184,7 +177,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) windowRefresh(window); - src += CREDITS_WINDOW_WIDTH; + src += windowWidth; sharedFpsLimiter.throttle(); renderPresent(); @@ -196,29 +189,29 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) } if (!stop) { - for (int index = 0; index < CREDITS_WINDOW_HEIGHT; index++) { + for (int index = 0; index < windowHeight; index++) { sharedFpsLimiter.mark(); if (inputGetInput() != -1) { break; } - memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH); - memset(intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH, 0, CREDITS_WINDOW_WIDTH); + memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth); + memset(intermediateBuffer + windowWidth * windowHeight - windowWidth, 0, windowWidth); blitBufferToBuffer(backgroundBuffer, - CREDITS_WINDOW_WIDTH, - CREDITS_WINDOW_HEIGHT, - CREDITS_WINDOW_WIDTH, + windowWidth, + windowHeight, + windowWidth, windowBuffer, - CREDITS_WINDOW_WIDTH); + windowWidth); blitBufferToBufferTrans(intermediateBuffer, - CREDITS_WINDOW_WIDTH, - CREDITS_WINDOW_HEIGHT, - CREDITS_WINDOW_WIDTH, + windowWidth, + windowHeight, + windowWidth, windowBuffer, - CREDITS_WINDOW_WIDTH); + windowWidth); while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) { } From 03145e4fcd4e8ca2b180fca9d381715adc0e0f97 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 2 Jan 2023 14:34:42 +0300 Subject: [PATCH 03/50] Fix endgame slideshow issues See #3 Closes #215 --- src/endgame.cc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/endgame.cc b/src/endgame.cc index 9c68b9f..66653ea 100644 --- a/src/endgame.cc +++ b/src/endgame.cc @@ -83,6 +83,7 @@ static void _endgame_movie_bk_process(); static int endgameEndingInit(); static void endgameEndingFree(); static int endgameDeathEndingValidate(int* percentage); +static void endgameEndingUpdateOverlay(); // The number of lines in current subtitles file. // @@ -204,6 +205,8 @@ static unsigned char* gEndgameEndingSlideshowWindowBuffer; // 0x570BF4 static int gEndgameEndingSlideshowWindow; +static int gEndgameEndingOverlay; + // 0x43F788 void endgamePlaySlideshow() { @@ -321,6 +324,9 @@ static void endgameEndingRenderPanningScene(int direction, const char* narratorF bufferFill(gEndgameEndingSlideshowWindowBuffer, ENDGAME_ENDING_WINDOW_WIDTH, ENDGAME_ENDING_WINDOW_HEIGHT, ENDGAME_ENDING_WINDOW_WIDTH, _colorTable[0]); endgameEndingLoadPalette(6, 327); + // CE: Update overlay. + endgameEndingUpdateOverlay(); + unsigned char palette[768]; memcpy(palette, _cmap, 768); @@ -454,6 +460,9 @@ static void endgameEndingRenderStaticScene(int fid, const char* narratorFileName endgameEndingLoadPalette(FID_TYPE(fid), fid & 0xFFF); + // CE: Update overlay. + endgameEndingUpdateOverlay(); + endgameEndingVoiceOverInit(narratorFileName); unsigned int delay; @@ -555,6 +564,13 @@ static int endgameEndingSlideshowWindowInit() paletteFadeTo(gPaletteBlack); + // CE: Every slide has a separate color palette which is incompatible with + // main color palette. Setup overlay to hide everything. + gEndgameEndingOverlay = windowCreate(0, 0, screenGetWidth(), screenGetHeight(), _colorTable[0], WINDOW_MOVE_ON_TOP); + if (gEndgameEndingOverlay == -1) { + return -1; + } + int windowEndgameEndingX = (screenGetWidth() - ENDGAME_ENDING_WINDOW_WIDTH) / 2; int windowEndgameEndingY = (screenGetHeight() - ENDGAME_ENDING_WINDOW_HEIGHT) / 2; gEndgameEndingSlideshowWindow = windowCreate(windowEndgameEndingX, @@ -623,6 +639,7 @@ static void endgameEndingSlideshowWindowFree() speechSetEndCallback(NULL); windowDestroy(gEndgameEndingSlideshowWindow); + windowDestroy(gEndgameEndingOverlay); if (!_endgame_mouse_state) { mouseHideCursor(); @@ -1212,4 +1229,14 @@ char* endgameDeathEndingGetFileName() return gEndgameDeathEndingFileName; } +void endgameEndingUpdateOverlay() +{ + bufferFill(windowGetBuffer(gEndgameEndingOverlay), + windowGetWidth(gEndgameEndingOverlay), + windowGetHeight(gEndgameEndingOverlay), + windowGetWidth(gEndgameEndingOverlay), + intensityColorTable[_colorTable[0]][0]); + windowRefresh(gEndgameEndingOverlay); +} + } // namespace fallout From d040ea814aaa9ac60b1e0232c4daeb5932a27a7e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 2 Jan 2023 21:15:43 +0300 Subject: [PATCH 04/50] Fix objects being animated while playing movies --- src/interpreter_extra.cc | 58 ++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index efbd190..8000f43 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -358,27 +358,6 @@ static const int dword_453F90[3] = { 0x10000, }; -// 0x453F9C -static const unsigned short word_453F9C[MOVIE_COUNT] = { - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, - GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, -}; - // 0x453FC0 static Rect stru_453FC0 = { 0, 0, 640, 480 }; @@ -3591,21 +3570,48 @@ static void opRegAnimObjectRunToTile(Program* program) // 0x45A14C static void opPlayGameMovie(Program* program) { - unsigned short flags[MOVIE_COUNT]; - memcpy(flags, word_453F9C, sizeof(word_453F9C)); + // 0x453F9C + static const unsigned short flags[MOVIE_COUNT] = { + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC, + }; program->flags |= PROGRAM_FLAG_0x20; - int data = programStackPopInteger(program); + int movie = programStackPopInteger(program); + + // CE: Disable map updates. Needed to stop animation of objects (dude in + // particular) when playing movies (the problem can be seen as visual + // artifacts when playing endgame oilrig explosion). + bool isoWasDisabled = isoDisable(); gameDialogDisable(); - if (gameMoviePlay(data, word_453F9C[data]) == -1) { - debugPrint("\nError playing movie %d!", data); + if (gameMoviePlay(movie, flags[movie]) == -1) { + debugPrint("\nError playing movie %d!", movie); } gameDialogEnable(); + if (isoWasDisabled) { + isoEnable(); + } + program->flags &= ~PROGRAM_FLAG_0x20; } From a5cefd6c8b677b4764f05bf3085dc9121e638cbe Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 3 Jan 2023 21:10:20 +0300 Subject: [PATCH 05/50] Clear dirty rect during map updates Previous solution to replace squares was destructive in nature and could possibly lead to unexpected things (especially with mods where tile 0x293 might not be opaque black). New solution follows mapper refresh routines where entire dirty rect is reset to black as a preparation step. --- src/map.cc | 56 +++++++++++++++++++++++++---------------------------- src/tile.cc | 8 ++++++++ 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/map.cc b/src/map.cc index 5890d6a..5269ec7 100644 --- a/src/map.cc +++ b/src/map.cc @@ -1503,36 +1503,44 @@ static void isoWindowRefreshRect(Rect* rect) // 0x483EE4 static void isoWindowRefreshRectGame(Rect* rect) { - Rect clampedDirtyRect; - if (rectIntersection(rect, &gIsoWindowRect, &clampedDirtyRect) == -1) { + Rect rectToUpdate; + if (rectIntersection(rect, &gIsoWindowRect, &rectToUpdate) == -1) { return; } - tileRenderFloorsInRect(&clampedDirtyRect, gElevation); - _grid_render(&clampedDirtyRect, gElevation); - _obj_render_pre_roof(&clampedDirtyRect, gElevation); - tileRenderRoofsInRect(&clampedDirtyRect, gElevation); - _obj_render_post_roof(&clampedDirtyRect, gElevation); + // CE: Clear dirty rect to prevent most of the visual artifacts near map + // edges. + bufferFill(gIsoWindowBuffer + rectToUpdate.top * rectGetWidth(&gIsoWindowRect) + rectToUpdate.left, + rectGetWidth(&rectToUpdate), + rectGetHeight(&rectToUpdate), + rectGetWidth(&gIsoWindowRect), + 0); + + tileRenderFloorsInRect(&rectToUpdate, gElevation); + _obj_render_pre_roof(&rectToUpdate, gElevation); + tileRenderRoofsInRect(&rectToUpdate, gElevation); + _obj_render_post_roof(&rectToUpdate, gElevation); } // 0x483F44 static void isoWindowRefreshRectMapper(Rect* rect) { - Rect clampedDirtyRect; - if (rectIntersection(rect, &gIsoWindowRect, &clampedDirtyRect) == -1) { + Rect rectToUpdate; + if (rectIntersection(rect, &gIsoWindowRect, &rectToUpdate) == -1) { return; } - bufferFill(gIsoWindowBuffer + clampedDirtyRect.top * (_scr_size.right - _scr_size.left + 1) + clampedDirtyRect.left, - clampedDirtyRect.right - clampedDirtyRect.left + 1, - clampedDirtyRect.bottom - clampedDirtyRect.top + 1, - _scr_size.right - _scr_size.left + 1, + bufferFill(gIsoWindowBuffer + rectToUpdate.top * rectGetWidth(&gIsoWindowRect) + rectToUpdate.left, + rectGetWidth(&rectToUpdate), + rectGetHeight(&rectToUpdate), + rectGetWidth(&gIsoWindowRect), 0); - tileRenderFloorsInRect(&clampedDirtyRect, gElevation); - _grid_render(&clampedDirtyRect, gElevation); - _obj_render_pre_roof(&clampedDirtyRect, gElevation); - tileRenderRoofsInRect(&clampedDirtyRect, gElevation); - _obj_render_post_roof(&clampedDirtyRect, gElevation); + + tileRenderFloorsInRect(&rectToUpdate, gElevation); + _grid_render(&rectToUpdate, gElevation); + _obj_render_pre_roof(&rectToUpdate, gElevation); + tileRenderRoofsInRect(&rectToUpdate, gElevation); + _obj_render_post_roof(&rectToUpdate, gElevation); } // NOTE: Inlined. @@ -1721,18 +1729,6 @@ static int _square_load(File* stream, int flags) } } - // CE: Replace null tiles with a solid black tiles. This prevents copying - // buffer contents of nearby tiles during scrolling and repeating hex - // pointer when hovering mouse over null tiles. - for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { - for (int tileIndex = 0; tileIndex < SQUARE_GRID_SIZE; tileIndex++) { - int tile = _square[elevation]->field_0[tileIndex]; - if (tile == 0x00010001) { - _square[elevation]->field_0[tileIndex] = 0x00010293; - } - } - } - return 0; } diff --git a/src/tile.cc b/src/tile.cc index 0eb5ebc..e781c7d 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -631,6 +631,14 @@ static void tileRefreshGame(Rect* rect, int elevation) return; } + // CE: Clear dirty rect to prevent most of the visual artifacts near map + // edges. + bufferFill(gTileWindowBuffer + rectToUpdate.top * gTileWindowPitch + rectToUpdate.left, + rectGetWidth(&rectToUpdate), + rectGetHeight(&rectToUpdate), + gTileWindowPitch, + 0); + tileRenderFloorsInRect(&rectToUpdate, elevation); _obj_render_pre_roof(&rectToUpdate, elevation); tileRenderRoofsInRect(&rectToUpdate, elevation); From 9ee4cb4a263284a76a9b57cd7faaf39ca1a4b360 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 3 Jan 2023 23:00:38 +0300 Subject: [PATCH 06/50] Refactor min/max usage --- src/character_editor.cc | 43 +++++++++++++++++------------------------ src/credits.cc | 4 +++- src/dbox.cc | 2 +- src/dfile.cc | 7 +++---- src/game_mouse.cc | 19 ++++++++---------- src/graph_lib.cc | 5 ++--- src/interface.cc | 2 -- src/inventory.cc | 2 -- src/light.cc | 12 +++--------- src/object.cc | 40 +++++++++++++------------------------- src/text_object.cc | 7 ++----- src/tile.cc | 11 ++++------- 12 files changed, 57 insertions(+), 97 deletions(-) diff --git a/src/character_editor.cc b/src/character_editor.cc index a5c31de..70563ec 100644 --- a/src/character_editor.cc +++ b/src/character_editor.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include "art.h" @@ -4937,13 +4938,6 @@ static char* _itostndn(int value, char* dest) // 0x43AAEC static int characterEditorDrawCardWithOptions(int graphicId, const char* name, const char* attributes, char* description) { - unsigned char* ptr; - int v9; - int x; - int y; - short beginnings[WORD_WRAP_MAX_COUNT]; - short beginningsCount; - FrmImage frmImage; int fid = buildFid(OBJ_TYPE_SKILLDEX, graphicId, 0, 0, 0); if (!frmImage.lock(fid)) { @@ -4957,20 +4951,20 @@ static int characterEditorDrawCardWithOptions(int graphicId, const char* name, c gCharacterEditorWindowBuffer + 640 * 309 + 484, 640); - v9 = 150; - ptr = frmImage.getData(); - for (y = 0; y < frmImage.getHeight(); y++) { - for (x = 0; x < frmImage.getWidth(); x++) { - if (HighRGB(*ptr) < 2 && v9 >= x) { - v9 = x; + int extraDescriptionWidth = 150; + unsigned char* data = frmImage.getData(); + for (int y = 0; y < frmImage.getHeight(); y++) { + for (int x = 0; x < frmImage.getWidth(); x++) { + if (HighRGB(*data) < 2) { + extraDescriptionWidth = std::min(extraDescriptionWidth, x); } - ptr++; + data++; } } - v9 -= 8; - if (v9 < 0) { - v9 = 0; + extraDescriptionWidth -= 8; + if (extraDescriptionWidth < 0) { + extraDescriptionWidth = 0; } fontSetCurrent(102); @@ -4985,20 +4979,21 @@ static int characterEditorDrawCardWithOptions(int graphicId, const char* name, c fontDrawText(gCharacterEditorWindowBuffer + 640 * (268 + nameFontLineHeight - attributesFontLineHeight) + 348 + nameWidth + 8, attributes, 640, 640, _colorTable[0]); } - y = nameFontLineHeight; - windowDrawLine(gCharacterEditorWindow, 348, y + 272, 613, y + 272, _colorTable[0]); - windowDrawLine(gCharacterEditorWindow, 348, y + 273, 613, y + 273, _colorTable[0]); + windowDrawLine(gCharacterEditorWindow, 348, nameFontLineHeight + 272, 613, nameFontLineHeight + 272, _colorTable[0]); + windowDrawLine(gCharacterEditorWindow, 348, nameFontLineHeight + 273, 613, nameFontLineHeight + 273, _colorTable[0]); fontSetCurrent(101); int descriptionFontLineHeight = fontGetLineHeight(); - if (wordWrap(description, v9 + 136, beginnings, &beginningsCount) != 0) { + short beginnings[WORD_WRAP_MAX_COUNT]; + short beginningsCount; + if (wordWrap(description, extraDescriptionWidth + 136, beginnings, &beginningsCount) != 0) { // TODO: Leaking graphic handle. return -1; } - y = 315; + int y = 315; for (short i = 0; i < beginningsCount - 1; i++) { short beginning = beginnings[i]; short ending = beginnings[i + 1]; @@ -6660,9 +6655,7 @@ static int perkDialogDrawCard(int frmId, const char* name, const char* rank, cha unsigned char* stride = data; for (int x = 0; x < frmImage.getWidth(); x++) { if (HighRGB(*stride) < 2) { - if (extraDescriptionWidth > x) { - extraDescriptionWidth = x; - } + extraDescriptionWidth = std::min(extraDescriptionWidth, x); } stride++; } diff --git a/src/credits.cc b/src/credits.cc index 2021ac9..c380bbf 100644 --- a/src/credits.cc +++ b/src/credits.cc @@ -2,6 +2,8 @@ #include +#include + #include "art.h" #include "color.h" #include "cycle.h" @@ -112,7 +114,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle) fontSetCurrent(gCreditsWindowNameFont); int nameFontLineHeight = fontGetLineHeight(); - int lineHeight = nameFontLineHeight + (titleFontLineHeight >= nameFontLineHeight ? titleFontLineHeight - nameFontLineHeight : 0); + int lineHeight = std::max(titleFontLineHeight, nameFontLineHeight); int stringBufferSize = windowWidth * lineHeight; unsigned char* stringBuffer = (unsigned char*)internal_malloc(stringBufferSize); if (stringBuffer != NULL) { diff --git a/src/dbox.cc b/src/dbox.cc index c348a1f..8ca5d02 100644 --- a/src/dbox.cc +++ b/src/dbox.cc @@ -178,7 +178,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i int linesCount = 0; for (int index = 0; index < bodyLength; index++) { - // NOTE: Calls [fontGetStringWidth] twice because of [max] macro. + // NOTE: Originally there is no `max` macro. maximumLineWidth = std::max(fontGetStringWidth(body[index]), maximumLineWidth); linesCount++; } diff --git a/src/dfile.cc b/src/dfile.cc index 6e8e69f..52be55e 100644 --- a/src/dfile.cc +++ b/src/dfile.cc @@ -5,6 +5,8 @@ #include #include +#include + #include #include "platform_compat.h" @@ -818,10 +820,7 @@ static bool dfileReadCompressed(DFile* stream, void* ptr, size_t size) if (stream->decompressionStream->avail_in == 0) { // No more unprocessed data, request next chunk. - size_t bytesToRead = stream->entry->dataSize - stream->compressedBytesRead; - if (bytesToRead > DFILE_DECOMPRESSION_BUFFER_SIZE) { - bytesToRead = DFILE_DECOMPRESSION_BUFFER_SIZE; - } + size_t bytesToRead = std::min(DFILE_DECOMPRESSION_BUFFER_SIZE, stream->entry->dataSize - stream->compressedBytesRead); if (fread(stream->decompressionBuffer, bytesToRead, 1, stream->stream) != 1) { break; diff --git a/src/game_mouse.cc b/src/game_mouse.cc index ddd42bd..9f82fcd 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -4,6 +4,8 @@ #include #include +#include + #include "actions.h" #include "animation.h" #include "art.h" @@ -802,22 +804,17 @@ void gameMouseRefresh() char formattedActionPoints[8]; int color; - int v6 = _make_path(gDude, gDude->tile, gGameMouseHexCursor->tile, NULL, 1); - if (v6) { + int distance = _make_path(gDude, gDude->tile, gGameMouseHexCursor->tile, NULL, 1); + if (distance != 0) { if (!isInCombat()) { formattedActionPoints[0] = '\0'; color = _colorTable[31744]; } else { - int v7 = critterGetMovementPointCostAdjustedForCrippledLegs(gDude, v6); - int v8; - if (v7 - _combat_free_move >= 0) { - v8 = v7 - _combat_free_move; - } else { - v8 = 0; - } + int actionPointsMax = critterGetMovementPointCostAdjustedForCrippledLegs(gDude, distance); + int actionPointsRequired = std::max(0, actionPointsMax - _combat_free_move); - if (v8 <= gDude->data.critter.combat.ap) { - snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%d", v8); + if (actionPointsRequired <= gDude->data.critter.combat.ap) { + snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%d", actionPointsRequired); color = _colorTable[32767]; } else { snprintf(formattedActionPoints, sizeof(formattedActionPoints), "%c", 'X'); diff --git a/src/graph_lib.cc b/src/graph_lib.cc index a34a9b8..08a65dd 100644 --- a/src/graph_lib.cc +++ b/src/graph_lib.cc @@ -407,9 +407,8 @@ void grayscalePaletteUpdate(int a1, int a2) { if (a1 >= 0 && a2 <= 255) { for (int index = a1; index <= a2; index++) { - // NOTE: The only way to explain so much calls to `Color2RGB` with - // the same repeated pattern is by the use of min/max macros. - + // NOTE: Calls `Color2RGB` many times due to `min` and `max` macro + // uses. int v1 = std::max((Color2RGB(index) & 0x7C00) >> 10, std::max((Color2RGB(index) & 0x3E0) >> 5, Color2RGB(index) & 0x1F)); int v2 = std::min((Color2RGB(index) & 0x7C00) >> 10, std::min((Color2RGB(index) & 0x3E0) >> 5, Color2RGB(index) & 0x1F)); int v3 = v1 + v2; diff --git a/src/interface.cc b/src/interface.cc index e6bd9fa..7d492c3 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -1355,7 +1355,6 @@ int _intface_update_ammo_lights() int ratio = 0; if (p->isWeapon != 0) { - // calls sub_478674 twice, probably because if min/max kind macro int maximum = ammoGetCapacity(p->item); if (maximum > 0) { int current = ammoGetQuantity(p->item); @@ -1363,7 +1362,6 @@ int _intface_update_ammo_lights() } } else { if (itemGetType(p->item) == ITEM_TYPE_MISC) { - // calls sub_4793D0 twice, probably because if min/max kind macro int maximum = miscItemGetMaxCharges(p->item); if (maximum > 0) { int current = miscItemGetCharges(p->item); diff --git a/src/inventory.cc b/src/inventory.cc index 68fcf63..bf44d3c 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -1947,8 +1947,6 @@ static void _display_inventory_info(Object* item, int quantity, unsigned char* d v9 -= 1; } - // NOTE: Checking for quantity twice probably means inlined function - // or some macro expansion. if (quantity > 1) { if (v9 > 99999) { v9 = 99999; diff --git a/src/light.cc b/src/light.cc index 48f3a84..1077b89 100644 --- a/src/light.cc +++ b/src/light.cc @@ -1,6 +1,6 @@ #include "light.h" -#include +#include #include "map_defs.h" #include "object.h" @@ -55,7 +55,7 @@ void lightSetLightLevel(int lightLevel, bool shouldUpdateScreen) } } -// TODO: Looks strange - it tries to clamp intensity as light level? +// 0x47A980 int _light_get_tile(int elevation, int tile) { if (!elevationIsValid(elevation)) { @@ -66,13 +66,7 @@ int _light_get_tile(int elevation, int tile) return 0; } - int result = gLightIntensity[elevation][tile]; - - if (result >= 0x10000) { - result = 0x10000; - } - - return result; + return std::min(gLightIntensity[elevation][tile], LIGHT_LEVEL_MAX); } // 0x47A9C4 diff --git a/src/object.cc b/src/object.cc index a820746..71861ac 100644 --- a/src/object.cc +++ b/src/object.cc @@ -3,6 +3,8 @@ #include #include +#include + #include "animation.h" #include "art.h" #include "color.h" @@ -808,13 +810,8 @@ void _obj_render_pre_roof(Rect* rect, int elevation) ? gObjectListHeadByTile[topLeftTile + offsets[offsetIndex]] : NULL; if (objectListNode != NULL) { - // NOTE: calls _light_get_tile two times, probably result of min/max macro - int tileLight = _light_get_tile(elevation, objectListNode->obj->tile); - if (tileLight >= ambientLight) { - light = tileLight; - } else { - light = ambientLight; - } + // NOTE: Calls `_light_get_tile` twice. + light = std::max(ambientLight, _light_get_tile(elevation, objectListNode->obj->tile)); } while (objectListNode != NULL) { @@ -852,13 +849,8 @@ void _obj_render_pre_roof(Rect* rect, int elevation) ObjectListNode* objectListNode = _renderTable[i]; if (objectListNode != NULL) { - // NOTE: calls _light_get_tile two times, probably result of min/max macro - int tileLight = _light_get_tile(elevation, objectListNode->obj->tile); - if (tileLight >= ambientLight) { - light = tileLight; - } else { - light = ambientLight; - } + // NOTE: Calls `_light_get_tile` twice. + light = std::max(ambientLight, _light_get_tile(elevation, objectListNode->obj->tile)); } while (objectListNode != NULL) { @@ -1743,34 +1735,28 @@ void _obj_rebuild_all_light() // 0x48AC90 int objectSetLight(Object* obj, int lightDistance, int lightIntensity, Rect* rect) { - int v7; - Rect new_rect; - if (obj == NULL) { return -1; } - v7 = _obj_turn_off_light(obj, rect); + int rc = _obj_turn_off_light(obj, rect); if (lightIntensity > 0) { - if (lightDistance >= 8) { - lightDistance = 8; - } - + obj->lightDistance = std::min(lightDistance, 8); obj->lightIntensity = lightIntensity; - obj->lightDistance = lightDistance; if (rect != NULL) { - v7 = _obj_turn_on_light(obj, &new_rect); - rectUnion(rect, &new_rect, rect); + Rect tempRect; + rc = _obj_turn_on_light(obj, &tempRect); + rectUnion(rect, &tempRect, rect); } else { - v7 = _obj_turn_on_light(obj, NULL); + rc = _obj_turn_on_light(obj, NULL); } } else { obj->lightIntensity = 0; obj->lightDistance = 0; } - return v7; + return rc; } // 0x48AD04 diff --git a/src/text_object.cc b/src/text_object.cc index 4a068dc..8b5e303 100644 --- a/src/text_object.cc +++ b/src/text_object.cc @@ -206,11 +206,8 @@ int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rec char c = *ending; *ending = '\0'; - // NOTE: Calls [fontGetStringWidth] twice, probably result of using min/max macro - int width = fontGetStringWidth(beginning); - if (width >= textObject->width) { - textObject->width = width; - } + // NOTE: Calls `fontGetStringWidth` twice. + textObject->width = std::max(textObject->width, fontGetStringWidth(beginning)); *ending = c; } diff --git a/src/tile.cc b/src/tile.cc index e781c7d..c3a9e9b 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -4,6 +4,8 @@ #include #include +#include + #include "art.h" #include "color.h" #include "config.h" @@ -1660,13 +1662,8 @@ static void tileRenderFloor(int fid, int x, int y, Rect* rect) int parity = tile & 1; int ambientIntensity = lightGetLightLevel(); for (int i = 0; i < 10; i++) { - // NOTE: calling _light_get_tile two times, probably a result of using __min kind macro - int tileIntensity = _light_get_tile(elev, tile + _verticies[i].offsets[parity]); - if (tileIntensity <= ambientIntensity) { - tileIntensity = ambientIntensity; - } - - _verticies[i].intensity = tileIntensity; + // NOTE: Calls `_light_get_tile` twice. + _verticies[i].intensity = std::max(_light_get_tile(elev, tile + _verticies[i].offsets[parity]), ambientIntensity); } int v23 = 0; From 66955f893adba8af41ec9cc4e496806712a29f36 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 3 Jan 2023 23:56:52 +0300 Subject: [PATCH 07/50] Review light.cc --- src/combat_ai.cc | 4 +-- src/interpreter_extra.cc | 36 ++++++++++----------- src/inventory.cc | 2 +- src/item.cc | 2 +- src/light.cc | 67 +++++++++++++++++++++------------------- src/light.h | 22 +++++++------ src/map.cc | 2 +- src/object.cc | 42 ++++++++++++------------- src/tile.cc | 10 +++--- 9 files changed, 96 insertions(+), 91 deletions(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 5bb9275..84d3e1b 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -1745,7 +1745,7 @@ static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr) } if (weapon->pid == PROTO_ID_SOLAR_SCORCHER) { - return lightGetLightLevel() > 62259; + return lightGetAmbientIntensity() > LIGHT_INTENSITY_MAX * 0.95; } int inventoryItemIndex = -1; @@ -2879,7 +2879,7 @@ static int _ai_try_attack(Object* a1, Object* a2) // 0x42AE90 int _cAIPrepWeaponItem(Object* critter, Object* item) { - if (item != NULL && critterGetStat(critter, STAT_INTELLIGENCE) >= 3 && item->pid == PROTO_ID_FLARE && lightGetLightLevel() < 55705) { + if (item != NULL && critterGetStat(critter, STAT_INTELLIGENCE) >= 3 && item->pid == PROTO_ID_FLARE && lightGetAmbientIntensity() < LIGHT_INTENSITY_MAX * 0.85) { _protinst_use_item(critter, item); } return 0; diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index 8000f43..f790004 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -343,21 +343,6 @@ static void opGetPcStat(Program* program); // 0x504B0C static char _aCritter[] = ""; -// Maps light level to light intensity. -// -// Middle value is mapped one-to-one which corresponds to 50% light level -// (cavern lighting). Light levels above (51-100%) and below (0-49) is -// calculated as percentage from two adjacent light values. -// -// See [opSetLightLevel] for math. -// -// 0x453F90 -static const int dword_453F90[3] = { - 0x4000, - 0xA000, - 0x10000, -}; - // 0x453FC0 static Rect stru_453FC0 = { 0, 0, 640, 480 }; @@ -2234,23 +2219,36 @@ static void opCritterHeal(Program* program) // 0x457934 static void opSetLightLevel(Program* program) { + // Maps light level to light intensity. + // + // Middle value is mapped one-to-one which corresponds to 50% light level + // (cavern lighting). Light levels above (51-100%) and below (0-49%) is + // calculated as percentage from two adjacent light values. + // + // 0x453F90 + static const int intensities[3] = { + LIGHT_INTENSITY_MIN, + (LIGHT_INTENSITY_MIN + LIGHT_INTENSITY_MAX) / 2, + LIGHT_INTENSITY_MAX, + }; + int data = programStackPopInteger(program); int lightLevel = data; if (data == 50) { - lightSetLightLevel(dword_453F90[1], true); + lightSetAmbientIntensity(intensities[1], true); return; } int lightIntensity; if (data > 50) { - lightIntensity = dword_453F90[1] + data * (dword_453F90[2] - dword_453F90[1]) / 100; + lightIntensity = intensities[1] + data * (intensities[2] - intensities[1]) / 100; } else { - lightIntensity = dword_453F90[0] + data * (dword_453F90[1] - dword_453F90[0]) / 100; + lightIntensity = intensities[0] + data * (intensities[1] - intensities[0]) / 100; } - lightSetLightLevel(lightIntensity, true); + lightSetAmbientIntensity(lightIntensity, true); } // game_time diff --git a/src/inventory.cc b/src/inventory.cc index bf44d3c..2d1b832 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -3315,7 +3315,7 @@ int _invenWieldFunc(Object* critter, Object* item, int a3, bool a4) int lightIntensity; int lightDistance; if (critter == gDude) { - lightIntensity = LIGHT_LEVEL_MAX; + lightIntensity = LIGHT_INTENSITY_MAX; lightDistance = 4; } else { Proto* proto; diff --git a/src/item.cc b/src/item.cc index 4c363fe..9f29d36 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1494,7 +1494,7 @@ bool weaponCanBeReloadedWith(Object* weapon, Object* ammo) { if (weapon->pid == PROTO_ID_SOLAR_SCORCHER) { // Check light level to recharge solar scorcher. - if (lightGetLightLevel() > 62259) { + if (lightGetAmbientIntensity() > LIGHT_INTENSITY_MAX * 0.95) { return true; } diff --git a/src/light.cc b/src/light.cc index 1077b89..60bd80d 100644 --- a/src/light.cc +++ b/src/light.cc @@ -13,50 +13,55 @@ namespace fallout { #define LIGHT_LEVEL_NIGHT_VISION_BONUS (65536 / 5) // 0x51923C -static int gLightLevel = LIGHT_LEVEL_MAX; +static int gAmbientIntensity = LIGHT_INTENSITY_MAX; // light intensity per elevation per tile // 0x59E994 -static int gLightIntensity[ELEVATION_COUNT][HEX_GRID_SIZE]; +static int gTileIntensity[ELEVATION_COUNT][HEX_GRID_SIZE]; // 0x47A8F0 int lightInit() { - lightResetIntensity(); + lightResetTileIntensity(); return 0; } -// 0x47A8F8 -int lightGetLightLevel() +// 0x47A8F0 +void lightReset() { - return gLightLevel; + lightResetTileIntensity(); +} + +// 0x47A8F0 +void lightExit() +{ + lightResetTileIntensity(); +} + +// 0x47A8F8 +int lightGetAmbientIntensity() +{ + return gAmbientIntensity; } // 0x47A908 -void lightSetLightLevel(int lightLevel, bool shouldUpdateScreen) +void lightSetAmbientIntensity(int intensity, bool shouldUpdateScreen) { - int normalizedLightLevel = lightLevel + perkGetRank(gDude, PERK_NIGHT_VISION) * LIGHT_LEVEL_NIGHT_VISION_BONUS; + int adjustedIntensity = intensity + perkGetRank(gDude, PERK_NIGHT_VISION) * LIGHT_LEVEL_NIGHT_VISION_BONUS; + int normalizedIntensity = std::clamp(adjustedIntensity, LIGHT_INTENSITY_MIN, LIGHT_INTENSITY_MAX); - if (normalizedLightLevel < LIGHT_LEVEL_MIN) { - normalizedLightLevel = LIGHT_LEVEL_MIN; - } - - if (normalizedLightLevel > LIGHT_LEVEL_MAX) { - normalizedLightLevel = LIGHT_LEVEL_MAX; - } - - int oldLightLevel = gLightLevel; - gLightLevel = normalizedLightLevel; + int oldAmbientIntensity = gAmbientIntensity; + gAmbientIntensity = normalizedIntensity; if (shouldUpdateScreen) { - if (oldLightLevel != normalizedLightLevel) { + if (oldAmbientIntensity != normalizedIntensity) { tileWindowRefresh(); } } } // 0x47A980 -int _light_get_tile(int elevation, int tile) +int lightGetTileIntensity(int elevation, int tile) { if (!elevationIsValid(elevation)) { return 0; @@ -66,11 +71,11 @@ int _light_get_tile(int elevation, int tile) return 0; } - return std::min(gLightIntensity[elevation][tile], LIGHT_LEVEL_MAX); + return std::min(gTileIntensity[elevation][tile], LIGHT_INTENSITY_MAX); } // 0x47A9C4 -int lightGetIntensity(int elevation, int tile) +int lightGetTrueTileIntensity(int elevation, int tile) { if (!elevationIsValid(elevation)) { return 0; @@ -80,11 +85,11 @@ int lightGetIntensity(int elevation, int tile) return 0; } - return gLightIntensity[elevation][tile]; + return gTileIntensity[elevation][tile]; } // 0x47A9EC -void lightSetIntensity(int elevation, int tile, int lightIntensity) +void lightSetTileIntensity(int elevation, int tile, int intensity) { if (!elevationIsValid(elevation)) { return; @@ -94,11 +99,11 @@ void lightSetIntensity(int elevation, int tile, int lightIntensity) return; } - gLightIntensity[elevation][tile] = lightIntensity; + gTileIntensity[elevation][tile] = intensity; } // 0x47AA10 -void lightIncreaseIntensity(int elevation, int tile, int lightIntensity) +void lightIncreaseTileIntensity(int elevation, int tile, int intensity) { if (!elevationIsValid(elevation)) { return; @@ -108,11 +113,11 @@ void lightIncreaseIntensity(int elevation, int tile, int lightIntensity) return; } - gLightIntensity[elevation][tile] += lightIntensity; + gTileIntensity[elevation][tile] += intensity; } // 0x47AA48 -void lightDecreaseIntensity(int elevation, int tile, int lightIntensity) +void lightDecreaseTileIntensity(int elevation, int tile, int intensity) { if (!elevationIsValid(elevation)) { return; @@ -122,15 +127,15 @@ void lightDecreaseIntensity(int elevation, int tile, int lightIntensity) return; } - gLightIntensity[elevation][tile] -= lightIntensity; + gTileIntensity[elevation][tile] -= intensity; } // 0x47AA84 -void lightResetIntensity() +void lightResetTileIntensity() { for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { for (int tile = 0; tile < HEX_GRID_SIZE; tile++) { - gLightIntensity[elevation][tile] = 655; + gTileIntensity[elevation][tile] = 655; } } } diff --git a/src/light.h b/src/light.h index 414754e..d52930a 100644 --- a/src/light.h +++ b/src/light.h @@ -3,20 +3,22 @@ namespace fallout { -#define LIGHT_LEVEL_MIN (65536 / 4) -#define LIGHT_LEVEL_MAX 65536 +#define LIGHT_INTENSITY_MIN (65536 / 4) +#define LIGHT_INTENSITY_MAX 65536 typedef void AdjustLightIntensityProc(int elevation, int tile, int intensity); int lightInit(); -int lightGetLightLevel(); -void lightSetLightLevel(int lightLevel, bool shouldUpdateScreen); -int _light_get_tile(int elevation, int tile); -int lightGetIntensity(int elevation, int tile); -void lightSetIntensity(int elevation, int tile, int intensity); -void lightIncreaseIntensity(int elevation, int tile, int intensity); -void lightDecreaseIntensity(int elevation, int tile, int intensity); -void lightResetIntensity(); +void lightReset(); +void lightExit(); +int lightGetAmbientIntensity(); +void lightSetAmbientIntensity(int intensity, bool shouldUpdateScreen); +int lightGetTileIntensity(int elevation, int tile); +int lightGetTrueTileIntensity(int elevation, int tile); +void lightSetTileIntensity(int elevation, int tile, int intensity); +void lightIncreaseTileIntensity(int elevation, int tile, int intensity); +void lightDecreaseTileIntensity(int elevation, int tile, int intensity); +void lightResetTileIntensity(); } // namespace fallout diff --git a/src/map.cc b/src/map.cc index 5269ec7..4166e60 100644 --- a/src/map.cc +++ b/src/map.cc @@ -924,7 +924,7 @@ static int mapLoad(File* stream) goto err; } - lightSetLightLevel(LIGHT_LEVEL_MAX, false); + lightSetAmbientIntensity(LIGHT_INTENSITY_MAX, false); objectSetLocation(gDude, gCenterTile, gElevation, NULL); objectSetRotation(gDude, gEnteringRotation, NULL); gMapHeader.field_34 = wmMapMatchNameToIdx(gMapHeader.name); diff --git a/src/object.cc b/src/object.cc index 71861ac..0b3fb40 100644 --- a/src/object.cc +++ b/src/object.cc @@ -379,7 +379,7 @@ void objectsReset() textObjectsReset(); _obj_remove_all(); memset(_obj_seen, 0, 5001); - lightResetIntensity(); + lightReset(); } } @@ -396,7 +396,7 @@ void objectsExit() // NOTE: Uninline. _obj_blend_table_exit(); - lightResetIntensity(); + lightExit(); // NOTE: Uninline. _obj_render_table_exit(); @@ -763,7 +763,7 @@ void _obj_render_pre_roof(Rect* rect, int elevation) return; } - int ambientLight = lightGetLightLevel(); + int ambientIntensity = lightGetAmbientIntensity(); int minX = updatedRect.left - 320; int minY = updatedRect.top - 240; int maxX = updatedRect.right + 320; @@ -804,14 +804,14 @@ void _obj_render_pre_roof(Rect* rect, int elevation) for (int i = 0; i < gObjectsUpdateAreaHexSize; i++) { int offsetIndex = *orders++; if (updateAreaHexHeight > _offsetDivTable[offsetIndex] && updateAreaHexWidth > _offsetModTable[offsetIndex]) { - int light; + int lightIntensity; ObjectListNode* objectListNode = hexGridTileIsValid(topLeftTile + offsets[offsetIndex]) ? gObjectListHeadByTile[topLeftTile + offsets[offsetIndex]] : NULL; if (objectListNode != NULL) { - // NOTE: Calls `_light_get_tile` twice. - light = std::max(ambientLight, _light_get_tile(elevation, objectListNode->obj->tile)); + // NOTE: Calls `lightGetTileIntensity` twice. + lightIntensity = std::max(ambientIntensity, lightGetTileIntensity(elevation, objectListNode->obj->tile)); } while (objectListNode != NULL) { @@ -825,7 +825,7 @@ void _obj_render_pre_roof(Rect* rect, int elevation) } if ((objectListNode->obj->flags & OBJECT_HIDDEN) == 0) { - _obj_render_object(objectListNode->obj, &updatedRect, light); + _obj_render_object(objectListNode->obj, &updatedRect, lightIntensity); if ((objectListNode->obj->outline & OUTLINE_TYPE_MASK) != 0) { if ((objectListNode->obj->outline & OUTLINE_DISABLED) == 0 && _outlineCount < 100) { @@ -845,12 +845,12 @@ void _obj_render_pre_roof(Rect* rect, int elevation) } for (int i = 0; i < renderCount; i++) { - int light; + int lightIntensity; ObjectListNode* objectListNode = _renderTable[i]; if (objectListNode != NULL) { - // NOTE: Calls `_light_get_tile` twice. - light = std::max(ambientLight, _light_get_tile(elevation, objectListNode->obj->tile)); + // NOTE: Calls `lightGetTileIntensity` twice. + lightIntensity = std::max(ambientIntensity, lightGetTileIntensity(elevation, objectListNode->obj->tile)); } while (objectListNode != NULL) { @@ -861,7 +861,7 @@ void _obj_render_pre_roof(Rect* rect, int elevation) if (elevation == objectListNode->obj->elevation) { if ((objectListNode->obj->flags & OBJECT_HIDDEN) == 0) { - _obj_render_object(object, &updatedRect, light); + _obj_render_object(object, &updatedRect, lightIntensity); if ((objectListNode->obj->outline & OUTLINE_TYPE_MASK) != 0) { if ((objectListNode->obj->outline & OUTLINE_DISABLED) == 0 && _outlineCount < 100) { @@ -1721,7 +1721,7 @@ int objectRotateCounterClockwise(Object* obj, Rect* dirtyRect) // 0x48AC54 void _obj_rebuild_all_light() { - lightResetIntensity(); + lightResetTileIntensity(); for (int tile = 0; tile < HEX_GRID_SIZE; tile++) { ObjectListNode* objectListNode = gObjectListHeadByTile[tile]; @@ -1762,22 +1762,22 @@ int objectSetLight(Object* obj, int lightDistance, int lightIntensity, Rect* rec // 0x48AD04 int objectGetLightIntensity(Object* obj) { - int lightLevel = lightGetLightLevel(); - int lightIntensity = lightGetIntensity(obj->elevation, obj->tile); + int ambientIntensity = lightGetAmbientIntensity(); + int tileIntensity = lightGetTrueTileIntensity(obj->elevation, obj->tile); if (obj == gDude) { - lightIntensity -= gDude->lightIntensity; + tileIntensity -= gDude->lightIntensity; } - if (lightIntensity >= lightLevel) { - if (lightIntensity > 0x10000) { - lightIntensity = 0x10000; + if (tileIntensity >= ambientIntensity) { + if (tileIntensity > LIGHT_INTENSITY_MAX) { + tileIntensity = LIGHT_INTENSITY_MAX; } } else { - lightIntensity = lightLevel; + tileIntensity = ambientIntensity; } - return lightIntensity; + return tileIntensity; } // 0x48AD48 @@ -3993,7 +3993,7 @@ static int _obj_adjust_light(Object* obj, int a2, Rect* rect) return -1; } - AdjustLightIntensityProc* adjustLightIntensity = a2 ? lightDecreaseIntensity : lightIncreaseIntensity; + AdjustLightIntensityProc* adjustLightIntensity = a2 ? lightDecreaseTileIntensity : lightIncreaseTileIntensity; adjustLightIntensity(obj->elevation, obj->tile, obj->lightIntensity); Rect objectRect; diff --git a/src/tile.cc b/src/tile.cc index c3a9e9b..f52705a 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -1226,7 +1226,7 @@ void tileRenderRoofsInRect(Rect* rect, int elevation) minY = gSquareGridHeight - 1; } - int light = lightGetLightLevel(); + int light = lightGetAmbientIntensity(); int baseSquareTile = gSquareGridWidth * minY; @@ -1449,7 +1449,7 @@ void tileRenderFloorsInRect(Rect* rect, int elevation) minY = gSquareGridHeight - 1; } - lightGetLightLevel(); + lightGetAmbientIntensity(); int baseSquareTile = gSquareGridWidth * minY; @@ -1660,10 +1660,10 @@ static void tileRenderFloor(int fid, int x, int y, Rect* rect) tile = tileFromScreenXY(savedX, savedY + 13, gElevation); if (tile != -1) { int parity = tile & 1; - int ambientIntensity = lightGetLightLevel(); + int ambientIntensity = lightGetAmbientIntensity(); for (int i = 0; i < 10; i++) { - // NOTE: Calls `_light_get_tile` twice. - _verticies[i].intensity = std::max(_light_get_tile(elev, tile + _verticies[i].offsets[parity]), ambientIntensity); + // NOTE: Calls `lightGetTileIntensity` twice. + _verticies[i].intensity = std::max(lightGetTileIntensity(elev, tile + _verticies[i].offsets[parity]), ambientIntensity); } int v23 = 0; From 7496afa4f8900d61680da08612105f5fef73f12d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 04:06:18 +0300 Subject: [PATCH 08/50] Clarify textObjectAdd param --- src/text_object.cc | 10 +++++----- src/text_object.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/text_object.cc b/src/text_object.cc index 8b5e303..90dfdd8 100644 --- a/src/text_object.cc +++ b/src/text_object.cc @@ -153,7 +153,7 @@ void textObjectsSetLineDelay(double value) // text_object_create // 0x4B036C -int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rect* rect) +int textObjectAdd(Object* object, char* string, int font, int color, int outlineColor, Rect* rect) { if (!gTextObjectsInitialized) { return -1; @@ -214,7 +214,7 @@ int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rec textObject->height = (fontGetLineHeight() + 1) * textObject->linesCount; - if (a5 != -1) { + if (outlineColor != -1) { textObject->width += 2; textObject->height += 2; } @@ -231,7 +231,7 @@ int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rec unsigned char* dest = textObject->data; int skip = textObject->width * (fontGetLineHeight() + 1); - if (a5 != -1) { + if (outlineColor != -1) { dest += textObject->width; } @@ -253,8 +253,8 @@ int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rec dest += skip; } - if (a5 != -1) { - bufferOutline(textObject->data, textObject->width, textObject->height, textObject->width, a5); + if (outlineColor != -1) { + bufferOutline(textObject->data, textObject->width, textObject->height, textObject->width, outlineColor); } if (object != NULL) { diff --git a/src/text_object.h b/src/text_object.h index 08f3b84..17c4e34 100644 --- a/src/text_object.h +++ b/src/text_object.h @@ -13,7 +13,7 @@ void textObjectsDisable(); void textObjectsEnable(); void textObjectsSetBaseDelay(double value); void textObjectsSetLineDelay(double value); -int textObjectAdd(Object* object, char* string, int font, int color, int a5, Rect* rect); +int textObjectAdd(Object* object, char* string, int font, int color, int outlineColor, Rect* rect); void textObjectsRenderInRect(Rect* rect); int textObjectsGetCount(); void textObjectsRemoveByOwner(Object* object); From ac64fde502f52f830dba80b6f4ba7452390735ec Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 05:59:45 +0300 Subject: [PATCH 09/50] Fix object rendering Previous solution did not work well on high resolutions due to incorrect tile calculations - was not updating edges and sometimes hanged in endless loop trying to find upper-left or bottom-right tiles. New solution follows Sfall's HRP implementation. --- src/object.cc | 49 ++++++++++++++----------------------------------- src/tile.cc | 11 ++++++++++- src/tile.h | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/object.cc b/src/object.cc index 0b3fb40..60b3bd3 100644 --- a/src/object.cc +++ b/src/object.cc @@ -768,47 +768,23 @@ void _obj_render_pre_roof(Rect* rect, int elevation) int minY = updatedRect.top - 240; int maxX = updatedRect.right + 320; int maxY = updatedRect.bottom + 240; - int topLeftTile = tileFromScreenXY(minX, minY, elevation); + int upperLeftTile = tileFromScreenXY(minX, minY, elevation, true); int updateAreaHexWidth = (maxX - minX + 1) / 32; int updateAreaHexHeight = (maxY - minY + 1) / 12; - - // On some maps (which were designed too close to edges) HRP brings a new - // problem - extended update rect (+/- 320/240 stuff above) may end up - // outside of the map edge. In this case `topLeftTile` will be -1 which - // affect all subsequent calculations. In order to fix that attempt to - // find closest valid tile. - while (!hexGridTileIsValid(topLeftTile)) { - minX += 32; - minY += 12; - topLeftTile = tileFromScreenXY(minX, minY, elevation); - } - - // Do the same for the for bottom-right part of the extended update rect. - int bottomRightTile = tileFromScreenXY(maxX, maxY, elevation); - while (!hexGridTileIsValid(bottomRightTile)) { - maxX -= 32; - maxY -= 12; - bottomRightTile = tileFromScreenXY(maxX, maxY, elevation); - } - - updateAreaHexWidth = (maxX - minX + 1) / 32; - updateAreaHexHeight = (maxY - minY + 1) / 12; - int parity = gCenterTile & 1; - int* orders = _orderTable[parity]; - int* offsets = _offsetTable[parity]; _outlineCount = 0; int renderCount = 0; for (int i = 0; i < gObjectsUpdateAreaHexSize; i++) { - int offsetIndex = *orders++; + int offsetIndex = _orderTable[parity][i]; if (updateAreaHexHeight > _offsetDivTable[offsetIndex] && updateAreaHexWidth > _offsetModTable[offsetIndex]) { - int lightIntensity; - - ObjectListNode* objectListNode = hexGridTileIsValid(topLeftTile + offsets[offsetIndex]) - ? gObjectListHeadByTile[topLeftTile + offsets[offsetIndex]] + int tile = upperLeftTile + _offsetTable[parity][offsetIndex]; + ObjectListNode* objectListNode = hexGridTileIsValid(tile) + ? gObjectListHeadByTile[tile] : NULL; + + int lightIntensity; if (objectListNode != NULL) { // NOTE: Calls `lightGetTileIntensity` twice. lightIntensity = std::max(ambientIntensity, lightGetTileIntensity(elevation, objectListNode->obj->tile)); @@ -2995,7 +2971,7 @@ int _obj_intersects_with(Object* object, int x, int y) // 0x48C5C4 int _obj_create_intersect_list(int x, int y, int elevation, int objectType, ObjectWithFlags** entriesPtr) { - int v5 = tileFromScreenXY(x - 320, y - 240, elevation); + int upperLeftTile = tileFromScreenXY(x - 320, y - 240, elevation, true); *entriesPtr = NULL; if (gObjectsUpdateAreaHexSize <= 0) { @@ -3006,9 +2982,12 @@ int _obj_create_intersect_list(int x, int y, int elevation, int objectType, Obje int parity = gCenterTile & 1; for (int index = 0; index < gObjectsUpdateAreaHexSize; index++) { - int v7 = _orderTable[parity][index]; - if (_offsetDivTable[v7] < 30 && _offsetModTable[v7] < 20) { - ObjectListNode* objectListNode = gObjectListHeadByTile[_offsetTable[parity][v7] + v5]; + int offsetIndex = _orderTable[parity][index]; + if (_offsetDivTable[offsetIndex] < 30 && _offsetModTable[offsetIndex] < 20) { + int tile = _offsetTable[parity][offsetIndex] + upperLeftTile; + ObjectListNode* objectListNode = hexGridTileIsValid(tile) + ? gObjectListHeadByTile[tile] + : NULL; while (objectListNode != NULL) { Object* object = objectListNode->obj; if (object->elevation > elevation) { diff --git a/src/tile.cc b/src/tile.cc index f52705a..a7e98aa 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -692,8 +692,13 @@ int tileToScreenXY(int tile, int* screenX, int* screenY, int elevation) return 0; } +// CE: Added optional `ignoreBounds` param to return tile number without +// validating hex grid bounds. The resulting invalid tile number serves as an +// origin for calculations using prepared offsets table during objects +// rendering. +// // 0x4B1754 -int tileFromScreenXY(int screenX, int screenY, int elevation) +int tileFromScreenXY(int screenX, int screenY, int elevation, bool ignoreBounds) { int v2; int v3; @@ -763,6 +768,10 @@ int tileFromScreenXY(int screenX, int screenY, int elevation) return gHexGridWidth * v10 + v12; } + if (ignoreBounds) { + return gHexGridWidth * v10 + v12; + } + return -1; } diff --git a/src/tile.h b/src/tile.h index 398e777..ae227d2 100644 --- a/src/tile.h +++ b/src/tile.h @@ -28,7 +28,7 @@ void tileWindowRefresh(); int tileSetCenter(int tile, int flags); int tileRoofIsVisible(); int tileToScreenXY(int tile, int* x, int* y, int elevation); -int tileFromScreenXY(int x, int y, int elevation); +int tileFromScreenXY(int x, int y, int elevation, bool ignoreBounds = false); int tileDistanceBetween(int a1, int a2); bool tileIsInFrontOf(int tile1, int tile2); bool tileIsToRightOf(int tile1, int tile2); From 060c79fc208fdd3dd25f1d0f9219806b9afdb749 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 10:27:16 +0300 Subject: [PATCH 10/50] Remove electronic registration --- CMakeLists.txt | 2 -- src/electronic_registration.cc | 50 ---------------------------------- src/electronic_registration.h | 10 ------- src/game.cc | 2 -- 4 files changed, 64 deletions(-) delete mode 100644 src/electronic_registration.cc delete mode 100644 src/electronic_registration.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ca89118..0ea1be8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,8 +83,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/display_monitor.h" "src/draw.cc" "src/draw.h" - "src/electronic_registration.cc" - "src/electronic_registration.h" "src/elevator.cc" "src/elevator.h" "src/endgame.cc" diff --git a/src/electronic_registration.cc b/src/electronic_registration.cc deleted file mode 100644 index 9584db6..0000000 --- a/src/electronic_registration.cc +++ /dev/null @@ -1,50 +0,0 @@ -#include "electronic_registration.h" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include "platform_compat.h" -#include "settings.h" - -namespace fallout { - -// 0x440DD0 -void runElectronicRegistration() -{ - int timesRun = settings.system.times_run; - if (timesRun > 0 && timesRun < 5) { -#ifdef _WIN32 - char path[COMPAT_MAX_PATH]; - if (GetModuleFileNameA(NULL, path, sizeof(path)) != 0) { - char* pch = strrchr(path, '\\'); - if (pch == NULL) { - pch = path; - } - - strcpy(pch, "\\ereg"); - - STARTUPINFOA startupInfo; - memset(&startupInfo, 0, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); - - PROCESS_INFORMATION processInfo; - - // FIXME: Leaking processInfo.hProcess and processInfo.hThread: - // https://docs.microsoft.com/en-us/cpp/code-quality/c6335. - if (CreateProcessA("ereg\\reg32a.exe", NULL, NULL, NULL, FALSE, 0, NULL, path, &startupInfo, &processInfo)) { - WaitForSingleObject(processInfo.hProcess, INFINITE); - } - } -#endif - - settings.system.times_run = timesRun + 1; - } else { - if (timesRun == 0) { - settings.system.times_run = 1; - } - } -} - -} // namespace fallout diff --git a/src/electronic_registration.h b/src/electronic_registration.h deleted file mode 100644 index 90dd5bc..0000000 --- a/src/electronic_registration.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef ELECTRONIC_REGISTRATION_H -#define ELECTRONIC_REGISTRATION_H - -namespace fallout { - -void runElectronicRegistration(); - -} // namespace fallout - -#endif /* ELECTRONIC_REGISTRATION_H */ diff --git a/src/game.cc b/src/game.cc index 60099ac..9fca014 100644 --- a/src/game.cc +++ b/src/game.cc @@ -25,7 +25,6 @@ #include "debug.h" #include "display_monitor.h" #include "draw.h" -#include "electronic_registration.h" #include "endgame.h" #include "font_manager.h" #include "game_dialog.h" @@ -155,7 +154,6 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 // it should be initialized early in the process. messageListRepositoryInit(); - runElectronicRegistration(); programWindowSetTitle(windowTitle); _initWindow(1, a4); paletteInit(); From 6ab08bd22b4cd53388dce63c6e4b4786bd01048b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 10:39:46 +0300 Subject: [PATCH 11/50] Remove some unused db functions --- src/art.cc | 55 ---------------------------------------------- src/db.cc | 28 ----------------------- src/db.h | 5 ----- src/game.cc | 27 ++++------------------- src/game.h | 2 -- src/game_memory.cc | 1 - 6 files changed, 4 insertions(+), 114 deletions(-) diff --git a/src/art.cc b/src/art.cc index 75103ed..3955489 100644 --- a/src/art.cc +++ b/src/art.cc @@ -149,26 +149,11 @@ int artInit() gArtListDescriptions[objectType].flags = 0; snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name); - int oldDb; - if (objectType == OBJ_TYPE_CRITTER) { - oldDb = _db_current(); - critterDbSelected = true; - _db_select(_critter_db_handle); - } - if (artReadList(path, &(gArtListDescriptions[objectType].fileNames), &(gArtListDescriptions[objectType].fileNamesLength)) != 0) { debugPrint("art_read_lst failed in art_init\n"); - if (critterDbSelected) { - _db_select(oldDb); - } cacheFree(&gArtCache); return -1; } - - if (objectType == OBJ_TYPE_CRITTER) { - critterDbSelected = false; - _db_select(oldDb); - } } _anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength); @@ -859,12 +844,6 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation) bool artExists(int fid) { bool result = false; - int oldDb = -1; - - if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(); - _db_select(_critter_db_handle); - } char* filePath = artBuildFilePath(fid); if (filePath != NULL) { @@ -874,10 +853,6 @@ bool artExists(int fid) } } - if (oldDb != -1) { - _db_select(oldDb); - } - return result; } @@ -887,12 +862,6 @@ bool artExists(int fid) bool _art_fid_valid(int fid) { bool result = false; - int oldDb = -1; - - if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(); - _db_select(_critter_db_handle); - } char* filePath = artBuildFilePath(fid); if (filePath != NULL) { @@ -902,10 +871,6 @@ bool _art_fid_valid(int fid) } } - if (oldDb != -1) { - _db_select(oldDb); - } - return result; } @@ -952,14 +917,8 @@ int artAliasFid(int fid) // 0x419A78 static int artCacheGetFileSizeImpl(int fid, int* sizePtr) { - int oldDb = -1; int result = -1; - if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(); - _db_select(_critter_db_handle); - } - char* artFilePath = artBuildFilePath(fid); if (artFilePath != NULL) { int fileSize; @@ -991,24 +950,14 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr) } } - if (oldDb != -1) { - _db_select(oldDb); - } - return result; } // 0x419B78 static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data) { - int oldDb = -1; int result = -1; - if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(); - _db_select(_critter_db_handle); - } - char* artFileName = artBuildFilePath(fid); if (artFileName != NULL) { bool loaded = false; @@ -1039,10 +988,6 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data) } } - if (oldDb != -1) { - _db_select(oldDb); - } - return result; } diff --git a/src/db.cc b/src/db.cc index 09bf09a..e97763e 100644 --- a/src/db.cc +++ b/src/db.cc @@ -63,18 +63,6 @@ int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4) return 0; } -// 0x4C5D54 -int _db_select(int dbHandle) -{ - return 0; -} - -// NOTE: Uncollapsed 0x4C5D54. -int _db_current() -{ - return 0; -} - // 0x4C5D58 int _db_total() { @@ -700,14 +688,6 @@ void fileNameListFree(char*** fileNameListPtr, int a2) free(currentFileList); } -// NOTE: This function does nothing. It was probably used to set memory procs -// for building file name list. -// -// 0x4C68B8 -void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc) -{ -} - // TODO: Return type should be long. // // 0x4C68BC @@ -728,14 +708,6 @@ void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size) } } -// NOTE: This function is called when fallout2.cfg has "hashing" enabled, but -// it does nothing. It's impossible to guess it's name. -// -// 0x4C68E4 -void _db_enable_hash_table_() -{ -} - // 0x4C68E8 int _db_list_compare(const void* p1, const void* p2) { diff --git a/src/db.h b/src/db.h index d41d676..b8fbe18 100644 --- a/src/db.h +++ b/src/db.h @@ -3,7 +3,6 @@ #include -#include "memory_defs.h" #include "xfile.h" namespace fallout { @@ -13,8 +12,6 @@ typedef void FileReadProgressHandler(); typedef char* StrdupProc(const char* string); int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4); -int _db_select(int dbHandle); -int _db_current(); int _db_total(); void dbExit(); int dbGetFileSize(const char* filePath, int* sizePtr); @@ -63,10 +60,8 @@ int _db_fwriteLongCount(File* stream, int* arr, int count); int fileWriteUInt32List(File* stream, unsigned int* arr, int count); int fileNameListInit(const char* pattern, char*** fileNames, int a3, int a4); void fileNameListFree(char*** fileNames, int a2); -void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc); int fileGetSize(File* stream); void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size); -void _db_enable_hash_table_(); } // namespace fallout diff --git a/src/game.cc b/src/game.cc index 9fca014..277e34f 100644 --- a/src/game.cc +++ b/src/game.cc @@ -117,16 +117,6 @@ int _game_user_wants_to_quit = 0; // 0x58E940 MessageList gMiscMessageList; -// master.dat loading result -// -// 0x58E948 -int _master_db_handle; - -// critter.dat loading result -// -// 0x58E94C -int _critter_db_handle; - // 0x442580 int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4, int argc, char** argv) { @@ -1283,20 +1273,14 @@ int showQuitConfirmationDialog() // 0x44418C static int gameDbInit() { - int hashing; const char* main_file_name; const char* patch_file_name; int patch_index; char filename[COMPAT_MAX_PATH]; - hashing = 0; main_file_name = NULL; patch_file_name = NULL; - if (settings.system.hashing) { - _db_enable_hash_table_(); - } - main_file_name = settings.system.master_dat_path.c_str(); if (*main_file_name == '\0') { main_file_name = NULL; @@ -1307,8 +1291,8 @@ static int gameDbInit() patch_file_name = NULL; } - _master_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1); - if (_master_db_handle == -1) { + int master_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1); + if (master_db_handle == -1) { showMesageBox("Could not find the master datafile. Please make sure the FALLOUT CD is in the drive and that you are running FALLOUT from the directory you installed it to."); return -1; } @@ -1323,9 +1307,8 @@ static int gameDbInit() patch_file_name = NULL; } - _critter_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1); - if (_critter_db_handle == -1) { - _db_select(_master_db_handle); + int critter_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1); + if (critter_db_handle == -1) { showMesageBox("Could not find the critter datafile. Please make sure the FALLOUT CD is in the drive and that you are running FALLOUT from the directory you installed it to."); return -1; } @@ -1338,8 +1321,6 @@ static int gameDbInit() } } - _db_select(_master_db_handle); - return 0; } diff --git a/src/game.h b/src/game.h index e068c52..4ec43f5 100644 --- a/src/game.h +++ b/src/game.h @@ -21,8 +21,6 @@ extern const char* asc_5186C8; extern int _game_user_wants_to_quit; extern MessageList gMiscMessageList; -extern int _master_db_handle; -extern int _critter_db_handle; int gameInitWithOptions(const char* windowTitle, bool isMapper, int a3, int a4, int argc, char** argv); void gameReset(); diff --git a/src/game_memory.cc b/src/game_memory.cc index c88c60d..635995e 100644 --- a/src/game_memory.cc +++ b/src/game_memory.cc @@ -16,7 +16,6 @@ static void gameMemoryFree(void* ptr); int gameMemoryInit() { dictionarySetMemoryProcs(internal_malloc, internal_realloc, internal_free); - _db_register_mem(internal_malloc, internal_strdup, internal_free); memoryManagerSetProcs(gameMemoryMalloc, gameMemoryRealloc, gameMemoryFree); return 0; From bffe81d0b3cc89eb2cbe1d38ebc615ff56fca131 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 5 Jan 2023 11:29:00 +0300 Subject: [PATCH 12/50] Improve cycle.cc readability --- src/cycle.cc | 180 +++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/src/cycle.cc b/src/cycle.cc index 3b93376..5610203 100644 --- a/src/cycle.cc +++ b/src/cycle.cc @@ -8,10 +8,10 @@ namespace fallout { -#define COLOR_CYCLE_PERIOD_1 (200U) -#define COLOR_CYCLE_PERIOD_2 (142U) -#define COLOR_CYCLE_PERIOD_3 (100U) -#define COLOR_CYCLE_PERIOD_4 (33U) +static constexpr unsigned int kSlowCyclePeriod = 1000 / 5; +static constexpr unsigned int kMediumCyclePeriod = 1000 / 7; +static constexpr unsigned int kFastCyclePeriod = 1000 / 10; +static constexpr unsigned int kVeryFastCyclePeriod = 1000 / 30; // 0x51843C static int gColorCycleSpeedFactor = 1; @@ -22,7 +22,7 @@ static int gColorCycleSpeedFactor = 1; // Green. // // 0x518440 -static unsigned char _slime[12] = { +static unsigned char slime[12] = { 0, 108, 0, 11, 115, 7, 27, 123, 15, @@ -32,7 +32,7 @@ static unsigned char _slime[12] = { // Light gray? // // 0x51844C -static unsigned char _shoreline[18] = { +static unsigned char shoreline[18] = { 83, 63, 43, 75, 59, 43, 67, 55, 39, @@ -44,7 +44,7 @@ static unsigned char _shoreline[18] = { // Orange. // // 0x51845E -static unsigned char _fire_slow[15] = { +static unsigned char fire_slow[15] = { 255, 0, 0, 215, 0, 0, 147, 43, 11, @@ -55,7 +55,7 @@ static unsigned char _fire_slow[15] = { // Red. // // 0x51846D -static unsigned char _fire_fast[15] = { +static unsigned char fire_fast[15] = { 71, 0, 0, 123, 0, 0, 179, 0, 0, @@ -66,7 +66,7 @@ static unsigned char _fire_fast[15] = { // Light blue. // // 0x51847C -static unsigned char _monitors[15] = { +static unsigned char monitors[15] = { 107, 107, 111, 99, 103, 127, 87, 107, 143, @@ -82,38 +82,17 @@ static bool gColorCycleInitialized = false; // 0x518490 static bool gColorCycleEnabled = false; -// 0x518494 -static int _slime_start = 0; - -// 0x518498 -static int _shoreline_start = 0; - -// 0x51849C -static int _fire_slow_start = 0; - -// 0x5184A0 -static int _fire_fast_start = 0; - -// 0x5184A4 -static int _monitors_start = 0; - -// 0x5184A8 -static unsigned char _bobber_red = 0; - -// 0x5184A9 -static signed char _bobber_diff = -4; - // 0x56D7D0 -static unsigned int gColorCycleTimestamp3; +static unsigned int last_cycle_fast; // 0x56D7D4 -static unsigned int gColorCycleTimestamp1; +static unsigned int last_cycle_slow; // 0x56D7D8 -static unsigned int gColorCycleTimestamp2; +static unsigned int last_cycle_medium; // 0x56D7DC -static unsigned int gColorCycleTimestamp4; +static unsigned int last_cycle_very_fast; // 0x42E780 void colorCycleInit() @@ -127,23 +106,23 @@ void colorCycleInit() } for (int index = 0; index < 12; index++) { - _slime[index] >>= 2; + slime[index] >>= 2; } for (int index = 0; index < 18; index++) { - _shoreline[index] >>= 2; + shoreline[index] >>= 2; } for (int index = 0; index < 15; index++) { - _fire_slow[index] >>= 2; + fire_slow[index] >>= 2; } for (int index = 0; index < 15; index++) { - _fire_fast[index] >>= 2; + fire_fast[index] >>= 2; } for (int index = 0; index < 15; index++) { - _monitors[index] >>= 2; + monitors[index] >>= 2; } tickersAdd(colorCycleTicker); @@ -158,10 +137,10 @@ void colorCycleInit() void colorCycleReset() { if (gColorCycleInitialized) { - gColorCycleTimestamp1 = 0; - gColorCycleTimestamp2 = 0; - gColorCycleTimestamp3 = 0; - gColorCycleTimestamp4 = 0; + last_cycle_slow = 0; + last_cycle_medium = 0; + last_cycle_fast = 0; + last_cycle_very_fast = 0; tickersAdd(colorCycleTicker); gColorCycleEnabled = true; } @@ -205,6 +184,27 @@ void cycleSetSpeedFactor(int value) // 0x42E97C void colorCycleTicker() { + // 0x518494 + static int slime_start = 0; + + // 0x518498 + static int shoreline_start = 0; + + // 0x51849C + static int fire_slow_start = 0; + + // 0x5184A0 + static int fire_fast_start = 0; + + // 0x5184A4 + static int monitors_start = 0; + + // 0x5184A8 + static unsigned char bobber_red = 0; + + // 0x5184A9 + static signed char bobber_diff = -4; + if (!gColorCycleEnabled) { return; } @@ -214,109 +214,109 @@ void colorCycleTicker() unsigned char* palette = _getSystemPalette(); unsigned int time = getTicks(); - if (getTicksBetween(time, gColorCycleTimestamp1) >= COLOR_CYCLE_PERIOD_1 * gColorCycleSpeedFactor) { + if (getTicksBetween(time, last_cycle_slow) >= kSlowCyclePeriod * gColorCycleSpeedFactor) { changed = true; - gColorCycleTimestamp1 = time; + last_cycle_slow = time; int paletteIndex = 229 * 3; - for (int index = _slime_start; index < 12; index++) { - palette[paletteIndex++] = _slime[index]; + for (int index = slime_start; index < 12; index++) { + palette[paletteIndex++] = slime[index]; } - for (int index = 0; index < _slime_start; index++) { - palette[paletteIndex++] = _slime[index]; + for (int index = 0; index < slime_start; index++) { + palette[paletteIndex++] = slime[index]; } - _slime_start -= 3; - if (_slime_start < 0) { - _slime_start = 9; + slime_start -= 3; + if (slime_start < 0) { + slime_start = 9; } paletteIndex = 248 * 3; - for (int index = _shoreline_start; index < 18; index++) { - palette[paletteIndex++] = _shoreline[index]; + for (int index = shoreline_start; index < 18; index++) { + palette[paletteIndex++] = shoreline[index]; } - for (int index = 0; index < _shoreline_start; index++) { - palette[paletteIndex++] = _shoreline[index]; + for (int index = 0; index < shoreline_start; index++) { + palette[paletteIndex++] = shoreline[index]; } - _shoreline_start -= 3; - if (_shoreline_start < 0) { - _shoreline_start = 15; + shoreline_start -= 3; + if (shoreline_start < 0) { + shoreline_start = 15; } paletteIndex = 238 * 3; - for (int index = _fire_slow_start; index < 15; index++) { - palette[paletteIndex++] = _fire_slow[index]; + for (int index = fire_slow_start; index < 15; index++) { + palette[paletteIndex++] = fire_slow[index]; } - for (int index = 0; index < _fire_slow_start; index++) { - palette[paletteIndex++] = _fire_slow[index]; + for (int index = 0; index < fire_slow_start; index++) { + palette[paletteIndex++] = fire_slow[index]; } - _fire_slow_start -= 3; - if (_fire_slow_start < 0) { - _fire_slow_start = 12; + fire_slow_start -= 3; + if (fire_slow_start < 0) { + fire_slow_start = 12; } } - if (getTicksBetween(time, gColorCycleTimestamp2) >= COLOR_CYCLE_PERIOD_2 * gColorCycleSpeedFactor) { + if (getTicksBetween(time, last_cycle_medium) >= kMediumCyclePeriod * gColorCycleSpeedFactor) { changed = true; - gColorCycleTimestamp2 = time; + last_cycle_medium = time; int paletteIndex = 243 * 3; - for (int index = _fire_fast_start; index < 15; index++) { - palette[paletteIndex++] = _fire_fast[index]; + for (int index = fire_fast_start; index < 15; index++) { + palette[paletteIndex++] = fire_fast[index]; } - for (int index = 0; index < _fire_fast_start; index++) { - palette[paletteIndex++] = _fire_fast[index]; + for (int index = 0; index < fire_fast_start; index++) { + palette[paletteIndex++] = fire_fast[index]; } - _fire_fast_start -= 3; - if (_fire_fast_start < 0) { - _fire_fast_start = 12; + fire_fast_start -= 3; + if (fire_fast_start < 0) { + fire_fast_start = 12; } } - if (getTicksBetween(time, gColorCycleTimestamp3) >= COLOR_CYCLE_PERIOD_3 * gColorCycleSpeedFactor) { + if (getTicksBetween(time, last_cycle_fast) >= kFastCyclePeriod * gColorCycleSpeedFactor) { changed = true; - gColorCycleTimestamp3 = time; + last_cycle_fast = time; int paletteIndex = 233 * 3; - for (int index = _monitors_start; index < 15; index++) { - palette[paletteIndex++] = _monitors[index]; + for (int index = monitors_start; index < 15; index++) { + palette[paletteIndex++] = monitors[index]; } - for (int index = 0; index < _monitors_start; index++) { - palette[paletteIndex++] = _monitors[index]; + for (int index = 0; index < monitors_start; index++) { + palette[paletteIndex++] = monitors[index]; } - _monitors_start -= 3; + monitors_start -= 3; - if (_monitors_start < 0) { - _monitors_start = 12; + if (monitors_start < 0) { + monitors_start = 12; } } - if (getTicksBetween(time, gColorCycleTimestamp4) >= COLOR_CYCLE_PERIOD_4 * gColorCycleSpeedFactor) { + if (getTicksBetween(time, last_cycle_very_fast) >= kVeryFastCyclePeriod * gColorCycleSpeedFactor) { changed = true; - gColorCycleTimestamp4 = time; + last_cycle_very_fast = time; - if (_bobber_red == 0 || _bobber_red == 60) { - _bobber_diff = -_bobber_diff; + if (bobber_red == 0 || bobber_red == 60) { + bobber_diff = -bobber_diff; } - _bobber_red += _bobber_diff; + bobber_red += bobber_diff; int paletteIndex = 254 * 3; - palette[paletteIndex++] = _bobber_red; + palette[paletteIndex++] = bobber_red; palette[paletteIndex++] = 0; palette[paletteIndex++] = 0; } From ad3860790c42c859c6a8ac94e86319d1436f9629 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 16 Jan 2023 16:42:50 +0300 Subject: [PATCH 13/50] Fix NPCs not joining combat --- src/combat_ai.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 84d3e1b..1a55d27 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -3167,11 +3167,11 @@ bool _combatai_want_to_join(Object* a1) return true; } - if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_STOP_ATTACKING) == 0) { + if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_STOP_ATTACKING) != 0) { return false; } - if ((a1->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) == 0) { + if ((a1->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0) { return false; } From 6cac53d20bf9b5a7aa3ec889b3d6102fc424535c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 16 Jan 2023 16:49:50 +0300 Subject: [PATCH 14/50] Rename critter maneuver flags --- src/combat.cc | 6 +++--- src/combat_ai.cc | 20 ++++++++++---------- src/critter.cc | 2 +- src/interpreter_extra.cc | 12 ++++++------ src/obj_types.h | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/combat.cc b/src/combat.cc index 2b624cc..2daecf6 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -2581,7 +2581,7 @@ static void _combat_begin(Object* a1) for (int index = 0; index < _list_total; index++) { Object* critter = _combat_list[index]; CritterCombatData* combatData = &(critter->data.critter.combat); - combatData->maneuver &= CRITTER_MANEUVER_0x01; + combatData->maneuver &= CRITTER_MANEUVER_ENGAGING; combatData->damageLastTurn = 0; combatData->whoHitMe = NULL; combatData->ap = 0; @@ -3032,8 +3032,8 @@ static void _combat_sequence() Object* critter = _combat_list[index]; if (critter != gDude) { if ((critter->data.critter.combat.results & DAM_KNOCKED_OUT) != 0 - || critter->data.critter.combat.maneuver == CRITTER_MANEUVER_STOP_ATTACKING) { - critter->data.critter.combat.maneuver &= ~CRITTER_MANEUVER_0x01; + || critter->data.critter.combat.maneuver == CRITTER_MANEUVER_DISENGAGING) { + critter->data.critter.combat.maneuver &= ~CRITTER_MANEUVER_ENGAGING; _list_noncom += 1; _combat_list[index] = _combat_list[count - 1]; diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 1a55d27..0b2940d 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -1189,7 +1189,7 @@ static void _ai_run_away(Object* a1, Object* a2) } } } else { - combatData->maneuver |= CRITTER_MANEUVER_STOP_ATTACKING; + combatData->maneuver |= CRITTER_MANEUVER_DISENGAGING; } } @@ -1686,7 +1686,7 @@ int _caiSetupTeamCombat(Object* attackerTeam, Object* defenderTeam) Object* obj = objectFindFirstAtElevation(attackerTeam->elevation); while (obj != NULL) { if (PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER && obj != gDude) { - obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01; + obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; } obj = objectFindNextAtElevation(); } @@ -3081,7 +3081,7 @@ void _combat_ai(Object* a1, Object* a2) } else { int perception = critterGetStat(a1, STAT_PERCEPTION); if (!_ai_find_friend(a1, perception * 2, 5)) { - combatData->maneuver |= CRITTER_MANEUVER_STOP_ATTACKING; + combatData->maneuver |= CRITTER_MANEUVER_DISENGAGING; } } } @@ -3163,11 +3163,11 @@ bool _combatai_want_to_join(Object* a1) scriptExecProc(a1->sid, SCRIPT_PROC_COMBAT); } - if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) != 0) { + if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_ENGAGING) != 0) { return true; } - if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_STOP_ATTACKING) != 0) { + if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_DISENGAGING) != 0) { return false; } @@ -3187,7 +3187,7 @@ bool _combatai_want_to_stop(Object* a1) { _process_bk(); - if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_STOP_ATTACKING) != 0) { + if ((a1->data.critter.combat.maneuver & CRITTER_MANEUVER_DISENGAGING) != 0) { return true; } @@ -3589,9 +3589,9 @@ void _combatai_notify_onlookers(Object* a1) { for (int index = 0; index < _curr_crit_num; index++) { Object* obj = _curr_crit_list[index]; - if ((obj->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) == 0) { + if ((obj->data.critter.combat.maneuver & CRITTER_MANEUVER_ENGAGING) == 0) { if (isWithinPerception(obj, a1)) { - obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01; + obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; if ((a1->data.critter.combat.results & DAM_DEAD) != 0) { if (!isWithinPerception(obj, obj->data.critter.combat.whoHitMe)) { debugPrint("\nSomebody Died and I don't know why! Run!!!"); @@ -3610,9 +3610,9 @@ void _combatai_notify_friends(Object* a1) for (int index = 0; index < _curr_crit_num; index++) { Object* obj = _curr_crit_list[index]; - if ((obj->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) == 0 && team == obj->data.critter.combat.team) { + if ((obj->data.critter.combat.maneuver & CRITTER_MANEUVER_ENGAGING) == 0 && team == obj->data.critter.combat.team) { if (isWithinPerception(obj, a1)) { - obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01; + obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; } } } diff --git a/src/critter.cc b/src/critter.cc index 7b3b04a..cd896ab 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -1254,7 +1254,7 @@ int knockoutEventProcess(Object* obj, void* data) obj->data.critter.combat.results |= DAM_KNOCKED_DOWN; if (isInCombat()) { - obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01; + obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; } else { _dude_standup(obj); } diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index f790004..3a2d929 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -1844,8 +1844,8 @@ static void opAttackComplex(Program* program) if (isInCombat()) { CritterCombatData* combatData = &(self->data.critter.combat); - if ((combatData->maneuver & CRITTER_MANEUVER_0x01) == 0) { - combatData->maneuver |= CRITTER_MANEUVER_0x01; + if ((combatData->maneuver & CRITTER_MANEUVER_ENGAGING) == 0) { + combatData->maneuver |= CRITTER_MANEUVER_ENGAGING; combatData->whoHitMe = target; } } else { @@ -4431,8 +4431,8 @@ static void opAttackSetup(Program* program) } if (isInCombat()) { - if ((attacker->data.critter.combat.maneuver & CRITTER_MANEUVER_0x01) == 0) { - attacker->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01; + if ((attacker->data.critter.combat.maneuver & CRITTER_MANEUVER_ENGAGING) == 0) { + attacker->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; attacker->data.critter.combat.whoHitMe = defender; } } else { @@ -4756,7 +4756,7 @@ static void opTerminateCombat(Program* program) Object* self = scriptGetSelf(program); if (self != NULL) { if (PID_TYPE(self->pid) == OBJ_TYPE_CRITTER) { - self->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING; + self->data.critter.combat.maneuver |= CRITTER_MANEUVER_DISENGAGING; self->data.critter.combat.whoHitMe = NULL; aiInfoSetLastTarget(self, NULL); } @@ -4785,7 +4785,7 @@ static void opCritterStopAttacking(Program* program) Object* obj = static_cast(programStackPopPointer(program)); if (obj != NULL) { - obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_STOP_ATTACKING; + obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_DISENGAGING; obj->data.critter.combat.whoHitMe = NULL; aiInfoSetLastTarget(obj, NULL); } else { diff --git a/src/obj_types.h b/src/obj_types.h index 6ae7e13..c1d1867 100644 --- a/src/obj_types.h +++ b/src/obj_types.h @@ -118,8 +118,8 @@ typedef enum CritterFlags { typedef enum CritterManeuver { CRITTER_MANEUVER_NONE = 0, - CRITTER_MANEUVER_0x01 = 0x01, - CRITTER_MANEUVER_STOP_ATTACKING = 0x02, + CRITTER_MANEUVER_ENGAGING = 0x01, + CRITTER_MANEUVER_DISENGAGING = 0x02, CRITTER_MANUEVER_FLEEING = 0x04, } CritterManeuver; From ed7176b796f6a7206fb3bafa599c2185fa8c3684 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 16 Jan 2023 16:55:31 +0300 Subject: [PATCH 15/50] Fix combatai_notify_onlookers --- src/combat_ai.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 0b2940d..d9210d3 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -3593,7 +3593,7 @@ void _combatai_notify_onlookers(Object* a1) if (isWithinPerception(obj, a1)) { obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING; if ((a1->data.critter.combat.results & DAM_DEAD) != 0) { - if (!isWithinPerception(obj, obj->data.critter.combat.whoHitMe)) { + if (!isWithinPerception(obj, a1->data.critter.combat.whoHitMe)) { debugPrint("\nSomebody Died and I don't know why! Run!!!"); aiInfoSetFriendlyDead(obj, a1); } From b9261c3da2be577738ffad6269ab21608ec98256 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 16 Jan 2023 17:01:52 +0300 Subject: [PATCH 16/50] Fix directory name reading --- src/dictionary.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dictionary.cc b/src/dictionary.cc index 14d8604..af873e9 100644 --- a/src/dictionary.cc +++ b/src/dictionary.cc @@ -447,7 +447,7 @@ int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3) return -1; } - if (fgets(entry->key, keyLength, stream) == NULL) { + if (fgets(entry->key, keyLength + 1, stream) == NULL) { return -1; } From 8604d9c401703307fbab6491069cf6f443a9b85c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 16 Jan 2023 17:33:19 +0300 Subject: [PATCH 17/50] Fix check --- src/combat_ai.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index d9210d3..797d9e0 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -3071,7 +3071,7 @@ void _combat_ai(Object* a1, Object* a2) } if (a2 != NULL - && (a1->data.critter.combat.results & DAM_DEAD) == 0 + && (a2->data.critter.combat.results & DAM_DEAD) == 0 && a1->data.critter.combat.ap != 0 && objectGetDistanceBetween(a1, a2) > ai->max_dist) { Object* friendlyDead = aiInfoGetFriendlyDead(a1); From 2714dc1ea14b3786e28be3a631bae0dd43fe3b8f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 19 Jan 2023 20:27:22 +0300 Subject: [PATCH 18/50] Fix memory alignment See #135, #122 --- src/art.cc | 76 +++++++++++++++++++++++++++++++++++++++--------------- src/art.h | 5 ++-- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/art.cc b/src/art.cc index 3955489..bf17d69 100644 --- a/src/art.cc +++ b/src/art.cc @@ -34,8 +34,10 @@ static int artReadList(const char* path, char** out_arr, int* out_count); static int artCacheGetFileSizeImpl(int a1, int* out_size); static int artCacheReadDataImpl(int a1, int* a2, unsigned char* data); static void artCacheFreeImpl(void* ptr); -static int artReadFrameData(unsigned char* data, File* stream, int count); +static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr); static int artReadHeader(Art* art, File* stream); +static int artGetDataSize(Art* art); +static int paddingForSize(int size); // 0x5002D8 static char gDefaultJumpsuitMaleFileName[] = "hmjmps"; @@ -833,9 +835,9 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation) return NULL; } - ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation]); + ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation] + art->padding[rotation]); for (int index = 0; index < frame; index++) { - frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size); + frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size + paddingForSize(frm->size)); } return frm; } @@ -921,8 +923,8 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr) char* artFilePath = artBuildFilePath(fid); if (artFilePath != NULL) { - int fileSize; bool loaded = false; + File* stream = NULL; if (gArtLanguageInitialized) { char* pch = strchr(artFilePath, '\\'); @@ -933,20 +935,20 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr) char localizedPath[COMPAT_MAX_PATH]; snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch); - if (dbGetFileSize(localizedPath, &fileSize) == 0) { - loaded = true; - } + stream = fileOpen(localizedPath, "rb"); } - if (!loaded) { - if (dbGetFileSize(artFilePath, &fileSize) == 0) { - loaded = true; - } + if (stream == NULL) { + stream = fileOpen(artFilePath, "rb"); } - if (loaded) { - *sizePtr = fileSize; - result = 0; + if (stream != NULL) { + Art art; + if (artReadHeader(&art, stream) == 0) { + *sizePtr = artGetDataSize(&art); + result = 0; + } + fileClose(stream); } } @@ -982,8 +984,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data) } if (loaded) { - // TODO: Why it adds 74? - *sizePtr = ((Art*)data)->field_3A + 74; + *sizePtr = artGetDataSize((Art*)data); result = 0; } } @@ -1039,9 +1040,10 @@ out: } // 0x419D60 -static int artReadFrameData(unsigned char* data, File* stream, int count) +static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr) { unsigned char* ptr = data; + int padding = 0; for (int index = 0; index < count; index++) { ArtFrame* frame = (ArtFrame*)ptr; @@ -1053,8 +1055,12 @@ static int artReadFrameData(unsigned char* data, File* stream, int count) if (fileRead(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1; ptr += sizeof(ArtFrame) + frame->size; + ptr += paddingForSize(frame->size); + padding += paddingForSize(frame->size); } + *paddingPtr = padding; + return 0; } @@ -1068,7 +1074,7 @@ static int artReadHeader(Art* art, File* stream) if (fileReadInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1; if (fileReadInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1; if (fileReadInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1; - if (fileReadInt32(stream, &(art->field_3A)) == -1) return -1; + if (fileReadInt32(stream, &(art->dataSize)) == -1) return -1; return 0; } @@ -1087,9 +1093,16 @@ int artRead(const char* path, unsigned char* data) return -3; } + int currentPadding = paddingForSize(sizeof(Art)); + int previousPadding = 0; + for (int index = 0; index < ROTATION_COUNT; index++) { + art->padding[index] = currentPadding; + if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) { - if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index], stream, art->frameCount) != 0) { + art->padding[index] += previousPadding; + currentPadding += previousPadding; + if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount, &previousPadding) != 0) { fileClose(stream); return -5; } @@ -1117,6 +1130,7 @@ int artWriteFrameData(unsigned char* data, File* stream, int count) if (fileWrite(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1; ptr += sizeof(ArtFrame) + frame->size; + ptr += paddingForSize(frame->size); } return 0; @@ -1134,7 +1148,7 @@ int artWriteHeader(Art* art, File* stream) if (fileWriteInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1; if (fileWriteInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1; if (fileWriteInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1; - if (fileWriteInt32(stream, art->field_3A) == -1) return -1; + if (fileWriteInt32(stream, art->dataSize) == -1) return -1; return 0; } @@ -1161,7 +1175,7 @@ int artWrite(const char* path, unsigned char* data) for (int index = 0; index < ROTATION_COUNT; index++) { if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) { - if (artWriteFrameData(data + sizeof(Art) + art->dataOffsets[index], stream, art->frameCount) != 0) { + if (artWriteFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount) != 0) { fileClose(stream); return -1; } @@ -1172,6 +1186,26 @@ int artWrite(const char* path, unsigned char* data) return 0; } +static int artGetDataSize(Art* art) +{ + int dataSize = sizeof(*art) + art->dataSize; + + for (int index = 0; index < ROTATION_COUNT; index++) { + if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) { + // Assume worst case - every frame is unaligned and need + // max padding. + dataSize += (sizeof(int) - 1) * art->frameCount; + } + } + + return dataSize; +} + +static int paddingForSize(int size) +{ + return (sizeof(int) - size % sizeof(int)) % sizeof(int); +} + FrmImage::FrmImage() { _key = nullptr; diff --git a/src/art.h b/src/art.h index 9f0d628..0073cdc 100644 --- a/src/art.h +++ b/src/art.h @@ -67,7 +67,6 @@ typedef enum Background { BACKGROUND_COUNT, } Background; -#pragma pack(2) typedef struct Art { int field_0; short framesPerSecond; @@ -76,9 +75,9 @@ typedef struct Art { short xOffsets[6]; short yOffsets[6]; int dataOffsets[6]; - int field_3A; + int padding[6]; + int dataSize; } Art; -#pragma pack() typedef struct ArtFrame { short width; From bfbf692cc0adaed7c3114afe4caf872eb19a7ab3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 20 Jan 2023 09:57:52 +0300 Subject: [PATCH 19/50] Add iOS release workflow --- .github/workflows/release.yml | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 980fbc5..6f86dc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,48 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ios: + name: iOS + + runs-on: macos-11 + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Cache cmake build + uses: actions/cache@v3 + with: + path: build + key: ios-cmake-v1 + + - name: Configure + run: | + cmake \ + -B build \ + -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_TOOLCHAIN_FILE=cmake/toolchain/ios.toolchain.cmake \ + -D ENABLE_BITCODE=0 \ + -D PLATFORM=OS64 \ + # EOL + + - name: Build + run: | + cmake \ + --build build \ + -j $(sysctl -n hw.physicalcpu) \ + --target package \ + # EOL + + - name: Upload + run: | + cd build + cp fallout2-ce.zip fallout2-ce-ios.ipa + gh release upload ${{ github.ref_name }} fallout2-ce-ios.ipa + rm fallout2-ce-ios.ipa + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + linux: name: Linux (${{ matrix.arch }}) From dcd450a9b7effa71c96712301a7b774b3d36feb3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 20 Jan 2023 10:02:02 +0300 Subject: [PATCH 20/50] Bump version to 1.2.0 --- CMakeLists.txt | 4 ++-- os/android/app/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea1be8..56397b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,8 +320,8 @@ if(APPLE) set(MACOSX_BUNDLE_BUNDLE_NAME "${EXECUTABLE_NAME}") set(MACOSX_BUNDLE_ICON_FILE "fallout2-ce.icns") set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout 2") - set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.1.0") - set(MACOSX_BUNDLE_BUNDLE_VERSION "1.1.0") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.2.0") + set(MACOSX_BUNDLE_BUNDLE_VERSION "1.2.0") endif() add_subdirectory("third_party/fpattern") diff --git a/os/android/app/build.gradle b/os/android/app/build.gradle index 28dc7c3..de66abb 100644 --- a/os/android/app/build.gradle +++ b/os/android/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'com.alexbatalov.fallout2ce' minSdk 21 targetSdk 32 - versionCode 2 - versionName '1.1.0' + versionCode 3 + versionName '1.2.0' externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_static' From d435185e9eba1cc06307b1d2601935548f677f28 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 20 Jan 2023 14:12:03 +0300 Subject: [PATCH 21/50] Fix missing melee damage info --- src/character_selector.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/character_selector.cc b/src/character_selector.cc index 275a2d4..0a0bece 100644 --- a/src/character_selector.cc +++ b/src/character_selector.cc @@ -806,13 +806,13 @@ static bool characterSelectorWindowRenderStats() // MELEE DAMAGE y += vh; - str = statGetName(STAT_ARMOR_CLASS); + str = statGetName(STAT_MELEE_DAMAGE); strcpy(text, str); length = fontGetStringWidth(text); fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]); - value = critterGetStat(gDude, STAT_ARMOR_CLASS); + value = critterGetStat(gDude, STAT_MELEE_DAMAGE); snprintf(text, sizeof(text), " %d", value); length = fontGetStringWidth(text); From 47f5be8340f934e7cce3ffe40cce6f77a2496031 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 20 Jan 2023 14:16:43 +0300 Subject: [PATCH 22/50] Fix HP being used as AP --- src/combat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/combat.cc b/src/combat.cc index 2daecf6..f7aafaf 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -2803,7 +2803,7 @@ static void _combat_over() interfaceGetItemActions(&leftItemAction, &rightItemAction); interfaceUpdateItems(true, leftItemAction, rightItemAction); - gDude->data.critter.combat.ap = critterGetStat(gDude, STAT_MAXIMUM_HIT_POINTS); + gDude->data.critter.combat.ap = critterGetStat(gDude, STAT_MAXIMUM_ACTION_POINTS); interfaceRenderActionPoints(0, 0); From ba49abcea6912fad6eb163bebb5e58a363663872 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 21 Jan 2023 13:15:18 +0300 Subject: [PATCH 23/50] Fix some scripts not being properly removed Closes #224 --- src/scripts.cc | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/scripts.cc b/src/scripts.cc index 80c1de1..3ac7e4b 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -2337,37 +2337,30 @@ int _scr_remove_all() _queue_clear_type(EVENT_TYPE_SCRIPT, NULL); _scr_message_free(); - for (int scrType = 0; scrType < SCRIPT_TYPE_COUNT; scrType++) { - ScriptList* scriptList = &(gScriptLists[scrType]); + for (int scriptType = 0; scriptType < SCRIPT_TYPE_COUNT; scriptType++) { + ScriptList* scriptList = &(gScriptLists[scriptType]); - // TODO: Super odd way to remove scripts. The problem is that [scrRemove] - // does relocate scripts between extents, so current extent may become - // empty. In addition there is a 0x10 flag on the script that is not - // removed. Find a way to refactor this. ScriptListExtent* scriptListExtent = scriptList->head; while (scriptListExtent != NULL) { - ScriptListExtent* next = NULL; - for (int scriptIndex = 0; scriptIndex < scriptListExtent->length;) { - Script* script = &(scriptListExtent->scripts[scriptIndex]); + int index = 0; + while (scriptListExtent != NULL && index < scriptListExtent->length) { + Script* script = &(scriptListExtent->scripts[index]); if ((script->flags & SCRIPT_FLAG_0x10) != 0) { - scriptIndex++; + index++; } else { - if (scriptIndex != 0 || scriptListExtent->length != 1) { + if (index == 0 && scriptListExtent->length == 1) { + scriptListExtent = scriptListExtent->next; scriptRemove(script->sid); } else { - next = scriptListExtent->next; scriptRemove(script->sid); - - // CE: Current extent is freed in |scriptRemove|. Break - // to prevent next iteration which needs to dereference - // extent to obtain it's length. - break; } } } - scriptListExtent = next; + if (scriptListExtent != NULL) { + scriptListExtent = scriptListExtent->next; + } } } From 3aea6a98ef74dc3ea2d53c74a49992b4adc85f38 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 21 Jan 2023 15:24:18 +0300 Subject: [PATCH 24/50] Fix loading custom interface frms --- src/art.cc | 34 ++++++++++++++++++++++++++++++++++ src/art.h | 1 + src/interface.cc | 36 +++++++++++------------------------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/art.cc b/src/art.cc index bf17d69..1157f60 100644 --- a/src/art.cc +++ b/src/art.cc @@ -1079,6 +1079,40 @@ static int artReadHeader(Art* art, File* stream) return 0; } +// NOTE: Original function was slightly different, but never used. Basically +// it's a memory allocating variant of `artRead` (which reads data into given +// buffer). This function is useful to load custom `frm` files since `Art` now +// needs more memory then it's on-disk size (due to memory padding). +// +// 0x419EC0 +Art* artLoad(const char* path) +{ + File* stream = fileOpen(path, "rb"); + if (stream == nullptr) { + return nullptr; + } + + Art header; + if (artReadHeader(&header, stream) != 0) { + fileClose(stream); + return nullptr; + } + + fileClose(stream); + + unsigned char* data = reinterpret_cast(internal_malloc(artGetDataSize(&header))); + if (data == nullptr) { + return nullptr; + } + + if (artRead(path, data) != 0) { + internal_free(data); + return nullptr; + } + + return reinterpret_cast(data); +} + // 0x419FC0 int artRead(const char* path, unsigned char* data) { diff --git a/src/art.h b/src/art.h index 0073cdc..f7c31b4 100644 --- a/src/art.h +++ b/src/art.h @@ -147,6 +147,7 @@ int _art_alias_num(int a1); int artCritterFidShouldRun(int a1); int artAliasFid(int fid); int buildFid(int objectType, int frmId, int animType, int a4, int rotation); +Art* artLoad(const char* path); int artRead(const char* path, unsigned char* data); int artWrite(const char* path, unsigned char* data); diff --git a/src/interface.cc b/src/interface.cc index 7d492c3..3204faa 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -2483,30 +2483,26 @@ static void customInterfaceBarInit() { gInterfaceBarContentOffset = gInterfaceBarWidth - 640; - char path[COMPAT_MAX_PATH]; - snprintf(path, sizeof(path), "art\\intrface\\HR_IFACE_%d.FRM", gInterfaceBarWidth); + if (gInterfaceBarContentOffset > 0 && screenGetWidth() > 640) { + char path[COMPAT_MAX_PATH]; + snprintf(path, sizeof(path), "art\\intrface\\HR_IFACE_%d.FRM", gInterfaceBarWidth); - int size; - if (dbGetFileSize(path, &size) != 0 || gInterfaceBarContentOffset <= 0 || screenGetWidth() <= 640) { + gCustomInterfaceBarBackground = artLoad(path); + } + + if (gCustomInterfaceBarBackground != nullptr) { + gInterfaceBarIsCustom = true; + } else { gInterfaceBarContentOffset = 0; gInterfaceBarWidth = 640; gInterfaceBarIsCustom = false; - } else { - gInterfaceBarIsCustom = true; - - gCustomInterfaceBarBackground = (Art*)(malloc(size)); - if (artRead(path, (unsigned char*)gCustomInterfaceBarBackground) != 0) { - gInterfaceBarIsCustom = false; - free(gCustomInterfaceBarBackground); - gCustomInterfaceBarBackground = nullptr; - } } } static void customInterfaceBarExit() { if (gCustomInterfaceBarBackground != nullptr) { - free(gCustomInterfaceBarBackground); + internal_free(gCustomInterfaceBarBackground); gCustomInterfaceBarBackground = nullptr; } } @@ -2585,21 +2581,11 @@ static void sidePanelsShow() static void sidePanelsDraw(const char* path, int win, bool isLeading) { - int size; - if (dbGetFileSize(path, &size) != 0) { - return; - } - - Art* image = reinterpret_cast(internal_malloc(size)); + Art* image = artLoad(path); if (image == nullptr) { return; } - if (artRead(path, reinterpret_cast(image)) != 0) { - internal_free(image); - return; - } - unsigned char* imageData = artGetFrameData(image, 0, 0); int imageWidth = artGetWidth(image, 0, 0); From 5a230efd2bc7474f843f6fe849d485f53dd2f4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Mal=C3=A1k?= Date: Sun, 29 Jan 2023 06:56:03 +0100 Subject: [PATCH 25/50] Properly handle path with spaces (#228) --- third_party/zlib/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 9f17c85..7a89186 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -10,7 +10,7 @@ if (NOT zlib_POPULATED) FetchContent_Populate(zlib) endif() -add_subdirectory(${zlib_SOURCE_DIR} ${zlib_BINARY_DIR} EXCLUDE_FROM_ALL) +add_subdirectory("${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}" EXCLUDE_FROM_ALL) -set(ZLIB_LIBRARIES zlibstatic PARENT_SCOPE) -set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR} PARENT_SCOPE) +set(ZLIB_LIBRARIES "zlibstatic" PARENT_SCOPE) +set(ZLIB_INCLUDE_DIRS "${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}" PARENT_SCOPE) From c9864741f369e2b4c8b5c16cf23b6111b2a1e371 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 9 Feb 2023 19:27:25 +0300 Subject: [PATCH 26/50] Fix crash in artLockFrameDataReturningSize Closes #231 --- src/art.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/art.cc b/src/art.cc index 1157f60..426a33f 100644 --- a/src/art.cc +++ b/src/art.cc @@ -477,7 +477,7 @@ unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** handlePtr, in { *handlePtr = NULL; - Art* art; + Art* art = NULL; cacheLock(&gArtCache, fid, (void**)&art, handlePtr); if (art == NULL) { From 28083cfea9b2ff57a04a2be950bf1c6e54057eff Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 9 Feb 2023 19:40:25 +0300 Subject: [PATCH 27/50] Fix talking head mood transition --- src/game_dialog.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/game_dialog.cc b/src/game_dialog.cc index c0924e9..48a6518 100644 --- a/src/game_dialog.cc +++ b/src/game_dialog.cc @@ -2612,11 +2612,16 @@ void _gdPlayTransition(int anim) int frame = 0; unsigned int time = 0; while (frame < artGetFrameCount(headFrm)) { + sharedFpsLimiter.mark(); + if (getTicksSince(time) >= delay) { gameDialogRenderTalkingHead(headFrm, frame); time = getTicks(); frame++; } + + renderPresent(); + sharedFpsLimiter.throttle(); } if (artUnlock(headFrmHandle) == -1) { From 33141672ed8343271dab92e66bee5c29e2391fbd Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 10 Feb 2023 09:00:59 +0300 Subject: [PATCH 28/50] Use screen borders for scrolling worldmap --- src/worldmap.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/worldmap.cc b/src/worldmap.cc index 34148c9..a02914f 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -745,12 +745,6 @@ static int wmRndCallCount = 0; // 0x51DEAC static int _terrainCounter = 1; -// 0x51DEB0 -static unsigned int _lastTime_2 = 0; - -// 0x51DEB4 -static bool _couldScroll = true; - // 0x51DEC8 static char* wmRemapSfxList[2] = { _aCricket, @@ -4871,19 +4865,25 @@ static int wmInterfaceScrollPixel(int stepX, int stepY, int dx, int dy, bool* su // 0x4C32EC static void wmMouseBkProc() { + // 0x51DEB0 + static unsigned int lastTime = 0; + + // 0x51DEB4 + static bool couldScroll = true; + int x; int y; - mouseGetPositionInWindow(wmBkWin, &x, &y); + mouseGetPosition(&x, &y); int dx = 0; - if (x == 639) { + if (x == screenGetWidth() - 1) { dx = 1; } else if (x == 0) { dx = -1; } int dy = 0; - if (y == 479) { + if (y == screenGetHeight() - 1) { dy = 1; } else if (y == 0) { dy = -1; @@ -4918,13 +4918,13 @@ static void wmMouseBkProc() } unsigned int tick = _get_bk_time(); - if (getTicksBetween(tick, _lastTime_2) > 50) { - _lastTime_2 = _get_bk_time(); + if (getTicksBetween(tick, lastTime) > 50) { + lastTime = _get_bk_time(); // NOTE: Uninline. - wmInterfaceScroll(dx, dy, &_couldScroll); + wmInterfaceScroll(dx, dy, &couldScroll); } - if (!_couldScroll) { + if (!couldScroll) { newMouseCursor += 8; } } else { From c14f671a0d73548d77e822a4bcdb7b8ff7213455 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 10 Feb 2023 09:27:10 +0300 Subject: [PATCH 29/50] Add object and string concatenation Fixes #232 --- src/interpreter.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/interpreter.cc b/src/interpreter.cc index 14eebc0..8729fd5 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -1491,6 +1491,21 @@ static void opAdd(Program* program) break; } break; + // Sonora folks use "object + string" concatenation for debug purposes. + case VALUE_TYPE_PTR: + switch (value[0].opcode) { + case VALUE_TYPE_STRING: + case VALUE_TYPE_DYNAMIC_STRING: + strings[0] = programGetString(program, value[0].opcode, value[0].integerValue); + tempString = (char*)internal_malloc_safe(strlen(strings[0]) + 80, __FILE__, __LINE__); + snprintf(tempString, strlen(strings[0]) + 80, "%p", value[1].pointerValue); + strcat(tempString, strings[0]); + + programStackPushString(program, tempString); + + internal_free_safe(tempString, __FILE__, __LINE__); + break; + } } } From 81210f46af4177958e2c4932c33ddf86b3e96258 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 10 Feb 2023 11:05:42 +0300 Subject: [PATCH 30/50] Fix storing pointers in game global variables --- src/game.cc | 43 +++++++++++++++++++++++++++++++++++++++- src/game.h | 2 ++ src/interpreter_extra.cc | 23 ++++++++++++++------- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/game.cc b/src/game.cc index 277e34f..c0ffb89 100644 --- a/src/game.cc +++ b/src/game.cc @@ -117,6 +117,9 @@ int _game_user_wants_to_quit = 0; // 0x58E940 MessageList gMiscMessageList; +// CE: Sonora folks like to store objects in global variables. +static void** gGameGlobalPointers = nullptr; + // 0x442580 int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4, int argc, char** argv) { @@ -993,7 +996,18 @@ int gameSetGlobalVar(int var, int value) // 0x443CC8 static int gameLoadGlobalVars() { - return globalVarsRead("data\\vault13.gam", "GAME_GLOBAL_VARS:", &gGameGlobalVarsLength, &gGameGlobalVars); + if (globalVarsRead("data\\vault13.gam", "GAME_GLOBAL_VARS:", &gGameGlobalVarsLength, &gGameGlobalVars) != 0) { + return -1; + } + + gGameGlobalPointers = reinterpret_cast(internal_malloc(sizeof(*gGameGlobalPointers) * gGameGlobalVarsLength)); + if (gGameGlobalPointers == nullptr) { + return -1; + } + + memset(gGameGlobalPointers, 0, sizeof(*gGameGlobalPointers) * gGameGlobalVarsLength); + + return 0; } // 0x443CE8 @@ -1134,6 +1148,11 @@ static void gameFreeGlobalVars() internal_free(gGameGlobalVars); gGameGlobalVars = NULL; } + + if (gGameGlobalPointers != nullptr) { + internal_free(gGameGlobalPointers); + gGameGlobalPointers = nullptr; + } } // 0x443F74 @@ -1492,6 +1511,28 @@ int gameShowDeathDialog(const char* message) return rc; } +void* gameGetGlobalPointer(int var) +{ + if (var < 0 || var >= gGameGlobalVarsLength) { + debugPrint("ERROR: attempt to reference global pointer out of range: %d", var); + return nullptr; + } + + return gGameGlobalPointers[var]; +} + +int gameSetGlobalPointer(int var, void* value) +{ + if (var < 0 || var >= gGameGlobalVarsLength) { + debugPrint("ERROR: attempt to reference global var out of range: %d", var); + return -1; + } + + gGameGlobalPointers[var] = value; + + return 0; +} + int GameMode::currentGameMode = 0; void GameMode::enterGameMode(int gameMode) diff --git a/src/game.h b/src/game.h index 4ec43f5..967e291 100644 --- a/src/game.h +++ b/src/game.h @@ -38,6 +38,8 @@ void gameUpdateState(); int showQuitConfirmationDialog(); int gameShowDeathDialog(const char* message); +void* gameGetGlobalPointer(int var); +int gameSetGlobalPointer(int var, void* value); class GameMode { public: diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index 3a2d929..1cce7b4 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -1207,16 +1207,19 @@ static void opSetMapVar(Program* program) // 0x455950 static void opGetGlobalVar(Program* program) { - int data = programStackPopInteger(program); + int variable = programStackPopInteger(program); - int value = -1; if (gGameGlobalVarsLength != 0) { - value = gameGetGlobalVar(data); + void* ptr = gameGetGlobalPointer(variable); + if (ptr != nullptr) { + programStackPushPointer(program, ptr); + } else { + programStackPushInteger(program, gameGetGlobalVar(variable)); + } } else { scriptError("\nScript Error: %s: op_global_var: no global vars found!", program->name); + programStackPushInteger(program, -1); } - - programStackPushInteger(program, value); } // set_global_var @@ -1224,11 +1227,17 @@ static void opGetGlobalVar(Program* program) // 0x80C6 static void opSetGlobalVar(Program* program) { - int value = programStackPopInteger(program); + ProgramValue value = programStackPopValue(program); int variable = programStackPopInteger(program); if (gGameGlobalVarsLength != 0) { - gameSetGlobalVar(variable, value); + if (value.opcode == VALUE_TYPE_PTR) { + gameSetGlobalPointer(variable, value.pointerValue); + gameSetGlobalVar(variable, 0); + } else { + gameSetGlobalPointer(variable, nullptr); + gameSetGlobalVar(variable, value.integerValue); + } } else { scriptError("\nScript Error: %s: op_set_global_var: no global vars found!", program->name); } From f5d3cfb5e3a63c8af158be2d000da096806f8490 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 12 Feb 2023 23:12:36 +0300 Subject: [PATCH 31/50] Fix Fast Metabolism trait --- src/trait.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/trait.cc b/src/trait.cc index 9beafea..c2b9872 100644 --- a/src/trait.cc +++ b/src/trait.cc @@ -265,12 +265,12 @@ int traitGetStatModifier(int stat) break; case STAT_RADIATION_RESISTANCE: if (traitIsSelected(TRAIT_FAST_METABOLISM)) { - modifier -= -critterGetBaseStat(gDude, STAT_RADIATION_RESISTANCE); + modifier -= critterGetBaseStat(gDude, STAT_RADIATION_RESISTANCE); } break; case STAT_POISON_RESISTANCE: if (traitIsSelected(TRAIT_FAST_METABOLISM)) { - modifier -= -critterGetBaseStat(gDude, STAT_POISON_RESISTANCE); + modifier -= critterGetBaseStat(gDude, STAT_POISON_RESISTANCE); } break; } From 36b5ceba8a7aa7b583d4eff3191fb58815376d18 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 13 Feb 2023 11:02:17 +0300 Subject: [PATCH 32/50] Improve sound_decoder.cc accuracy --- src/audio.cc | 19 ++--- src/audio_file.cc | 19 ++--- src/sound_decoder.cc | 164 ++++++++++++++++++------------------- src/sound_decoder.h | 6 +- src/sound_effects_cache.cc | 7 +- src/sound_effects_list.cc | 12 +-- 6 files changed, 111 insertions(+), 116 deletions(-) diff --git a/src/audio.cc b/src/audio.cc index 33ee26c..4c80182 100644 --- a/src/audio.cc +++ b/src/audio.cc @@ -7,7 +7,6 @@ #include "db.h" #include "debug.h" #include "memory_manager.h" -#include "pointer_registry.h" #include "sound.h" #include "sound_decoder.h" @@ -20,7 +19,7 @@ typedef enum AudioFlags { typedef struct Audio { int flags; - int stream; + File* stream; SoundDecoder* soundDecoder; int fileSize; int sampleRate; @@ -29,7 +28,7 @@ typedef struct Audio { } Audio; static bool defaultCompressionFunc(char* filePath); -static int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size); +static int audioSoundDecoderReadHandler(void* data, void* buf, unsigned int size); // 0x5108BC static AudioQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc; @@ -52,9 +51,9 @@ static bool defaultCompressionFunc(char* filePath) } // 0x41A2D0 -static int audioSoundDecoderReadHandler(int handle, void* buffer, unsigned int size) +static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int size) { - return fileRead(buffer, 1, size, (File*)intToPtr(handle)); + return fileRead(buffer, 1, size, reinterpret_cast(data)); } // AudioOpen @@ -116,7 +115,7 @@ int audioOpen(const char* fname, int flags, ...) Audio* audioFile = &(gAudioList[index]); audioFile->flags = AUDIO_IN_USE; - audioFile->stream = ptrToInt(stream); + audioFile->stream = stream; if (compression == 2) { audioFile->flags |= AUDIO_COMPRESSED; @@ -135,7 +134,7 @@ int audioOpen(const char* fname, int flags, ...) int audioClose(int handle) { Audio* audioFile = &(gAudioList[handle - 1]); - fileClose((File*)intToPtr(audioFile->stream, true)); + fileClose(audioFile->stream); if ((audioFile->flags & AUDIO_COMPRESSED) != 0) { soundDecoderFree(audioFile->soundDecoder); @@ -155,7 +154,7 @@ int audioRead(int handle, void* buffer, unsigned int size) if ((audioFile->flags & AUDIO_COMPRESSED) != 0) { bytesRead = soundDecoderDecode(audioFile->soundDecoder, buffer, size); } else { - bytesRead = fileRead(buffer, 1, size, (File*)intToPtr(audioFile->stream)); + bytesRead = fileRead(buffer, 1, size, audioFile->stream); } audioFile->position += bytesRead; @@ -189,7 +188,7 @@ long audioSeek(int handle, long offset, int origin) if ((audioFile->flags & AUDIO_COMPRESSED) != 0) { if (pos < audioFile->position) { soundDecoderFree(audioFile->soundDecoder); - fileSeek((File*)intToPtr(audioFile->stream), 0, SEEK_SET); + fileSeek(audioFile->stream, 0, SEEK_SET); audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->position = 0; audioFile->fileSize *= 2; @@ -224,7 +223,7 @@ long audioSeek(int handle, long offset, int origin) return audioFile->position; } else { - return fileSeek((File*)intToPtr(audioFile->stream), offset, origin); + return fileSeek(audioFile->stream, offset, origin); } } diff --git a/src/audio_file.cc b/src/audio_file.cc index b0638e4..30db375 100644 --- a/src/audio_file.cc +++ b/src/audio_file.cc @@ -7,7 +7,6 @@ #include "debug.h" #include "memory_manager.h" #include "platform_compat.h" -#include "pointer_registry.h" #include "sound.h" #include "sound_decoder.h" @@ -20,7 +19,7 @@ typedef enum AudioFileFlags { typedef struct AudioFile { int flags; - int stream; + FILE* stream; SoundDecoder* soundDecoder; int fileSize; int sampleRate; @@ -29,7 +28,7 @@ typedef struct AudioFile { } AudioFile; static bool defaultCompressionFunc(char* filePath); -static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size); +static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size); // 0x5108C0 static AudioFileQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc; @@ -52,9 +51,9 @@ static bool defaultCompressionFunc(char* filePath) } // 0x41A870 -static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size) +static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size) { - return fread(buffer, 1, size, (FILE*)intToPtr(handle)); + return fread(buffer, 1, size, reinterpret_cast(data)); } // 0x41A88C @@ -114,7 +113,7 @@ int audioFileOpen(const char* fname, int flags, ...) AudioFile* audioFile = &(gAudioFileList[index]); audioFile->flags = AUDIO_FILE_IN_USE; - audioFile->stream = ptrToInt(stream); + audioFile->stream = stream; if (compression == 2) { audioFile->flags |= AUDIO_FILE_COMPRESSED; @@ -133,7 +132,7 @@ int audioFileOpen(const char* fname, int flags, ...) int audioFileClose(int handle) { AudioFile* audioFile = &(gAudioFileList[handle - 1]); - fclose((FILE*)intToPtr(audioFile->stream, true)); + fclose(audioFile->stream); if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) { soundDecoderFree(audioFile->soundDecoder); @@ -155,7 +154,7 @@ int audioFileRead(int handle, void* buffer, unsigned int size) if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) { bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size); } else { - bytesRead = fread(buffer, 1, size, (FILE*)intToPtr(ptr->stream)); + bytesRead = fread(buffer, 1, size, ptr->stream); } ptr->position += bytesRead; @@ -190,7 +189,7 @@ long audioFileSeek(int handle, long offset, int origin) if (a4 <= audioFile->position) { soundDecoderFree(audioFile->soundDecoder); - fseek((FILE*)intToPtr(audioFile->stream), 0, 0); + fseek(audioFile->stream, 0, 0); audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->fileSize *= 2; @@ -222,7 +221,7 @@ long audioFileSeek(int handle, long offset, int origin) return audioFile->position; } - return fseek((FILE*)intToPtr(audioFile->stream), offset, origin); + return fseek(audioFile->stream, offset, origin); } // 0x41AD20 diff --git a/src/sound_decoder.cc b/src/sound_decoder.cc index b61c91e..3b2c33a 100644 --- a/src/sound_decoder.cc +++ b/src/sound_decoder.cc @@ -17,27 +17,27 @@ namespace fallout { typedef int (*ReadBandFunc)(SoundDecoder* soundDecoder, int offset, int bits); -static bool soundDecoderPrepare(SoundDecoder* a1, SoundDecoderReadProc* readProc, int fileHandle); -static unsigned char soundDecoderReadNextChunk(SoundDecoder* a1); -static void _init_pack_tables(); -static int _ReadBand_Fail_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits); -static int _ReadBands_(SoundDecoder* ptr); -static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4); -static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4); -static void _untransform_all(SoundDecoder* a1); +static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, void* data); +static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder); +static void init_pack_tables(); +static int ReadBand_Fail(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt0(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt3_16(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt17(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt18(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt19(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt20(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt21(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt22(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt23(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits); +static int ReadBands(SoundDecoder* ptr); +static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4); +static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4); +static void untransform_all(SoundDecoder* soundDecoder); static inline void soundDecoderRequireBits(SoundDecoder* soundDecoder, int bits); static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits); @@ -47,38 +47,38 @@ static int gSoundDecodersCount = 0; // 0x51E330 static ReadBandFunc _ReadBand_tbl[32] = { - _ReadBand_Fmt0_, - _ReadBand_Fail_, - _ReadBand_Fail_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt3_16_, - _ReadBand_Fmt17_, - _ReadBand_Fmt18_, - _ReadBand_Fmt19_, - _ReadBand_Fmt20_, - _ReadBand_Fmt21_, - _ReadBand_Fmt22_, - _ReadBand_Fmt23_, - _ReadBand_Fmt24_, - _ReadBand_Fail_, - _ReadBand_Fmt26_, - _ReadBand_Fmt27_, - _ReadBand_Fail_, - _ReadBand_Fmt29_, - _ReadBand_Fail_, - _ReadBand_Fail_, + ReadBand_Fmt0, + ReadBand_Fail, + ReadBand_Fail, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt3_16, + ReadBand_Fmt17, + ReadBand_Fmt18, + ReadBand_Fmt19, + ReadBand_Fmt20, + ReadBand_Fmt21, + ReadBand_Fmt22, + ReadBand_Fmt23, + ReadBand_Fmt24, + ReadBand_Fail, + ReadBand_Fmt26, + ReadBand_Fmt27, + ReadBand_Fail, + ReadBand_Fmt29, + ReadBand_Fail, + ReadBand_Fail, }; // 0x6AD960 @@ -97,10 +97,10 @@ static unsigned char* _AudioDecoder_scale0; static unsigned char* _AudioDecoder_scale_tbl; // 0x4D3BB0 -static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, int fileHandle) +static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc* readProc, void* data) { soundDecoder->readProc = readProc; - soundDecoder->fd = fileHandle; + soundDecoder->data = data; soundDecoder->bufferIn = (unsigned char*)malloc(SOUND_DECODER_IN_BUFFER_SIZE); if (soundDecoder->bufferIn == NULL) { @@ -116,7 +116,7 @@ static bool soundDecoderPrepare(SoundDecoder* soundDecoder, SoundDecoderReadProc // 0x4D3BE0 static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder) { - soundDecoder->remainingInSize = soundDecoder->readProc(soundDecoder->fd, soundDecoder->bufferIn, soundDecoder->bufferInSize); + soundDecoder->remainingInSize = soundDecoder->readProc(soundDecoder->data, soundDecoder->bufferIn, soundDecoder->bufferInSize); if (soundDecoder->remainingInSize == 0) { memset(soundDecoder->bufferIn, 0, soundDecoder->bufferInSize); soundDecoder->remainingInSize = soundDecoder->bufferInSize; @@ -128,7 +128,7 @@ static unsigned char soundDecoderReadNextChunk(SoundDecoder* soundDecoder) } // 0x4D3C78 -static void _init_pack_tables() +static void init_pack_tables() { // 0x51E32C static bool inited = false; @@ -167,13 +167,13 @@ static void _init_pack_tables() } // 0x4D3D9C -static int _ReadBand_Fail_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fail(SoundDecoder* soundDecoder, int offset, int bits) { return 0; } // 0x4D3DA0 -static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt0(SoundDecoder* soundDecoder, int offset, int bits) { int* p = (int*)soundDecoder->samples; p += offset; @@ -189,7 +189,7 @@ static int _ReadBand_Fmt0_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D3DC8 -static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt3_16(SoundDecoder* soundDecoder, int offset, int bits) { int value; int v14; @@ -218,7 +218,7 @@ static int _ReadBand_Fmt3_16_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D3E90 -static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt17(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -272,7 +272,7 @@ static int _ReadBand_Fmt17_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D3F98 -static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt18(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -310,7 +310,7 @@ static int _ReadBand_Fmt18_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4068 -static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt19(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; base -= 1; @@ -348,7 +348,7 @@ static int _ReadBand_Fmt19_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4158 -static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt20(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -411,7 +411,7 @@ static int _ReadBand_Fmt20_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4254 -static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt21(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -458,7 +458,7 @@ static int _ReadBand_Fmt21_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4338 -static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt22(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; base -= 2; @@ -500,7 +500,7 @@ static int _ReadBand_Fmt22_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4434 -static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt23(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -569,7 +569,7 @@ static int _ReadBand_Fmt23_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4584 -static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -623,7 +623,7 @@ static int _ReadBand_Fmt24_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4698 -static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -679,7 +679,7 @@ static int _ReadBand_Fmt26_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D47A4 -static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -719,7 +719,7 @@ static int _ReadBand_Fmt27_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D4870 -static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits) +static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits) { short* base = (short*)_AudioDecoder_scale0; @@ -751,7 +751,7 @@ static int _ReadBand_Fmt29_(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D493C -static int _ReadBands_(SoundDecoder* soundDecoder) +static int ReadBands(SoundDecoder* soundDecoder) { int v9; int v15; @@ -788,7 +788,7 @@ static int _ReadBands_(SoundDecoder* soundDecoder) v21 -= v15; } - _init_pack_tables(); + init_pack_tables(); for (int index = 0; index < soundDecoder->subbands; index++) { soundDecoderRequireBits(soundDecoder, 5); @@ -804,7 +804,7 @@ static int _ReadBands_(SoundDecoder* soundDecoder) } // 0x4D4ADC -static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4) +static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4) { short* p; @@ -902,7 +902,7 @@ static void _untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, } // 0x4D4D1C -static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4) +static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4) { int v13; int* v14; @@ -993,7 +993,7 @@ static void _untransform_subband(unsigned char* a1, unsigned char* a2, int a3, i } // 0x4D4E80 -static void _untransform_all(SoundDecoder* soundDecoder) +static void untransform_all(SoundDecoder* soundDecoder) { int v8; unsigned char* ptr; @@ -1019,7 +1019,7 @@ static void _untransform_all(SoundDecoder* soundDecoder) v4 *= 2; - _untransform_subband0(soundDecoder->prev_samples, ptr, v3, v4); + untransform_subband0(soundDecoder->prev_samples, ptr, v3, v4); v5 = (int*)ptr; for (v6 = 0; v6 < v4; v6++) { @@ -1034,7 +1034,7 @@ static void _untransform_all(SoundDecoder* soundDecoder) if (v3 == 0) { break; } - _untransform_subband(j, ptr, v3, v4); + untransform_subband(j, ptr, v3, v4); j += 8 * v3; } @@ -1063,11 +1063,11 @@ size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size) break; } - if (!_ReadBands_(soundDecoder)) { + if (!ReadBands(soundDecoder)) { break; } - _untransform_all(soundDecoder); + untransform_all(soundDecoder); soundDecoder->file_cnt -= soundDecoder->total_samples; soundDecoder->samp_ptr = soundDecoder->samples; @@ -1122,7 +1122,7 @@ void soundDecoderFree(SoundDecoder* soundDecoder) } // 0x4D50A8 -SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr) +SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, void* data, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr) { int v14; int v20; @@ -1137,7 +1137,7 @@ SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, i gSoundDecodersCount++; - if (!soundDecoderPrepare(soundDecoder, readProc, fileHandle)) { + if (!soundDecoderPrepare(soundDecoder, readProc, data)) { goto L66; } diff --git a/src/sound_decoder.h b/src/sound_decoder.h index aeb6f79..ff7fbee 100644 --- a/src/sound_decoder.h +++ b/src/sound_decoder.h @@ -5,11 +5,11 @@ namespace fallout { -typedef int(SoundDecoderReadProc)(int fileHandle, void* buffer, unsigned int size); +typedef int(SoundDecoderReadProc)(void* data, void* buffer, unsigned int size); typedef struct SoundDecoder { SoundDecoderReadProc* readProc; - int fd; + void* data; unsigned char* bufferIn; size_t bufferInSize; @@ -41,7 +41,7 @@ typedef struct SoundDecoder { size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size); void soundDecoderFree(SoundDecoder* soundDecoder); -SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, int fileHandle, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr); +SoundDecoder* soundDecoderInit(SoundDecoderReadProc* readProc, void* data, int* channelsPtr, int* sampleRatePtr, int* sampleCountPtr); } // namespace fallout diff --git a/src/sound_effects_cache.cc b/src/sound_effects_cache.cc index 851d7b5..d11603f 100644 --- a/src/sound_effects_cache.cc +++ b/src/sound_effects_cache.cc @@ -40,7 +40,7 @@ static void soundEffectsCacheFreeHandles(); static int soundEffectsCreate(int* handlePtr, int id, void* data, CacheEntry* cacheHandle); static bool soundEffectsIsValidHandle(int a1); static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned int size); -static int _sfxc_ad_reader(int handle, void* buf, unsigned int size); +static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size); // 0x50DE04 static const char* off_50DE04 = ""; @@ -476,7 +476,7 @@ static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned i int channels; int sampleRate; int sampleCount; - SoundDecoder* soundDecoder = soundDecoderInit(_sfxc_ad_reader, handle, &channels, &sampleRate, &sampleCount); + SoundDecoder* soundDecoder = soundDecoderInit(soundEffectsCacheSoundDecoderReadHandler, &handle, &channels, &sampleRate, &sampleCount); if (soundEffect->position != 0) { void* temp = internal_malloc(soundEffect->position); @@ -505,12 +505,13 @@ static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned i } // 0x4A9774 -static int _sfxc_ad_reader(int handle, void* buf, unsigned int size) +static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size) { if (size == 0) { return 0; } + int handle = *reinterpret_cast(data); SoundEffect* soundEffect = &(gSoundEffects[handle]); unsigned int bytesToRead = soundEffect->fileSize - soundEffect->dataPosition; diff --git a/src/sound_effects_list.cc b/src/sound_effects_list.cc index d3140da..8e9b779 100644 --- a/src/sound_effects_list.cc +++ b/src/sound_effects_list.cc @@ -9,7 +9,6 @@ #include "debug.h" #include "memory.h" #include "platform_compat.h" -#include "pointer_registry.h" #include "sound_decoder.h" namespace fallout { @@ -28,7 +27,7 @@ static int soundEffectsListCopyFileNames(char** fileNameList); static int soundEffectsListPopulateFileSizes(); static int soundEffectsListSort(); static int soundEffectsListCompareByName(const void* a1, const void* a2); -static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size); +static int soundEffectsListSoundDecoderReadHandler(void* data, void* buf, unsigned int size); // 0x51C8F8 static bool gSoundEffectsListInitialized = false; @@ -424,16 +423,13 @@ static int soundEffectsListPopulateFileSizes() return 1; } - int fileHandle = ptrToInt((void*)stream); - int channels; int sampleRate; int sampleCount; - SoundDecoder* soundDecoder = soundDecoderInit(_sfxl_ad_reader, fileHandle, &channels, &sampleRate, &sampleCount); + SoundDecoder* soundDecoder = soundDecoderInit(soundEffectsListSoundDecoderReadHandler, stream, &channels, &sampleRate, &sampleCount); entry->dataSize = 2 * sampleCount; soundDecoderFree(soundDecoder); fileClose(stream); - intToPtr(fileHandle, true); } break; default: @@ -468,9 +464,9 @@ static int soundEffectsListCompareByName(const void* a1, const void* a2) } // read via xfile -static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size) +static int soundEffectsListSoundDecoderReadHandler(void* data, void* buf, unsigned int size) { - return fileRead(buf, 1, size, (File*)intToPtr(fileHandle)); + return fileRead(buf, 1, size, reinterpret_cast(data)); } } // namespace fallout From 075de8f837b4c08a5f359431e868d4cf1a23e9a7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 13 Feb 2023 11:51:09 +0300 Subject: [PATCH 33/50] Review sound IO functions --- src/audio.cc | 2 +- src/audio.h | 2 +- src/audio_file.cc | 2 +- src/audio_file.h | 2 +- src/game_sound.cc | 6 ++-- src/platform_compat.cc | 15 -------- src/platform_compat.h | 3 -- src/sound.cc | 70 ++++++++++++++++++++++++++++++++++---- src/sound.h | 2 +- src/sound_effects_cache.cc | 2 +- src/sound_effects_cache.h | 2 +- 11 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/audio.cc b/src/audio.cc index 4c80182..ee71201 100644 --- a/src/audio.cc +++ b/src/audio.cc @@ -58,7 +58,7 @@ static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int s // AudioOpen // 0x41A2EC -int audioOpen(const char* fname, int flags, ...) +int audioOpen(const char* fname, int flags) { char path[80]; snprintf(path, sizeof(path), "%s", fname); diff --git a/src/audio.h b/src/audio.h index f916b16..2b96ac9 100644 --- a/src/audio.h +++ b/src/audio.h @@ -5,7 +5,7 @@ namespace fallout { typedef bool(AudioQueryCompressedFunc)(char* filePath); -int audioOpen(const char* fname, int mode, ...); +int audioOpen(const char* fname, int mode); int audioClose(int handle); int audioRead(int handle, void* buffer, unsigned int size); long audioSeek(int handle, long offset, int origin); diff --git a/src/audio_file.cc b/src/audio_file.cc index 30db375..d1dce84 100644 --- a/src/audio_file.cc +++ b/src/audio_file.cc @@ -57,7 +57,7 @@ static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned i } // 0x41A88C -int audioFileOpen(const char* fname, int flags, ...) +int audioFileOpen(const char* fname, int flags) { char path[COMPAT_MAX_PATH]; strcpy(path, fname); diff --git a/src/audio_file.h b/src/audio_file.h index 38c7a02..f00734b 100644 --- a/src/audio_file.h +++ b/src/audio_file.h @@ -5,7 +5,7 @@ namespace fallout { typedef bool(AudioFileQueryCompressedFunc)(char* filePath); -int audioFileOpen(const char* fname, int flags, ...); +int audioFileOpen(const char* fname, int flags); int audioFileClose(int handle); int audioFileRead(int handle, void* buf, unsigned int size); long audioFileSeek(int handle, long offset, int origin); diff --git a/src/game_sound.cc b/src/game_sound.cc index 3423f72..e7a838c 100644 --- a/src/game_sound.cc +++ b/src/game_sound.cc @@ -157,7 +157,7 @@ static int _gsound_speech_volume_get_set(int volume); static void speechPause(); static void speechResume(); static void _gsound_bkg_proc(); -static int gameSoundFileOpen(const char* fname, int access, ...); +static int gameSoundFileOpen(const char* fname, int access); static long _gsound_write_(); static long gameSoundFileTellNotImplemented(int handle); static int gameSoundFileWrite(int handle, const void* buf, unsigned int size); @@ -898,7 +898,7 @@ int speechLoad(const char* fname, int a2, int a3, int a4) return -1; } - if (soundSetFileIO(gSpeechSound, &audioOpen, &audioClose, &audioRead, NULL, &audioSeek, &gameSoundFileTellNotImplemented, &audioGetSize)) { + if (soundSetFileIO(gSpeechSound, audioOpen, audioClose, audioRead, NULL, audioSeek, gameSoundFileTellNotImplemented, audioGetSize)) { if (gGameSoundDebugEnabled) { debugPrint("failed because file IO could not be set for compression.\n"); } @@ -1548,7 +1548,7 @@ void _gsound_bkg_proc() } // 0x451A08 -int gameSoundFileOpen(const char* fname, int flags, ...) +int gameSoundFileOpen(const char* fname, int flags) { if ((flags & 2) != 0) { return -1; diff --git a/src/platform_compat.cc b/src/platform_compat.cc index ba49610..339739c 100644 --- a/src/platform_compat.cc +++ b/src/platform_compat.cc @@ -187,21 +187,6 @@ void compat_makepath(char* path, const char* drive, const char* dir, const char* #endif } -int compat_read(int fileHandle, void* buf, unsigned int size) -{ - return read(fileHandle, buf, size); -} - -int compat_write(int fileHandle, const void* buf, unsigned int size) -{ - return write(fileHandle, buf, size); -} - -long compat_lseek(int fileHandle, long offset, int origin) -{ - return lseek(fileHandle, offset, origin); -} - long compat_tell(int fd) { return lseek(fd, 0, SEEK_CUR); diff --git a/src/platform_compat.h b/src/platform_compat.h index 4da0706..be88974 100644 --- a/src/platform_compat.h +++ b/src/platform_compat.h @@ -29,9 +29,6 @@ char* compat_strlwr(char* string); char* compat_itoa(int value, char* buffer, int radix); void compat_splitpath(const char* path, char* drive, char* dir, char* fname, char* ext); void compat_makepath(char* path, const char* drive, const char* dir, const char* fname, const char* ext); -int compat_read(int fileHandle, void* buf, unsigned int size); -int compat_write(int fileHandle, const void* buf, unsigned int size); -long compat_lseek(int fileHandle, long offset, int origin); long compat_tell(int fileHandle); long compat_filelength(int fd); int compat_mkdir(const char* path); diff --git a/src/sound.cc b/src/sound.cc index 3881a68..353b605 100644 --- a/src/sound.cc +++ b/src/sound.cc @@ -45,6 +45,13 @@ typedef struct FadeSound { static void* soundMallocProcDefaultImpl(size_t size); static void* soundReallocProcDefaultImpl(void* ptr, size_t size); static void soundFreeProcDefaultImpl(void* ptr); +static long soundFileSize(int fileHandle); +static long soundTellData(int fileHandle); +static int soundWriteData(int fileHandle, const void* buf, unsigned int size); +static int soundReadData(int fileHandle, void* buf, unsigned int size); +static int soundOpenData(const char* filePath, int flags); +static long soundSeekData(int fileHandle, long offset, int origin); +static int soundCloseData(int fileHandle); static char* soundFileManglerDefaultImpl(char* fname); static void _refreshSoundBuffers(Sound* sound); static int _preloadBuffers(Sound* sound); @@ -77,13 +84,13 @@ static FreeProc* gSoundFreeProc = soundFreeProcDefaultImpl; // 0x51D494 static SoundFileIO gSoundDefaultFileIO = { - open, - close, - compat_read, - compat_write, - compat_lseek, - compat_tell, - compat_filelength, + soundOpenData, + soundCloseData, + soundReadData, + soundWriteData, + soundSeekData, + soundTellData, + soundFileSize, -1, }; @@ -184,6 +191,55 @@ void soundSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeP gSoundFreeProc = freeProc; } +// 0x4AC71C +static long soundFileSize(int fileHandle) +{ + long pos; + long size; + + pos = compat_tell(fileHandle); + size = lseek(fileHandle, 0, SEEK_END); + lseek(fileHandle, pos, SEEK_SET); + + return size; +} + +// 0x4AC750 +static long soundTellData(int fileHandle) +{ + return compat_tell(fileHandle); +} + +// 0x4AC758 +static int soundWriteData(int fileHandle, const void* buf, unsigned int size) +{ + return write(fileHandle, buf, size); +} + +// 0x4AC760 +static int soundReadData(int fileHandle, void* buf, unsigned int size) +{ + return read(fileHandle, buf, size); +} + +// 0x4AC768 +static int soundOpenData(const char* filePath, int flags) +{ + return open(filePath, flags); +} + +// 0x4AC774 +static long soundSeekData(int fileHandle, long offset, int origin) +{ + return lseek(fileHandle, offset, origin); +} + +// 0x4AC77C +static int soundCloseData(int fileHandle) +{ + return close(fileHandle); +} + // 0x4AC78C char* soundFileManglerDefaultImpl(char* fname) { diff --git a/src/sound.h b/src/sound.h index 57d371c..ab81e63 100644 --- a/src/sound.h +++ b/src/sound.h @@ -46,7 +46,7 @@ typedef enum SoundError { SOUND_ERR_COUNT, } SoundError; -typedef int SoundOpenProc(const char* filePath, int flags, ...); +typedef int SoundOpenProc(const char* filePath, int flags); typedef int SoundCloseProc(int fileHandle); typedef int SoundReadProc(int fileHandle, void* buf, unsigned int size); typedef int SoundWriteProc(int fileHandle, const void* buf, unsigned int size); diff --git a/src/sound_effects_cache.cc b/src/sound_effects_cache.cc index d11603f..26aead6 100644 --- a/src/sound_effects_cache.cc +++ b/src/sound_effects_cache.cc @@ -154,7 +154,7 @@ void soundEffectsCacheFlush() // sfxc_cached_open // 0x4A915C -int soundEffectsCacheFileOpen(const char* fname, int mode, ...) +int soundEffectsCacheFileOpen(const char* fname, int mode) { if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) { return -1; diff --git a/src/sound_effects_cache.h b/src/sound_effects_cache.h index 3650bcd..e6e0972 100644 --- a/src/sound_effects_cache.h +++ b/src/sound_effects_cache.h @@ -11,7 +11,7 @@ int soundEffectsCacheInit(int cache_size, const char* effectsPath); void soundEffectsCacheExit(); int soundEffectsCacheInitialized(); void soundEffectsCacheFlush(); -int soundEffectsCacheFileOpen(const char* fname, int mode, ...); +int soundEffectsCacheFileOpen(const char* fname, int mode); int soundEffectsCacheFileClose(int handle); int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size); int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size); From 0bb822ba60046b673c79259219fd2c5a71e1b2bb Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 15 Feb 2023 09:23:46 +0300 Subject: [PATCH 34/50] Fix "Affect player speed" font Closes #245 --- src/options.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/options.cc b/src/options.cc index 782fc36..f94dc01 100644 --- a/src/options.cc +++ b/src/options.cc @@ -1554,6 +1554,7 @@ static int preferencesWindowInit() fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 283, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); // Affect player speed + fontSetCurrent(101); messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 122); fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 72 + 405, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); From 7ac651b7366c1d27a9e7b7e2fc83567739cc2988 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 15 Feb 2023 11:37:00 +0300 Subject: [PATCH 35/50] Adjust modal dialogs position --- src/dbox.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dbox.cc b/src/dbox.cc index 8ca5d02..eee9345 100644 --- a/src/dbox.cc +++ b/src/dbox.cc @@ -206,8 +206,8 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i } // Maintain original position in original resolution, otherwise center it. - if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundFrmImage.getWidth()) / 2; - if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundFrmImage.getHeight()) / 2; + x += (screenGetWidth() - 640) / 2; + y += (screenGetHeight() - 480) / 2; int win = windowCreate(x, y, backgroundFrmImage.getWidth(), @@ -596,8 +596,8 @@ int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLen int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight(); // Maintain original position in original resolution, otherwise center it. - if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2; - if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2; + x += (screenGetWidth() - 640) / 2; + y += (screenGetHeight() - 480) / 2; int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); if (win == -1) { return -1; @@ -962,8 +962,8 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight(); // Maintain original position in original resolution, otherwise center it. - if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2; - if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2; + x += (screenGetWidth() - 640) / 2; + y += (screenGetHeight() - 480) / 2; int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); if (win == -1) { return -1; From 42346f66b207ed7de302fd7359419f58a6d5fd2c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 15 Feb 2023 12:47:32 +0300 Subject: [PATCH 36/50] Update readme --- README.md | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0b42d7f..e58b082 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ # Fallout 2 Community Edition +Fallout 2 Community Edition is a fully working re-implementation of Fallout 2, with the same original gameplay, engine bugfixes, and some quality of life improvements, that works (mostly) hassle-free on multiple platforms. + +Popular Fallout 2 total conversion mods are partially supported. Original versions of Nevada and Sonora (that do not rely on extended features provided by Sfall) likely work, although there is no complete walkthrough confirmation yet. [Fallout 2 Restoration Project](https://github.com/BGforgeNet/Fallout2_Restoration_Project), [Fallout Et Tu](https://github.com/rotators/Fo1in2) and [Olympus 2207](https://olympus2207.com) are not yet supported. Other mods (particularly Resurrection and Yesterday) are not tested. + +There is also [Fallout Community Edition](https://github.com/alexbatalov/fallout1-ce). + ## Installation -You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2) or [Steam](https://store.steampowered.com/app/38410). Download latest release or build from source. +You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2) or [Steam](https://store.steampowered.com/app/38410). Download latest [release](https://github.com/alexbatalov/fallout2-ce/releases) or build from source. You can also check latest [debug](https://github.com/alexbatalov/fallout2-ce/actions) build intended for testers. ### Windows @@ -38,11 +44,13 @@ $ sudo apt install libsdl2-2.0-0 > **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. When playing on Android you'll use fingers to move mouse cursor, not a character, or a map. Double tap to "click" left mouse button in the current cursor position, triple tap to "click" right mouse button. It might feel awkward at first, but it's super handy - you can play with just a thumb. This is not set in stone and might change in the future. -- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder. +> **NOTE**: From Android standpoint release and debug builds are different apps. Both apps require their own copy of game assets and have their own savegames. This is intentional. As a gamer just stick with release version and check for updates. + +- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder. Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)). - Download `fallout2-ce.apk` and copy it to your device. Open it with file explorer, follow instructions (install from unknown source). -- When you run the game for the first time it will immediately present file picker. Select the folder from the first step. Wait until this data is copied. A loading dialog will appear, just wait for about 30 seconds. The game will start automatically. +- When you run the game for the first time it will immediately present file picker. Select the folder from the first step. Wait until this data is copied. A loading dialog will appear, just wait for about 30 seconds. If you're installing total conversion mod or localized version with a large number of unpacked resources in `data` folder it can take up to 20 minutes. Once copied, the game will start automatically. ### iOS @@ -52,7 +60,31 @@ $ sudo apt install libsdl2-2.0-0 - Run the game once. You'll see error message saying "Couldn't find/load text fonts". This step is needed for iOS to expose the game via File Sharing feature. -- Use Finder (macOS Catalina and later) or iTunes (Windows and macOS Mojave or earlier) to copy `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder to "Fallout 2" app ([how-to](https://support.apple.com/HT210598)). +- Use Finder (macOS Catalina and later) or iTunes (Windows and macOS Mojave or earlier) to copy `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder to "Fallout 2" app ([how-to](https://support.apple.com/HT210598)). Watch for file names - keep (or make) them lowercased (see [Configuration](#configuration)). + +## Configuration + +The main configuration file is `fallout2.cfg`. There are several important settings you might need to adjust for your installation. Depending on your Fallout distribution main game assets `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder might be either all lowercased, or all uppercased. You can either update `master_dat`, `critter_dat`, `master_patches` and `critter_patches` settings to match your file names, or rename files to match entries in your `fallout2.cfg`. + +The `sound` folder (with `music` folder inside) might be located either in `data` folder, or be in the Fallout folder. Update `music_path1` setting to match your hierarchy, usually it's `data/sound/music/` or `sound/music/`. Make sure it matches your path exactly (so it might be `SOUND/MUSIC/` if you've installed Fallout from CD). Music files themselves (with `ACM` extension) should be all uppercased, regardless of `sound` and `music` folders. + +The second configuration file is `f2_res.ini`. Use it to change game window size and enable/disable fullscreen mode. + +```ini +[MAIN] +SCR_WIDTH=1280 +SCR_HEIGHT=720 +WINDOWED=1 +``` + +Recommendations: +- **Desktops**: Use any size you see fit. +- **Tablets**: Set these values to logical resolution of your device, for example iPad Pro 11 is 1668x2388 (pixels), but it's logical resolution is 834x1194 (points). +- **Mobile phones**: Set height to 480, calculate width according to your device screen (aspect) ratio, for example Samsung S21 is 20:9 device, so the width should be 480 * 20 / 9 = 1067. + +In time this stuff will receive in-game interface, right now you have to do it manually. + +The third configuration file is `ddraw.ini` (part of Sfall). There are dozens of options that adjust or override engine behaviour and gameplay mechanics. This file is intended for modders and advanced users. Currently only a small subset of these settings are actually implemented. ## Contributing From 0a9a1dc7cade816842ed98ac026e59f82d4a2052 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 16 Feb 2023 15:46:49 +0300 Subject: [PATCH 37/50] Fix faulty frms Closes #235 --- src/art.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/art.cc b/src/art.cc index 426a33f..bb1a0f3 100644 --- a/src/art.cc +++ b/src/art.cc @@ -1076,6 +1076,11 @@ static int artReadHeader(Art* art, File* stream) if (fileReadInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1; if (fileReadInt32(stream, &(art->dataSize)) == -1) return -1; + // CE: Fix malformed `frm` files with `dataSize` set to 0 in Nevada. + if (art->dataSize == 0) { + art->dataSize = fileGetSize(stream); + } + return 0; } From d7c4589a557afddded8a176ef82d6a796977cc14 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 17 Feb 2023 12:33:26 +0300 Subject: [PATCH 38/50] Extract preferences --- CMakeLists.txt | 2 + src/character_selector.cc | 2 +- src/game.cc | 5 + src/loadsave.cc | 2 +- src/main.cc | 10 +- src/options.cc | 1586 +----------------------------------- src/options.h | 26 - src/preferences.cc | 1617 +++++++++++++++++++++++++++++++++++++ src/preferences.h | 17 + 9 files changed, 1665 insertions(+), 1602 deletions(-) create mode 100644 src/preferences.cc create mode 100644 src/preferences.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 56397b2..b9ae928 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/platform_compat.h" "src/pointer_registry.cc" "src/pointer_registry.h" + "src/preferences.cc" + "src/preferences.h" "src/settings.cc" "src/settings.h" "src/sfall_config.cc" diff --git a/src/character_selector.cc b/src/character_selector.cc index 0a0bece..adcb9d1 100644 --- a/src/character_selector.cc +++ b/src/character_selector.cc @@ -21,9 +21,9 @@ #include "message.h" #include "mouse.h" #include "object.h" -#include "options.h" #include "palette.h" #include "platform_compat.h" +#include "preferences.h" #include "proto.h" #include "settings.h" #include "sfall_config.h" diff --git a/src/game.cc b/src/game.cc index c0ffb89..01c87ec 100644 --- a/src/game.cc +++ b/src/game.cc @@ -50,6 +50,7 @@ #include "perk.h" #include "pipboy.h" #include "platform_compat.h" +#include "preferences.h" #include "proto.h" #include "queue.h" #include "random.h" @@ -1340,6 +1341,10 @@ static int gameDbInit() } } + if (access("f2_res.dat", 0) == 0) { + dbOpen("f2_res.dat", 0, NULL, 1); + } + return 0; } diff --git a/src/loadsave.cc b/src/loadsave.cc index b581086..d469311 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -35,11 +35,11 @@ #include "message.h" #include "mouse.h" #include "object.h" -#include "options.h" #include "party_member.h" #include "perk.h" #include "pipboy.h" #include "platform_compat.h" +#include "preferences.h" #include "proto.h" #include "queue.h" #include "random.h" diff --git a/src/main.cc b/src/main.cc index 03cf886..aff1459 100644 --- a/src/main.cc +++ b/src/main.cc @@ -24,9 +24,9 @@ #include "map.h" #include "mouse.h" #include "object.h" -#include "options.h" #include "palette.h" #include "platform_compat.h" +#include "preferences.h" #include "proto.h" #include "random.h" #include "scripts.h" @@ -290,12 +290,8 @@ int falloutMain(int argc, char** argv) _main_selfrun_play(); break; case MAIN_MENU_OPTIONS: - mainMenuWindowHide(false); - mouseShowCursor(); - showOptionsWithInitialKeyCode(112); - gameMouseSetCursor(MOUSE_CURSOR_ARROW); - mouseShowCursor(); - mainMenuWindowUnhide(0); + mainMenuWindowHide(true); + doPreferences(true); break; case MAIN_MENU_CREDITS: mainMenuWindowHide(true); diff --git a/src/options.cc b/src/options.cc index f94dc01..80b7f6c 100644 --- a/src/options.cc +++ b/src/options.cc @@ -1,22 +1,13 @@ #include "options.h" -#include -#include -#include - -#include - #include "art.h" #include "color.h" -#include "combat.h" -#include "combat_ai.h" #include "cycle.h" #include "debug.h" #include "draw.h" #include "game.h" #include "game_mouse.h" #include "game_sound.h" -#include "geometry.h" #include "graph_lib.h" #include "input.h" #include "kb.h" @@ -24,55 +15,15 @@ #include "memory.h" #include "message.h" #include "mouse.h" -#include "platform_compat.h" -#include "scripts.h" -#include "settings.h" +#include "preferences.h" #include "svga.h" #include "text_font.h" -#include "text_object.h" #include "tile.h" #include "window_manager.h" namespace fallout { -#define PREFERENCES_WINDOW_WIDTH 640 -#define PREFERENCES_WINDOW_HEIGHT 480 - #define OPTIONS_WINDOW_BUTTONS_COUNT (10) -#define PRIMARY_OPTION_VALUE_COUNT (4) -#define SECONDARY_OPTION_VALUE_COUNT (2) - -typedef enum Preference { - PREF_GAME_DIFFICULTY, - PREF_COMBAT_DIFFICULTY, - PREF_VIOLENCE_LEVEL, - PREF_TARGET_HIGHLIGHT, - PREF_COMBAT_LOOKS, - PREF_COMBAT_MESSAGES, - PREF_COMBAT_TAUNTS, - PREF_LANGUAGE_FILTER, - PREF_RUNNING, - PREF_SUBTITLES, - PREF_ITEM_HIGHLIGHT, - PREF_COMBAT_SPEED, - PREF_TEXT_BASE_DELAY, - PREF_MASTER_VOLUME, - PREF_MUSIC_VOLUME, - PREF_SFX_VOLUME, - PREF_SPEECH_VOLUME, - PREF_BRIGHTNESS, - PREF_MOUSE_SENSITIVIY, - PREF_COUNT, - FIRST_PRIMARY_PREF = PREF_GAME_DIFFICULTY, - LAST_PRIMARY_PREF = PREF_COMBAT_LOOKS, - PRIMARY_PREF_COUNT = LAST_PRIMARY_PREF - FIRST_PRIMARY_PREF + 1, - FIRST_SECONDARY_PREF = PREF_COMBAT_MESSAGES, - LAST_SECONDARY_PREF = PREF_ITEM_HIGHLIGHT, - SECONDARY_PREF_COUNT = LAST_SECONDARY_PREF - FIRST_SECONDARY_PREF + 1, - FIRST_RANGE_PREF = PREF_COMBAT_SPEED, - LAST_RANGE_PREF = PREF_MOUSE_SENSITIVIY, - RANGE_PREF_COUNT = LAST_RANGE_PREF - FIRST_RANGE_PREF + 1, -} Preference; typedef enum PauseWindowFrm { PAUSE_WINDOW_FRM_BACKGROUND, @@ -89,116 +40,9 @@ typedef enum OptionsWindowFrm { OPTIONS_WINDOW_FRM_COUNT, } OptionsWindowFrm; -typedef enum PreferencesWindowFrm { - PREFERENCES_WINDOW_FRM_BACKGROUND, - // Knob (for range preferences) - PREFERENCES_WINDOW_FRM_KNOB_OFF, - // 4-way switch (for primary preferences) - PREFERENCES_WINDOW_FRM_PRIMARY_SWITCH, - // 2-way switch (for secondary preferences) - PREFERENCES_WINDOW_FRM_SECONDARY_SWITCH, - PREFERENCES_WINDOW_FRM_CHECKBOX_ON, - PREFERENCES_WINDOW_FRM_CHECKBOX_OFF, - PREFERENCES_WINDOW_FRM_6, - // Active knob (for range preferences) - PREFERENCES_WINDOW_FRM_KNOB_ON, - PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP, - PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN, - PREFERENCES_WINDOW_FRM_COUNT, -} PreferencesWindowFrm; - -typedef struct PreferenceDescription { - // The number of options. - short valuesCount; - - // Direction of rotation: - // 0 - clockwise (incrementing value), - // 1 - counter-clockwise (decrementing value) - short direction; - short knobX; - short knobY; - // Min x coordinate of the preference control bounding box. - short minX; - // Max x coordinate of the preference control bounding box. - short maxX; - short labelIds[PRIMARY_OPTION_VALUE_COUNT]; - int btn; - char name[32]; - double minValue; - double maxValue; - int* valuePtr; -} PreferenceDescription; - static int optionsWindowInit(); static int optionsWindowFree(); static void _ShadeScreen(bool a1); -static void _SetSystemPrefs(); -static void _SaveSettings(); -static void _RestoreSettings(); -static void preferencesSetDefaults(bool a1); -static void _JustUpdate_(); -static void _UpdateThing(int index); -int _SavePrefs(bool save); -static int preferencesWindowInit(); -static int preferencesWindowFree(); -static int _do_prefscreen(); -static void _DoThing(int eventCode); - -// 0x48FBD0 -static const int _row1Ytab[PRIMARY_PREF_COUNT] = { - 48, - 125, - 203, - 286, - 363, -}; - -// 0x48FBDA -static const int _row2Ytab[SECONDARY_PREF_COUNT] = { - 49, - 116, - 181, - 247, - 313, - 380, -}; - -// 0x48FBE6 -static const int _row3Ytab[RANGE_PREF_COUNT] = { - 19, - 94, - 165, - 216, - 268, - 319, - 369, - 420, -}; - -// x offsets for primary preferences from the knob position -// 0x48FBF6 -static const short word_48FBF6[PRIMARY_OPTION_VALUE_COUNT] = { - 2, - 25, - 46, - 46, -}; - -// y offsets for primary preference option values from the knob position -// 0x48FBFE -static const short word_48FBFE[PRIMARY_OPTION_VALUE_COUNT] = { - 10, - -4, - 10, - 31, -}; - -// x offsets for secondary prefrence option values from the knob position -// 0x48FC06 -static const short word_48FC06[SECONDARY_OPTION_VALUE_COUNT] = { - 4, - 21, -}; // 0x48FC0C static const int gPauseWindowFrmIds[PAUSE_WINDOW_FRM_COUNT] = { @@ -208,48 +52,6 @@ static const int gPauseWindowFrmIds[PAUSE_WINDOW_FRM_COUNT] = { 9, // lilreddn.frm - little red button down }; -// y offsets for secondary preferences -// 0x48FC30 -static const int dword_48FC30[SECONDARY_PREF_COUNT] = { - 66, // combat messages - 133, // combat taunts - 200, // language filter - 264, // running - 331, // subtitles - 397, // item highlight -}; - -// y offsets for primary preferences -// 0x48FC1C -static const int dword_48FC1C[PRIMARY_PREF_COUNT] = { - 66, // game difficulty - 143, // combat difficulty - 222, // violence level - 304, // target highlight - 382, // combat looks -}; - -// 0x50C168 -static const double dbl_50C168 = 1.17999267578125; - -// 0x50C170 -static const double dbl_50C170 = 0.01124954223632812; - -// 0x50C178 -static const double dbl_50C178 = -0.01124954223632812; - -// 0x50C180 -static const double dbl_50C180 = 1.17999267578125; - -// 0x50C2D0 -static const double dbl_50C2D0 = -1.0; - -// 0x50C2D8 -static const double dbl_50C2D8 = 0.2; - -// 0x50C2E0 -static const double dbl_50C2E0 = 2.0; - // 0x5197C0 static const int gOptionsWindowFrmIds[OPTIONS_WINDOW_FRM_COUNT] = { 220, // opbase.frm - character editor @@ -257,210 +59,34 @@ static const int gOptionsWindowFrmIds[OPTIONS_WINDOW_FRM_COUNT] = { 221, // opbtnoff.frm - character editor }; -// 0x5197CC -static const int gPreferencesWindowFrmIds[PREFERENCES_WINDOW_FRM_COUNT] = { - 240, // prefscrn.frm - options screen - 241, // prfsldof.frm - options screen - 242, // prfbknbs.frm - options screen - 243, // prflknbs.frm - options screen - 244, // prfxin.frm - options screen - 245, // prfxout.frm - options screen - 246, // prefcvr.frm - options screen - 247, // prfsldon.frm - options screen - 8, // lilredup.frm - little red button up - 9, // lilreddn.frm - little red button down -}; - -// 0x5197F8 -static PreferenceDescription gPreferenceDescriptions[PREF_COUNT] = { - { 3, 0, 76, 71, 0, 0, { 203, 204, 205, 0 }, 0, GAME_CONFIG_GAME_DIFFICULTY_KEY, 0, 0, &gPreferencesGameDifficulty1 }, - { 3, 0, 76, 149, 0, 0, { 206, 204, 208, 0 }, 0, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, 0, 0, &gPreferencesCombatDifficulty1 }, - { 4, 0, 76, 226, 0, 0, { 214, 215, 204, 216 }, 0, GAME_CONFIG_VIOLENCE_LEVEL_KEY, 0, 0, &gPreferencesViolenceLevel1 }, - { 3, 0, 76, 309, 0, 0, { 202, 201, 213, 0 }, 0, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, 0, 0, &gPreferencesTargetHighlight1 }, - { 2, 0, 76, 387, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_COMBAT_LOOKS_KEY, 0, 0, &gPreferencesCombatLooks1 }, - { 2, 0, 299, 74, 0, 0, { 211, 212, 0, 0 }, 0, GAME_CONFIG_COMBAT_MESSAGES_KEY, 0, 0, &gPreferencesCombatMessages1 }, - { 2, 0, 299, 141, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_COMBAT_TAUNTS_KEY, 0, 0, &gPreferencesCombatTaunts1 }, - { 2, 0, 299, 207, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_LANGUAGE_FILTER_KEY, 0, 0, &gPreferencesLanguageFilter1 }, - { 2, 0, 299, 271, 0, 0, { 209, 219, 0, 0 }, 0, GAME_CONFIG_RUNNING_KEY, 0, 0, &gPreferencesRunning1 }, - { 2, 0, 299, 338, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_SUBTITLES_KEY, 0, 0, &gPreferencesSubtitles1 }, - { 2, 0, 299, 404, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, 0, 0, &gPreferencesItemHighlight1 }, - { 2, 0, 374, 50, 0, 0, { 207, 210, 0, 0 }, 0, GAME_CONFIG_COMBAT_SPEED_KEY, 0.0, 50.0, &gPreferencesCombatSpeed1 }, - { 3, 0, 374, 125, 0, 0, { 217, 209, 218, 0 }, 0, GAME_CONFIG_TEXT_BASE_DELAY_KEY, 1.0, 6.0, NULL }, - { 4, 0, 374, 196, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_MASTER_VOLUME_KEY, 0, 32767.0, &gPreferencesMasterVolume1 }, - { 4, 0, 374, 247, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_MUSIC_VOLUME_KEY, 0, 32767.0, &gPreferencesMusicVolume1 }, - { 4, 0, 374, 298, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_SNDFX_VOLUME_KEY, 0, 32767.0, &gPreferencesSoundEffectsVolume1 }, - { 4, 0, 374, 349, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_SPEECH_VOLUME_KEY, 0, 32767.0, &gPreferencesSpeechVolume1 }, - { 2, 0, 374, 400, 0, 0, { 207, 223, 0, 0 }, 0, GAME_CONFIG_BRIGHTNESS_KEY, 1.0, 1.17999267578125, NULL }, - { 2, 0, 374, 451, 0, 0, { 207, 218, 0, 0 }, 0, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, 1.0, 2.5, NULL }, -}; - // 0x6637E8 -static MessageList gOptionsMessageList; +static MessageList gPreferencesMessageList; // 0x663840 -static MessageListItem gOptionsMessageListItem; +static MessageListItem gPreferencesMessageListItem; // 0x663878 static unsigned char* _opbtns[OPTIONS_WINDOW_BUTTONS_COUNT]; -// 0x6638C8 -static double gPreferencesTextBaseDelay2; - -// 0x6638D0 -static double gPreferencesBrightness1; - -// 0x6638D8 -static double gPreferencesBrightness2; - -// 0x6638E0 -static double gPreferencesTextBaseDelay1; - -// 0x6638E8 -static double gPreferencesMouseSensitivity1; - -// 0x6638F0 -static double gPreferencesMouseSensitivity2; - -// 0x6638F8 -static unsigned char* gPreferencesWindowBuffer; - // 0x6638FC static bool gOptionsWindowGameMouseObjectsWasVisible; // 0x663900 static int gOptionsWindow; -// 0x663904 -static int gPreferencesWindow; - // 0x663908 static unsigned char* gOptionsWindowBuffer; -// 0x663924 -static int gPreferencesGameDifficulty2; - -// 0x663928 -static int gPreferencesCombatDifficulty2; - -// 0x66392C -static int gPreferencesViolenceLevel2; - -// 0x663930 -static int gPreferencesTargetHighlight2; - -// 0x663934 -static int gPreferencesCombatLooks2; - -// 0x663938 -static int gPreferencesCombatMessages2; - -// 0x66393C -static int gPreferencesCombatTaunts2; - -// 0x663940 -static int gPreferencesLanguageFilter2; - -// 0x663944 -static int gPreferencesRunning2; - -// 0x663948 -static int gPreferencesSubtitles2; - -// 0x66394C -static int gPreferencesItemHighlight2; - -// 0x663950 -static int gPreferencesCombatSpeed2; - -// 0x663954 -static int gPreferencesPlayerSpeedup2; - -// 0x663958 -static int gPreferencesMasterVolume2; - -// 0x66395C -static int gPreferencesMusicVolume2; - -// 0x663960 -static int gPreferencesSoundEffectsVolume2; - -// 0x663964 -static int gPreferencesSpeechVolume2; - -// 0x663970 -int gPreferencesSoundEffectsVolume1; - -// 0x663974 -int gPreferencesSubtitles1; - -// 0x663978 -int gPreferencesLanguageFilter1; - -// 0x66397C -int gPreferencesSpeechVolume1; - -// 0x663980 -int gPreferencesMasterVolume1; - -// 0x663984 -int gPreferencesPlayerSpeedup1; - -// 0x663988 -int gPreferencesCombatTaunts1; - // 0x66398C static int gOptionsWindowOldFont; -// 0x663990 -int gPreferencesMusicVolume1; - // 0x663994 static bool gOptionsWindowIsoWasEnabled; -// 0x663998 -int gPreferencesRunning1; - -// 0x66399C -int gPreferencesCombatSpeed1; - -// -static int _plyrspdbid; - -// 0x6639A4 -int gPreferencesItemHighlight1; - -// 0x6639A8 -static bool _changed; - -// 0x6639AC -int gPreferencesCombatMessages1; - -// 0x6639B0 -int gPreferencesTargetHighlight1; - -// 0x6639B4 -int gPreferencesCombatDifficulty1; - -// 0x6639B8 -int gPreferencesViolenceLevel1; - -// 0x6639BC -int gPreferencesGameDifficulty1; - -// 0x6639C0 -int gPreferencesCombatLooks1; - static FrmImage _optionsFrmImages[OPTIONS_WINDOW_FRM_COUNT]; -static FrmImage _preferencesFrmImages[PREFERENCES_WINDOW_FRM_COUNT]; - -// 0x48FC48 -int showOptions() -{ - return showOptionsWithInitialKeyCode(-1); -} // 0x48FC50 -int showOptionsWithInitialKeyCode(int initialKeyCode) +int showOptions() { ScopedGameMode gm(GameMode::kOptions); @@ -474,12 +100,6 @@ int showOptionsWithInitialKeyCode(int initialKeyCode) sharedFpsLimiter.mark(); int keyCode = inputGetInput(); - bool showPreferences = false; - - if (initialKeyCode != -1) { - keyCode = initialKeyCode; - rc = 1; - } if (keyCode == KEY_ESCAPE || keyCode == 504 || _game_user_wants_to_quit != 0) { rc = 0; @@ -513,7 +133,7 @@ int showOptionsWithInitialKeyCode(int initialKeyCode) // FALLTHROUGH case 502: // PREFERENCES - showPreferences = true; + doPreferences(false); break; case KEY_PLUS: case KEY_EQUAL: @@ -523,13 +143,6 @@ int showOptionsWithInitialKeyCode(int initialKeyCode) case KEY_MINUS: brightnessDecrease(); break; - } - } - - if (showPreferences) { - _do_prefscreen(); - } else { - switch (keyCode) { case KEY_F12: takeScreenshot(); break; @@ -558,13 +171,13 @@ static int optionsWindowInit() { gOptionsWindowOldFont = fontGetCurrent(); - if (!messageListInit(&gOptionsMessageList)) { + if (!messageListInit(&gPreferencesMessageList)) { return -1; } char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s%s", asc_5186C8, "options.msg"); - if (!messageListLoad(&gOptionsMessageList, path)) { + if (!messageListLoad(&gPreferencesMessageList, path)) { return -1; } @@ -575,7 +188,7 @@ static int optionsWindowInit() _optionsFrmImages[index].unlock(); } - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); return -1; } @@ -593,7 +206,7 @@ static int optionsWindowInit() _optionsFrmImages[index].unlock(); } - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); return -1; } @@ -621,7 +234,7 @@ static int optionsWindowInit() _optionsFrmImages[index].unlock(); } - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); return -1; } @@ -646,7 +259,7 @@ static int optionsWindowInit() for (int index = 0; index < OPTIONS_WINDOW_BUTTONS_COUNT; index += 2) { char text[128]; - const char* msg = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, index / 2); + const char* msg = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, index / 2); strcpy(text, msg); int textX = (_optionsFrmImages[OPTIONS_WINDOW_FRM_BUTTON_ON].getWidth() - fontGetStringWidth(text)) / 2; @@ -689,7 +302,7 @@ static int optionsWindowFree() { windowDestroy(gOptionsWindow); fontSetCurrent(gOptionsWindowOldFont); - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); for (int index = 0; index < OPTIONS_WINDOW_BUTTONS_COUNT; index++) { internal_free(_opbtns[index]); @@ -736,14 +349,14 @@ int showPause(bool a1) } } - if (!messageListInit(&gOptionsMessageList)) { + if (!messageListInit(&gPreferencesMessageList)) { // FIXME: Leaking graphics. return -1; } char path[COMPAT_MAX_PATH]; snprintf(path, sizeof(path), "%s%s", asc_5186C8, "options.msg"); - if (!messageListLoad(&gOptionsMessageList, path)) { + if (!messageListLoad(&gPreferencesMessageList, path)) { // FIXME: Leaking graphics. return -1; } @@ -765,7 +378,7 @@ int showPause(bool a1) 256, WINDOW_MODAL | WINDOW_DONT_MOVE_TOP); if (window == -1) { - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); debugPrint("\n** Error opening pause window! **\n"); return -1; @@ -788,7 +401,7 @@ int showPause(bool a1) char* messageItemText; - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 300); + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 300); fontDrawText(windowBuffer + frmImages[PAUSE_WINDOW_FRM_BACKGROUND].getWidth() * 45 + 52, messageItemText, frmImages[PAUSE_WINDOW_FRM_BACKGROUND].getWidth(), @@ -797,7 +410,7 @@ int showPause(bool a1) fontSetCurrent(104); - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 301); + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 301); strcpy(path, messageItemText); int length = fontGetStringWidth(path); @@ -859,7 +472,7 @@ int showPause(bool a1) } windowDestroy(window); - messageListFree(&gOptionsMessageList); + messageListFree(&gPreferencesMessageList); if (!a1) { if (gameMouseWasVisible) { @@ -900,1176 +513,15 @@ static void _ShadeScreen(bool a1) mouseShowCursor(); } -// 0x492AA8 -static void _SetSystemPrefs() -{ - preferencesSetDefaults(false); - - gPreferencesGameDifficulty1 = settings.preferences.game_difficulty; - gPreferencesCombatDifficulty1 = settings.preferences.combat_difficulty; - gPreferencesViolenceLevel1 = settings.preferences.violence_level; - gPreferencesTargetHighlight1 = settings.preferences.target_highlight; - gPreferencesCombatMessages1 = settings.preferences.combat_messages; - gPreferencesCombatLooks1 = settings.preferences.combat_looks; - gPreferencesCombatTaunts1 = settings.preferences.combat_taunts; - gPreferencesLanguageFilter1 = settings.preferences.language_filter; - gPreferencesRunning1 = settings.preferences.running; - gPreferencesSubtitles1 = settings.preferences.subtitles; - gPreferencesItemHighlight1 = settings.preferences.item_highlight; - gPreferencesCombatSpeed1 = settings.preferences.combat_speed; - gPreferencesTextBaseDelay1 = settings.preferences.text_base_delay; - gPreferencesPlayerSpeedup1 = settings.preferences.player_speedup; - gPreferencesMasterVolume1 = settings.sound.master_volume; - gPreferencesMusicVolume1 = settings.sound.music_volume; - gPreferencesSoundEffectsVolume1 = settings.sound.sndfx_volume; - gPreferencesSpeechVolume1 = settings.sound.speech_volume; - gPreferencesBrightness1 = settings.preferences.brightness; - gPreferencesMouseSensitivity1 = settings.preferences.mouse_sensitivity; - - _JustUpdate_(); -} - -// Copy options (1) to (2). -// -// 0x493054 -static void _SaveSettings() -{ - gPreferencesGameDifficulty2 = gPreferencesGameDifficulty1; - gPreferencesCombatDifficulty2 = gPreferencesCombatDifficulty1; - gPreferencesViolenceLevel2 = gPreferencesViolenceLevel1; - gPreferencesTargetHighlight2 = gPreferencesTargetHighlight1; - gPreferencesCombatLooks2 = gPreferencesCombatLooks1; - gPreferencesCombatMessages2 = gPreferencesCombatMessages1; - gPreferencesCombatTaunts2 = gPreferencesCombatTaunts1; - gPreferencesLanguageFilter2 = gPreferencesLanguageFilter1; - gPreferencesRunning2 = gPreferencesRunning1; - gPreferencesSubtitles2 = gPreferencesSubtitles1; - gPreferencesItemHighlight2 = gPreferencesItemHighlight1; - gPreferencesCombatSpeed2 = gPreferencesCombatSpeed1; - gPreferencesPlayerSpeedup2 = gPreferencesPlayerSpeedup1; - gPreferencesMasterVolume2 = gPreferencesMasterVolume1; - gPreferencesTextBaseDelay2 = gPreferencesTextBaseDelay1; - gPreferencesMusicVolume2 = gPreferencesMusicVolume1; - gPreferencesBrightness2 = gPreferencesBrightness1; - gPreferencesSoundEffectsVolume2 = gPreferencesSoundEffectsVolume1; - gPreferencesMouseSensitivity2 = gPreferencesMouseSensitivity1; - gPreferencesSpeechVolume2 = gPreferencesSpeechVolume1; -} - -// Copy options (2) to (1). -// -// 0x493128 -static void _RestoreSettings() -{ - gPreferencesGameDifficulty1 = gPreferencesGameDifficulty2; - gPreferencesCombatDifficulty1 = gPreferencesCombatDifficulty2; - gPreferencesViolenceLevel1 = gPreferencesViolenceLevel2; - gPreferencesTargetHighlight1 = gPreferencesTargetHighlight2; - gPreferencesCombatLooks1 = gPreferencesCombatLooks2; - gPreferencesCombatMessages1 = gPreferencesCombatMessages2; - gPreferencesCombatTaunts1 = gPreferencesCombatTaunts2; - gPreferencesLanguageFilter1 = gPreferencesLanguageFilter2; - gPreferencesRunning1 = gPreferencesRunning2; - gPreferencesSubtitles1 = gPreferencesSubtitles2; - gPreferencesItemHighlight1 = gPreferencesItemHighlight2; - gPreferencesCombatSpeed1 = gPreferencesCombatSpeed2; - gPreferencesPlayerSpeedup1 = gPreferencesPlayerSpeedup2; - gPreferencesMasterVolume1 = gPreferencesMasterVolume2; - gPreferencesTextBaseDelay1 = gPreferencesTextBaseDelay2; - gPreferencesMusicVolume1 = gPreferencesMusicVolume2; - gPreferencesBrightness1 = gPreferencesBrightness2; - gPreferencesSoundEffectsVolume1 = gPreferencesSoundEffectsVolume2; - gPreferencesMouseSensitivity1 = gPreferencesMouseSensitivity2; - gPreferencesSpeechVolume1 = gPreferencesSpeechVolume2; - - _JustUpdate_(); -} - -// 0x492F60 -static void preferencesSetDefaults(bool a1) -{ - gPreferencesCombatDifficulty1 = COMBAT_DIFFICULTY_NORMAL; - gPreferencesViolenceLevel1 = VIOLENCE_LEVEL_MAXIMUM_BLOOD; - gPreferencesTargetHighlight1 = TARGET_HIGHLIGHT_TARGETING_ONLY; - gPreferencesCombatMessages1 = 1; - gPreferencesCombatLooks1 = 0; - gPreferencesCombatTaunts1 = 1; - gPreferencesRunning1 = 0; - gPreferencesSubtitles1 = 0; - gPreferencesItemHighlight1 = 1; - gPreferencesCombatSpeed1 = 0; - gPreferencesPlayerSpeedup1 = 0; - gPreferencesTextBaseDelay1 = 3.5; - gPreferencesBrightness1 = 1.0; - gPreferencesMouseSensitivity1 = 1.0; - gPreferencesGameDifficulty1 = 1; - gPreferencesLanguageFilter1 = 0; - gPreferencesMasterVolume1 = 22281; - gPreferencesMusicVolume1 = 22281; - gPreferencesSoundEffectsVolume1 = 22281; - gPreferencesSpeechVolume1 = 22281; - - if (a1) { - for (int index = 0; index < PREF_COUNT; index++) { - _UpdateThing(index); - } - _win_set_button_rest_state(_plyrspdbid, gPreferencesPlayerSpeedup1, 0); - windowRefresh(gPreferencesWindow); - _changed = true; - } -} - -// 0x4931F8 -static void _JustUpdate_() -{ - gPreferencesGameDifficulty1 = std::clamp(gPreferencesGameDifficulty1, 0, 2); - gPreferencesCombatDifficulty1 = std::clamp(gPreferencesCombatDifficulty1, 0, 2); - gPreferencesViolenceLevel1 = std::clamp(gPreferencesViolenceLevel1, 0, 3); - gPreferencesTargetHighlight1 = std::clamp(gPreferencesTargetHighlight1, 0, 2); - gPreferencesCombatMessages1 = std::clamp(gPreferencesCombatMessages1, 0, 1); - gPreferencesCombatLooks1 = std::clamp(gPreferencesCombatLooks1, 0, 1); - gPreferencesCombatTaunts1 = std::clamp(gPreferencesCombatTaunts1, 0, 1); - gPreferencesLanguageFilter1 = std::clamp(gPreferencesLanguageFilter1, 0, 1); - gPreferencesRunning1 = std::clamp(gPreferencesRunning1, 0, 1); - gPreferencesSubtitles1 = std::clamp(gPreferencesSubtitles1, 0, 1); - gPreferencesItemHighlight1 = std::clamp(gPreferencesItemHighlight1, 0, 1); - gPreferencesCombatSpeed1 = std::clamp(gPreferencesCombatSpeed1, 0, 50); - gPreferencesPlayerSpeedup1 = std::clamp(gPreferencesPlayerSpeedup1, 0, 1); - gPreferencesTextBaseDelay1 = std::clamp(gPreferencesTextBaseDelay1, 6.0, 10.0); - gPreferencesMasterVolume1 = std::clamp(gPreferencesMasterVolume1, 0, VOLUME_MAX); - gPreferencesMusicVolume1 = std::clamp(gPreferencesMusicVolume1, 0, VOLUME_MAX); - gPreferencesSoundEffectsVolume1 = std::clamp(gPreferencesSoundEffectsVolume1, 0, VOLUME_MAX); - gPreferencesSpeechVolume1 = std::clamp(gPreferencesSpeechVolume1, 0, VOLUME_MAX); - gPreferencesBrightness1 = std::clamp(gPreferencesBrightness1, 1.0, 1.17999267578125); - gPreferencesMouseSensitivity1 = std::clamp(gPreferencesMouseSensitivity1, 1.0, 2.5); - - textObjectsSetBaseDelay(gPreferencesTextBaseDelay1); - gameMouseLoadItemHighlight(); - - double textLineDelay = (gPreferencesTextBaseDelay1 + (-1.0)) * 0.2 * 2.0; - textLineDelay = std::clamp(textLineDelay, 0.0, 2.0); - - textObjectsSetLineDelay(textLineDelay); - aiMessageListReloadIfNeeded(); - _scr_message_free(); - gameSoundSetMasterVolume(gPreferencesMasterVolume1); - backgroundSoundSetVolume(gPreferencesMusicVolume1); - soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); - speechSetVolume(gPreferencesSpeechVolume1); - mouseSetSensitivity(gPreferencesMouseSensitivity1); - colorSetBrightness(gPreferencesBrightness1); -} - // init_options_menu // 0x4928B8 int _init_options_menu() { - for (int index = 0; index < 11; index++) { - gPreferenceDescriptions[index].direction = 0; - } - - _SetSystemPrefs(); + preferencesInit(); grayscalePaletteUpdate(0, 255); return 0; } -// 0x491A68 -static void _UpdateThing(int index) -{ - fontSetCurrent(101); - - PreferenceDescription* meta = &(gPreferenceDescriptions[index]); - - if (index >= FIRST_PRIMARY_PREF && index <= LAST_PRIMARY_PREF) { - int primaryOptionIndex = index - FIRST_PRIMARY_PREF; - - int offsets[PRIMARY_PREF_COUNT]; - memcpy(offsets, dword_48FC1C, sizeof(dword_48FC1C)); - - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * offsets[primaryOptionIndex] + 23, 160, 54, 640, gPreferencesWindowBuffer + 640 * offsets[primaryOptionIndex] + 23, 640); - - for (int valueIndex = 0; valueIndex < meta->valuesCount; valueIndex++) { - const char* text = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, meta->labelIds[valueIndex]); - - char copy[100]; // TODO: Size is probably wrong. - strcpy(copy, text); - - int x = meta->knobX + word_48FBF6[valueIndex]; - int len = fontGetStringWidth(copy); - switch (valueIndex) { - case 0: - x -= fontGetStringWidth(copy); - meta->minX = x; - break; - case 1: - x -= len / 2; - meta->maxX = x + len; - break; - case 2: - case 3: - meta->maxX = x + len; - break; - } - - char* p = copy; - while (*p != '\0' && *p != ' ') { - p++; - } - - int y = meta->knobY + word_48FBFE[valueIndex]; - const char* s; - if (*p != '\0') { - *p = '\0'; - fontDrawText(gPreferencesWindowBuffer + 640 * y + x, copy, 640, 640, _colorTable[18979]); - s = p + 1; - y += fontGetLineHeight(); - } else { - s = copy; - } - - fontDrawText(gPreferencesWindowBuffer + 640 * y + x, s, 640, 640, _colorTable[18979]); - } - - int value = *(meta->valuePtr); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_PRIMARY_SWITCH].getData() + (46 * 47) * value, 46, 47, 46, gPreferencesWindowBuffer + 640 * meta->knobY + meta->knobX, 640); - } else if (index >= FIRST_SECONDARY_PREF && index <= LAST_SECONDARY_PREF) { - int secondaryOptionIndex = index - FIRST_SECONDARY_PREF; - - int offsets[SECONDARY_PREF_COUNT]; - memcpy(offsets, dword_48FC30, sizeof(dword_48FC30)); - - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * offsets[secondaryOptionIndex] + 251, 113, 34, 640, gPreferencesWindowBuffer + 640 * offsets[secondaryOptionIndex] + 251, 640); - - // Secondary options are booleans, so it's index is also it's value. - for (int value = 0; value < 2; value++) { - const char* text = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, meta->labelIds[value]); - - int x; - if (value) { - x = meta->knobX + word_48FC06[value]; - meta->maxX = x + fontGetStringWidth(text); - } else { - x = meta->knobX + word_48FC06[value] - fontGetStringWidth(text); - meta->minX = x; - } - fontDrawText(gPreferencesWindowBuffer + 640 * (meta->knobY - 5) + x, text, 640, 640, _colorTable[18979]); - } - - int value = *(meta->valuePtr); - if (index == PREF_COMBAT_MESSAGES) { - value ^= 1; - } - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_SECONDARY_SWITCH].getData() + (22 * 25) * value, 22, 25, 22, gPreferencesWindowBuffer + 640 * meta->knobY + meta->knobX, 640); - } else if (index >= FIRST_RANGE_PREF && index <= LAST_RANGE_PREF) { - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * (meta->knobY - 12) + 384, 240, 24, 640, gPreferencesWindowBuffer + 640 * (meta->knobY - 12) + 384, 640); - switch (index) { - case PREF_COMBAT_SPEED: - if (1) { - double value = *meta->valuePtr; - value = std::clamp(value, 0.0, 50.0); - - int x = (int)((value - meta->minValue) * 219.0 / (meta->maxValue - meta->minValue) + 384.0); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); - } - break; - case PREF_TEXT_BASE_DELAY: - if (1) { - gPreferencesTextBaseDelay1 = std::clamp(gPreferencesTextBaseDelay1, 1.0, 6.0); - - int x = (int)((6.0 - gPreferencesTextBaseDelay1) * 43.8 + 384.0); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); - - double value = (gPreferencesTextBaseDelay1 - 1.0) * 0.2 * 2.0; - value = std::clamp(value, 0.0, 2.0); - - textObjectsSetBaseDelay(gPreferencesTextBaseDelay1); - textObjectsSetLineDelay(value); - } - break; - case PREF_MASTER_VOLUME: - case PREF_MUSIC_VOLUME: - case PREF_SFX_VOLUME: - case PREF_SPEECH_VOLUME: - if (1) { - double value = *meta->valuePtr; - value = std::clamp(value, meta->minValue, meta->maxValue); - - int x = (int)((value - meta->minValue) * 219.0 / (meta->maxValue - meta->minValue) + 384.0); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); - - switch (index) { - case PREF_MASTER_VOLUME: - gameSoundSetMasterVolume(gPreferencesMasterVolume1); - break; - case PREF_MUSIC_VOLUME: - backgroundSoundSetVolume(gPreferencesMusicVolume1); - break; - case PREF_SFX_VOLUME: - soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); - break; - case PREF_SPEECH_VOLUME: - speechSetVolume(gPreferencesSpeechVolume1); - break; - } - } - break; - case PREF_BRIGHTNESS: - if (1) { - gPreferencesBrightness1 = std::clamp(gPreferencesBrightness1, 1.0, 1.17999267578125); - - int x = (int)((gPreferencesBrightness1 - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); - - colorSetBrightness(gPreferencesBrightness1); - } - break; - case PREF_MOUSE_SENSITIVIY: - if (1) { - gPreferencesMouseSensitivity1 = std::clamp(gPreferencesMouseSensitivity1, 1.0, 2.5); - - int x = (int)((gPreferencesMouseSensitivity1 - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); - - mouseSetSensitivity(gPreferencesMouseSensitivity1); - } - break; - } - - for (int optionIndex = 0; optionIndex < meta->valuesCount; optionIndex++) { - const char* str = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, meta->labelIds[optionIndex]); - - int x; - switch (optionIndex) { - case 0: - // 0x4926AA - x = 384; - // TODO: Incomplete. - break; - case 1: - // 0x4926F3 - switch (meta->valuesCount) { - case 2: - x = 624 - fontGetStringWidth(str); - break; - case 3: - // This code path does not use floating-point arithmetic - x = 504 - fontGetStringWidth(str) / 2 - 2; - break; - case 4: - // Uses floating-point arithmetic - x = 444 + fontGetStringWidth(str) / 2 - 8; - break; - } - break; - case 2: - // 0x492766 - switch (meta->valuesCount) { - case 3: - x = 624 - fontGetStringWidth(str); - break; - case 4: - // Uses floating-point arithmetic - x = 564 - fontGetStringWidth(str) - 4; - break; - } - break; - case 3: - // 0x49279E - x = 624 - fontGetStringWidth(str); - break; - } - fontDrawText(gPreferencesWindowBuffer + 640 * (meta->knobY - 12) + x, str, 640, 640, _colorTable[18979]); - } - } else { - // return false; - } - - // TODO: Incomplete. - - // return true; -} - -// 0x492CB0 -int _SavePrefs(bool save) -{ - settings.preferences.game_difficulty = gPreferencesGameDifficulty1; - settings.preferences.combat_difficulty = gPreferencesCombatDifficulty1; - settings.preferences.violence_level = gPreferencesViolenceLevel1; - settings.preferences.target_highlight = gPreferencesTargetHighlight1; - settings.preferences.combat_messages = gPreferencesCombatMessages1; - settings.preferences.combat_looks = gPreferencesCombatLooks1; - settings.preferences.combat_taunts = gPreferencesCombatTaunts1; - settings.preferences.language_filter = gPreferencesLanguageFilter1; - settings.preferences.running = gPreferencesRunning1; - settings.preferences.subtitles = gPreferencesSubtitles1; - settings.preferences.item_highlight = gPreferencesItemHighlight1; - settings.preferences.combat_speed = gPreferencesCombatSpeed1; - settings.preferences.text_base_delay = gPreferencesTextBaseDelay1; - - double textLineDelay = (gPreferencesTextBaseDelay1 + dbl_50C2D0) * dbl_50C2D8 * dbl_50C2E0; - if (textLineDelay >= 0.0) { - if (textLineDelay > dbl_50C2E0) { - textLineDelay = 2.0; - } - - settings.preferences.text_line_delay = textLineDelay; - } else { - settings.preferences.text_line_delay = 0.0; - } - - settings.preferences.player_speedup = gPreferencesPlayerSpeedup1; - settings.sound.master_volume = gPreferencesMasterVolume1; - settings.sound.music_volume = gPreferencesMusicVolume1; - settings.sound.sndfx_volume = gPreferencesSoundEffectsVolume1; - settings.sound.speech_volume = gPreferencesSpeechVolume1; - - settings.preferences.brightness = gPreferencesBrightness1; - settings.preferences.mouse_sensitivity = gPreferencesMouseSensitivity1; - - if (save) { - settingsSave(); - } - - return 0; -} - -// 0x493224 -int preferencesSave(File* stream) -{ - float textBaseDelay = (float)gPreferencesTextBaseDelay1; - float brightness = (float)gPreferencesBrightness1; - float mouseSensitivity = (float)gPreferencesMouseSensitivity1; - - if (fileWriteInt32(stream, gPreferencesGameDifficulty1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesCombatDifficulty1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesViolenceLevel1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesTargetHighlight1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesCombatLooks1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesCombatMessages1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesCombatTaunts1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesLanguageFilter1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesRunning1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesSubtitles1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesItemHighlight1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesCombatSpeed1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesPlayerSpeedup1) == -1) goto err; - if (fileWriteFloat(stream, textBaseDelay) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesMasterVolume1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesMusicVolume1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesSoundEffectsVolume1) == -1) goto err; - if (fileWriteInt32(stream, gPreferencesSpeechVolume1) == -1) goto err; - if (fileWriteFloat(stream, brightness) == -1) goto err; - if (fileWriteFloat(stream, mouseSensitivity) == -1) goto err; - - return 0; - -err: - - debugPrint("\nOPTION MENU: Error save option data!\n"); - - return -1; -} - -// 0x49340C -int preferencesLoad(File* stream) -{ - float textBaseDelay; - float brightness; - float mouseSensitivity; - - preferencesSetDefaults(false); - - if (fileReadInt32(stream, &gPreferencesGameDifficulty1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesCombatDifficulty1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesViolenceLevel1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesTargetHighlight1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesCombatLooks1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesCombatMessages1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesCombatTaunts1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesLanguageFilter1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesRunning1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesSubtitles1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesItemHighlight1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesCombatSpeed1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesPlayerSpeedup1) == -1) goto err; - if (fileReadFloat(stream, &textBaseDelay) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesMasterVolume1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesMusicVolume1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesSoundEffectsVolume1) == -1) goto err; - if (fileReadInt32(stream, &gPreferencesSpeechVolume1) == -1) goto err; - if (fileReadFloat(stream, &brightness) == -1) goto err; - if (fileReadFloat(stream, &mouseSensitivity) == -1) goto err; - - gPreferencesBrightness1 = brightness; - gPreferencesMouseSensitivity1 = mouseSensitivity; - gPreferencesTextBaseDelay1 = textBaseDelay; - - _JustUpdate_(); - _SavePrefs(0); - - return 0; - -err: - - debugPrint("\nOPTION MENU: Error loading option data!, using defaults.\n"); - - preferencesSetDefaults(false); - _JustUpdate_(); - _SavePrefs(0); - - return -1; -} - -// 0x4928E4 -void brightnessIncrease() -{ - gPreferencesBrightness1 = settings.preferences.brightness; - - if (gPreferencesBrightness1 < dbl_50C168) { - gPreferencesBrightness1 += dbl_50C170; - - if (gPreferencesBrightness1 >= 1.0) { - if (gPreferencesBrightness1 > dbl_50C168) { - gPreferencesBrightness1 = dbl_50C168; - } - } else { - gPreferencesBrightness1 = 1.0; - } - - colorSetBrightness(gPreferencesBrightness1); - - settings.preferences.brightness = gPreferencesBrightness1; - - settingsSave(); - } -} - -// 0x4929C8 -void brightnessDecrease() -{ - gPreferencesBrightness1 = settings.preferences.brightness; - - if (gPreferencesBrightness1 > 1.0) { - gPreferencesBrightness1 += dbl_50C178; - - if (gPreferencesBrightness1 >= 1.0) { - if (gPreferencesBrightness1 > dbl_50C180) { - gPreferencesBrightness1 = dbl_50C180; - } - } else { - gPreferencesBrightness1 = 1.0; - } - - colorSetBrightness(gPreferencesBrightness1); - - settings.preferences.brightness = gPreferencesBrightness1; - - settingsSave(); - } -} - -// 0x4908A0 -static int preferencesWindowInit() -{ - int i; - int fid; - char* messageItemText; - int x; - int y; - int width; - int height; - int messageItemId; - int btn; - - _SaveSettings(); - - for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) { - fid = buildFid(OBJ_TYPE_INTERFACE, gPreferencesWindowFrmIds[i], 0, 0, 0); - if (!_preferencesFrmImages[i].lock(fid)) { - while (--i >= 0) { - _preferencesFrmImages[i].unlock(); - } - return -1; - } - } - - _changed = false; - - int preferencesWindowX = (screenGetWidth() - PREFERENCES_WINDOW_WIDTH) / 2; - int preferencesWindowY = (screenGetHeight() - PREFERENCES_WINDOW_HEIGHT) / 2; - gPreferencesWindow = windowCreate(preferencesWindowX, - preferencesWindowY, - PREFERENCES_WINDOW_WIDTH, - PREFERENCES_WINDOW_HEIGHT, - 256, - WINDOW_MODAL | WINDOW_DONT_MOVE_TOP); - if (gPreferencesWindow == -1) { - for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) { - _preferencesFrmImages[i].unlock(); - } - return -1; - } - - gPreferencesWindowBuffer = windowGetBuffer(gPreferencesWindow); - memcpy(gPreferencesWindowBuffer, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getWidth() * _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getHeight()); - - fontSetCurrent(104); - - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 100); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 10 + 74, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - - fontSetCurrent(103); - - messageItemId = 101; - for (i = 0; i < PRIMARY_PREF_COUNT; i++) { - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, messageItemId++); - x = 99 - fontGetStringWidth(messageItemText) / 2; - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row1Ytab[i] + x, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - } - - for (i = 0; i < SECONDARY_PREF_COUNT; i++) { - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, messageItemId++); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row2Ytab[i] + 206, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - } - - for (i = 0; i < RANGE_PREF_COUNT; i++) { - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, messageItemId++); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row3Ytab[i] + 384, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - } - - // DEFAULT - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 120); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 43, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - - // DONE - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 4); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 169, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - - // CANCEL - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 121); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 283, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - - // Affect player speed - fontSetCurrent(101); - messageItemText = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, 122); - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 72 + 405, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - - for (i = 0; i < PREF_COUNT; i++) { - _UpdateThing(i); - } - - for (i = 0; i < PREF_COUNT; i++) { - int mouseEnterEventCode; - int mouseExitEventCode; - int mouseDownEventCode; - int mouseUpEventCode; - - if (i >= FIRST_RANGE_PREF) { - x = 384; - y = gPreferenceDescriptions[i].knobY - 12; - width = 240; - height = 23; - mouseEnterEventCode = 526; - mouseExitEventCode = 526; - mouseDownEventCode = 505 + i; - mouseUpEventCode = 526; - - } else if (i >= FIRST_SECONDARY_PREF) { - x = gPreferenceDescriptions[i].minX; - y = gPreferenceDescriptions[i].knobY - 5; - width = gPreferenceDescriptions[i].maxX - x; - height = 28; - mouseEnterEventCode = -1; - mouseExitEventCode = -1; - mouseDownEventCode = -1; - mouseUpEventCode = 505 + i; - } else { - x = gPreferenceDescriptions[i].minX; - y = gPreferenceDescriptions[i].knobY - 4; - width = gPreferenceDescriptions[i].maxX - x; - height = 48; - mouseEnterEventCode = -1; - mouseExitEventCode = -1; - mouseDownEventCode = -1; - mouseUpEventCode = 505 + i; - } - - gPreferenceDescriptions[i].btn = buttonCreate(gPreferencesWindow, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, NULL, NULL, NULL, 32); - } - - _plyrspdbid = buttonCreate(gPreferencesWindow, - 383, - 68, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_OFF].getWidth(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_ON].getHeight(), - -1, - -1, - 524, - 524, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_OFF].getData(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_ON].getData(), - NULL, - BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01 | BUTTON_FLAG_0x02); - if (_plyrspdbid != -1) { - _win_set_button_rest_state(_plyrspdbid, gPreferencesPlayerSpeedup1, 0); - } - - buttonSetCallbacks(_plyrspdbid, _gsound_med_butt_press, _gsound_med_butt_press); - - // DEFAULT - btn = buttonCreate(gPreferencesWindow, - 23, - 450, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), - -1, - -1, - -1, - 527, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), - NULL, - BUTTON_FLAG_TRANSPARENT); - if (btn != -1) { - buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); - } - - // DONE - btn = buttonCreate(gPreferencesWindow, - 148, - 450, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), - -1, - -1, - -1, - 504, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), - NULL, - BUTTON_FLAG_TRANSPARENT); - if (btn != -1) { - buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); - } - - // CANCEL - btn = buttonCreate(gPreferencesWindow, - 263, - 450, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), - -1, - -1, - -1, - 528, - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), - _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), - NULL, - BUTTON_FLAG_TRANSPARENT); - if (btn != -1) { - buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); - } - - fontSetCurrent(101); - - windowRefresh(gPreferencesWindow); - - return 0; -} - -// 0x492870 -static int preferencesWindowFree() -{ - if (_changed) { - _SavePrefs(1); - _JustUpdate_(); - _combat_highlight_change(); - } - - windowDestroy(gPreferencesWindow); - - for (int index = 0; index < PREFERENCES_WINDOW_FRM_COUNT; index++) { - _preferencesFrmImages[index].unlock(); - } - - return 0; -} - -// 0x490798 -static int _do_prefscreen() -{ - ScopedGameMode gm(GameMode::kPreferences); - - if (preferencesWindowInit() == -1) { - debugPrint("\nPREFERENCE MENU: Error loading preference dialog data!\n"); - return -1; - } - - int rc = -1; - while (rc == -1) { - sharedFpsLimiter.mark(); - - int eventCode = inputGetInput(); - - switch (eventCode) { - case KEY_RETURN: - case KEY_UPPERCASE_P: - case KEY_LOWERCASE_P: - soundPlayFile("ib1p1xx1"); - // FALLTHROUGH - case 504: - rc = 1; - break; - case KEY_CTRL_Q: - case KEY_CTRL_X: - case KEY_F10: - showQuitConfirmationDialog(); - break; - case KEY_EQUAL: - case KEY_PLUS: - brightnessIncrease(); - break; - case KEY_MINUS: - case KEY_UNDERSCORE: - brightnessDecrease(); - break; - case KEY_F12: - takeScreenshot(); - break; - case 527: - preferencesSetDefaults(true); - break; - default: - if (eventCode == KEY_ESCAPE || eventCode == 528 || _game_user_wants_to_quit != 0) { - _RestoreSettings(); - rc = 0; - } else if (eventCode >= 505 && eventCode <= 524) { - _DoThing(eventCode); - } - break; - } - - renderPresent(); - sharedFpsLimiter.throttle(); - } - - preferencesWindowFree(); - - return rc; -} - -// 0x490E8C -static void _DoThing(int eventCode) -{ - int x; - int y; - mouseGetPositionInWindow(gPreferencesWindow, &x, &y); - - // This preference index also contains out-of-bounds value 19, - // which is the only preference expressed as checkbox. - int preferenceIndex = eventCode - 505; - - if (preferenceIndex >= FIRST_PRIMARY_PREF && preferenceIndex <= LAST_PRIMARY_PREF) { - PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); - int* valuePtr = meta->valuePtr; - int value = *valuePtr; - bool valueChanged = false; - - int v1 = meta->knobX + 23; - int v2 = meta->knobY + 21; - - if (sqrt(pow((double)x - (double)v1, 2) + pow((double)y - (double)v2, 2)) > 16.0) { - if (y > meta->knobY) { - int v14 = meta->knobY + word_48FBFE[0]; - if (y >= v14 && y <= v14 + fontGetLineHeight()) { - if (x >= meta->minX && x <= meta->knobX) { - *valuePtr = 0; - meta->direction = 0; - valueChanged = true; - } else { - if (meta->valuesCount >= 3 && x >= meta->knobX + word_48FBF6[2] && x <= meta->maxX) { - *valuePtr = 2; - meta->direction = 0; - valueChanged = true; - } - } - } - } else { - if (x >= meta->knobX + 9 && x <= meta->knobX + 37) { - *valuePtr = 1; - if (value != 0) { - meta->direction = 1; - } else { - meta->direction = 0; - } - valueChanged = true; - } - } - - if (meta->valuesCount == 4) { - int v19 = meta->knobY + word_48FBFE[3]; - if (y >= v19 && y <= v19 + 2 * fontGetLineHeight() && x >= meta->knobX + word_48FBF6[3] && x <= meta->maxX) { - *valuePtr = 3; - meta->direction = 1; - valueChanged = true; - } - } - } else { - if (meta->direction != 0) { - if (value == 0) { - meta->direction = 0; - } - } else { - if (value == meta->valuesCount - 1) { - meta->direction = 1; - } - } - - if (meta->direction != 0) { - *valuePtr = value - 1; - } else { - *valuePtr = value + 1; - } - - valueChanged = true; - } - - if (valueChanged) { - soundPlayFile("ib3p1xx1"); - inputBlockForTocks(70); - soundPlayFile("ib3lu1x1"); - _UpdateThing(preferenceIndex); - windowRefresh(gPreferencesWindow); - _changed = true; - return; - } - } else if (preferenceIndex >= FIRST_SECONDARY_PREF && preferenceIndex <= LAST_SECONDARY_PREF) { - PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); - int* valuePtr = meta->valuePtr; - int value = *valuePtr; - bool valueChanged = false; - - int v1 = meta->knobX + 11; - int v2 = meta->knobY + 12; - - if (sqrt(pow((double)x - (double)v1, 2) + pow((double)y - (double)v2, 2)) > 10.0) { - int v23 = meta->knobY - 5; - if (y >= v23 && y <= v23 + fontGetLineHeight() + 2) { - if (x >= meta->minX && x <= meta->knobX) { - *valuePtr = preferenceIndex == PREF_COMBAT_MESSAGES ? 1 : 0; - valueChanged = true; - } else if (x >= meta->knobX + 22.0 && x <= meta->maxX) { - *valuePtr = preferenceIndex == PREF_COMBAT_MESSAGES ? 0 : 1; - valueChanged = true; - } - } - } else { - *valuePtr ^= 1; - valueChanged = true; - } - - if (valueChanged) { - soundPlayFile("ib2p1xx1"); - inputBlockForTocks(70); - soundPlayFile("ib2lu1x1"); - _UpdateThing(preferenceIndex); - windowRefresh(gPreferencesWindow); - _changed = true; - return; - } - } else if (preferenceIndex >= FIRST_RANGE_PREF && preferenceIndex <= LAST_RANGE_PREF) { - PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); - int* valuePtr = meta->valuePtr; - - soundPlayFile("ib1p1xx1"); - - double value; - switch (preferenceIndex) { - case PREF_TEXT_BASE_DELAY: - value = 6.0 - gPreferencesTextBaseDelay1 + 1.0; - break; - case PREF_BRIGHTNESS: - value = gPreferencesBrightness1; - break; - case PREF_MOUSE_SENSITIVIY: - value = gPreferencesMouseSensitivity1; - break; - default: - value = *valuePtr; - break; - } - - int knobX = (int)(219.0 / (meta->maxValue - meta->minValue)); - int v31 = (int)((value - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + PREFERENCES_WINDOW_WIDTH * meta->knobY + 384, 240, 12, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + 384, PREFERENCES_WINDOW_WIDTH); - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_ON].getData(), 21, 12, 21, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + v31, PREFERENCES_WINDOW_WIDTH); - - windowRefresh(gPreferencesWindow); - - int sfxVolumeExample = 0; - int speechVolumeExample = 0; - while (true) { - sharedFpsLimiter.mark(); - - inputGetInput(); - - int tick = getTicks(); - - mouseGetPositionInWindow(gPreferencesWindow, &x, &y); - - if (mouseGetEvent() & 0x10) { - soundPlayFile("ib1lu1x1"); - _UpdateThing(preferenceIndex); - windowRefresh(gPreferencesWindow); - renderPresent(); - _changed = true; - return; - } - - if (v31 + 14 > x) { - if (v31 + 6 > x) { - v31 = x - 6; - if (v31 < 384) { - v31 = 384; - } - } - } else { - v31 = x - 6; - if (v31 > 603) { - v31 = 603; - } - } - - double newValue = ((double)v31 - 384.0) / (219.0 / (meta->maxValue - meta->minValue)) + meta->minValue; - - int v52 = 0; - - switch (preferenceIndex) { - case PREF_COMBAT_SPEED: - *meta->valuePtr = (int)newValue; - break; - case PREF_TEXT_BASE_DELAY: - gPreferencesTextBaseDelay1 = 6.0 - newValue + 1.0; - break; - case PREF_MASTER_VOLUME: - *meta->valuePtr = (int)newValue; - gameSoundSetMasterVolume(gPreferencesMasterVolume1); - v52 = 1; - break; - case PREF_MUSIC_VOLUME: - *meta->valuePtr = (int)newValue; - backgroundSoundSetVolume(gPreferencesMusicVolume1); - v52 = 1; - break; - case PREF_SFX_VOLUME: - *meta->valuePtr = (int)newValue; - soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); - v52 = 1; - if (sfxVolumeExample == 0) { - soundPlayFile("butin1"); - sfxVolumeExample = 7; - } else { - sfxVolumeExample--; - } - break; - case PREF_SPEECH_VOLUME: - *meta->valuePtr = (int)newValue; - speechSetVolume(gPreferencesSpeechVolume1); - v52 = 1; - if (speechVolumeExample == 0) { - speechLoad("narrator\\options", 12, 13, 15); - speechVolumeExample = 40; - } else { - speechVolumeExample--; - } - break; - case PREF_BRIGHTNESS: - gPreferencesBrightness1 = newValue; - colorSetBrightness(newValue); - break; - case PREF_MOUSE_SENSITIVIY: - gPreferencesMouseSensitivity1 = newValue; - break; - } - - if (v52) { - int off = PREFERENCES_WINDOW_WIDTH * (meta->knobY - 12) + 384; - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + off, 240, 24, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + off, PREFERENCES_WINDOW_WIDTH); - - for (int optionIndex = 0; optionIndex < meta->valuesCount; optionIndex++) { - const char* str = getmsg(&gOptionsMessageList, &gOptionsMessageListItem, meta->labelIds[optionIndex]); - - int x; - switch (optionIndex) { - case 0: - // 0x4926AA - x = 384; - // TODO: Incomplete. - break; - case 1: - // 0x4926F3 - switch (meta->valuesCount) { - case 2: - x = 624 - fontGetStringWidth(str); - break; - case 3: - // This code path does not use floating-point arithmetic - x = 504 - fontGetStringWidth(str) / 2 - 2; - break; - case 4: - // Uses floating-point arithmetic - x = 444 + fontGetStringWidth(str) / 2 - 8; - break; - } - break; - case 2: - // 0x492766 - switch (meta->valuesCount) { - case 3: - x = 624 - fontGetStringWidth(str); - break; - case 4: - // Uses floating-point arithmetic - x = 564 - fontGetStringWidth(str) - 4; - break; - } - break; - case 3: - // 0x49279E - x = 624 - fontGetStringWidth(str); - break; - } - fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * (meta->knobY - 12) + x, str, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); - } - } else { - int off = PREFERENCES_WINDOW_WIDTH * meta->knobY + 384; - blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + off, 240, 12, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + off, PREFERENCES_WINDOW_WIDTH); - } - - blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_ON].getData(), 21, 12, 21, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + v31, PREFERENCES_WINDOW_WIDTH); - windowRefresh(gPreferencesWindow); - - while (getTicksSince(tick) < 35) - ; - - renderPresent(); - sharedFpsLimiter.throttle(); - } - } else if (preferenceIndex == 19) { - gPreferencesPlayerSpeedup1 ^= 1; - } - - _changed = true; -} - -// 0x48FC48 -int _do_options() -{ - return showOptionsWithInitialKeyCode(-1); -} - } // namespace fallout diff --git a/src/options.h b/src/options.h index 0571a6e..4373856 100644 --- a/src/options.h +++ b/src/options.h @@ -1,37 +1,11 @@ #ifndef OPTIONS_H #define OPTIONS_H -#include "db.h" - namespace fallout { -extern int gPreferencesSoundEffectsVolume1; -extern int gPreferencesSubtitles1; -extern int gPreferencesLanguageFilter1; -extern int gPreferencesSpeechVolume1; -extern int gPreferencesMasterVolume1; -extern int gPreferencesPlayerSpeedup1; -extern int gPreferencesCombatTaunts1; -extern int gPreferencesMusicVolume1; -extern int gPreferencesRunning1; -extern int gPreferencesCombatSpeed1; -extern int gPreferencesItemHighlight1; -extern int gPreferencesCombatMessages1; -extern int gPreferencesTargetHighlight1; -extern int gPreferencesCombatDifficulty1; -extern int gPreferencesViolenceLevel1; -extern int gPreferencesGameDifficulty1; -extern int gPreferencesCombatLooks1; - int showOptions(); -int showOptionsWithInitialKeyCode(int initialKeyCode); int showPause(bool a1); int _init_options_menu(); -int preferencesSave(File* stream); -int preferencesLoad(File* stream); -void brightnessIncrease(); -void brightnessDecrease(); -int _do_options(); } // namespace fallout diff --git a/src/preferences.cc b/src/preferences.cc new file mode 100644 index 0000000..80a71ef --- /dev/null +++ b/src/preferences.cc @@ -0,0 +1,1617 @@ +#include "options.h" + +#include + +#include "art.h" +#include "color.h" +#include "combat.h" +#include "combat_ai.h" +#include "cycle.h" +#include "debug.h" +#include "draw.h" +#include "game.h" +#include "game_mouse.h" +#include "game_sound.h" +#include "graph_lib.h" +#include "input.h" +#include "kb.h" +#include "map.h" +#include "message.h" +#include "mouse.h" +#include "palette.h" +#include "scripts.h" +#include "settings.h" +#include "svga.h" +#include "text_font.h" +#include "text_object.h" +#include "window_manager.h" + +namespace fallout { + +#define PREFERENCES_WINDOW_WIDTH 640 +#define PREFERENCES_WINDOW_HEIGHT 480 + +#define PRIMARY_OPTION_VALUE_COUNT 4 +#define SECONDARY_OPTION_VALUE_COUNT 2 + +typedef enum Preference { + PREF_GAME_DIFFICULTY, + PREF_COMBAT_DIFFICULTY, + PREF_VIOLENCE_LEVEL, + PREF_TARGET_HIGHLIGHT, + PREF_COMBAT_LOOKS, + PREF_COMBAT_MESSAGES, + PREF_COMBAT_TAUNTS, + PREF_LANGUAGE_FILTER, + PREF_RUNNING, + PREF_SUBTITLES, + PREF_ITEM_HIGHLIGHT, + PREF_COMBAT_SPEED, + PREF_TEXT_BASE_DELAY, + PREF_MASTER_VOLUME, + PREF_MUSIC_VOLUME, + PREF_SFX_VOLUME, + PREF_SPEECH_VOLUME, + PREF_BRIGHTNESS, + PREF_MOUSE_SENSITIVIY, + PREF_COUNT, + FIRST_PRIMARY_PREF = PREF_GAME_DIFFICULTY, + LAST_PRIMARY_PREF = PREF_COMBAT_LOOKS, + PRIMARY_PREF_COUNT = LAST_PRIMARY_PREF - FIRST_PRIMARY_PREF + 1, + FIRST_SECONDARY_PREF = PREF_COMBAT_MESSAGES, + LAST_SECONDARY_PREF = PREF_ITEM_HIGHLIGHT, + SECONDARY_PREF_COUNT = LAST_SECONDARY_PREF - FIRST_SECONDARY_PREF + 1, + FIRST_RANGE_PREF = PREF_COMBAT_SPEED, + LAST_RANGE_PREF = PREF_MOUSE_SENSITIVIY, + RANGE_PREF_COUNT = LAST_RANGE_PREF - FIRST_RANGE_PREF + 1, +} Preference; + +typedef enum PreferencesWindowFrm { + PREFERENCES_WINDOW_FRM_BACKGROUND, + // Knob (for range preferences) + PREFERENCES_WINDOW_FRM_KNOB_OFF, + // 4-way switch (for primary preferences) + PREFERENCES_WINDOW_FRM_PRIMARY_SWITCH, + // 2-way switch (for secondary preferences) + PREFERENCES_WINDOW_FRM_SECONDARY_SWITCH, + PREFERENCES_WINDOW_FRM_CHECKBOX_ON, + PREFERENCES_WINDOW_FRM_CHECKBOX_OFF, + PREFERENCES_WINDOW_FRM_6, + // Active knob (for range preferences) + PREFERENCES_WINDOW_FRM_KNOB_ON, + PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP, + PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN, + PREFERENCES_WINDOW_FRM_COUNT, +} PreferencesWindowFrm; + +typedef struct PreferenceDescription { + // The number of options. + short valuesCount; + + // Direction of rotation: + // 0 - clockwise (incrementing value), + // 1 - counter-clockwise (decrementing value) + short direction; + short knobX; + short knobY; + // Min x coordinate of the preference control bounding box. + short minX; + // Max x coordinate of the preference control bounding box. + short maxX; + short labelIds[PRIMARY_OPTION_VALUE_COUNT]; + int btn; + char name[32]; + double minValue; + double maxValue; + int* valuePtr; +} PreferenceDescription; + +static void _SetSystemPrefs(); +static void _SaveSettings(); +static void _RestoreSettings(); +static void preferencesSetDefaults(bool a1); +static void _JustUpdate_(); +static void _UpdateThing(int index); +int _SavePrefs(bool save); +static int preferencesWindowInit(); +static int preferencesWindowFree(); +static void _DoThing(int eventCode); + +// 0x48FBD0 +static const int _row1Ytab[PRIMARY_PREF_COUNT] = { + 48, + 125, + 203, + 286, + 363, +}; + +// 0x48FBDA +static const int _row2Ytab[SECONDARY_PREF_COUNT] = { + 49, + 116, + 181, + 247, + 313, + 380, +}; + +// 0x48FBE6 +static const int _row3Ytab[RANGE_PREF_COUNT] = { + 19, + 94, + 165, + 216, + 268, + 319, + 369, + 420, +}; + +// x offsets for primary preferences from the knob position +// 0x48FBF6 +static const short word_48FBF6[PRIMARY_OPTION_VALUE_COUNT] = { + 2, + 25, + 46, + 46, +}; + +// y offsets for primary preference option values from the knob position +// 0x48FBFE +static const short word_48FBFE[PRIMARY_OPTION_VALUE_COUNT] = { + 10, + -4, + 10, + 31, +}; + +// x offsets for secondary prefrence option values from the knob position +// 0x48FC06 +static const short word_48FC06[SECONDARY_OPTION_VALUE_COUNT] = { + 4, + 21, +}; + +// y offsets for secondary preferences +// 0x48FC30 +static const int dword_48FC30[SECONDARY_PREF_COUNT] = { + 66, // combat messages + 133, // combat taunts + 200, // language filter + 264, // running + 331, // subtitles + 397, // item highlight +}; + +// y offsets for primary preferences +// 0x48FC1C +static const int dword_48FC1C[PRIMARY_PREF_COUNT] = { + 66, // game difficulty + 143, // combat difficulty + 222, // violence level + 304, // target highlight + 382, // combat looks +}; + +// 0x50C168 +static const double dbl_50C168 = 1.17999267578125; + +// 0x50C170 +static const double dbl_50C170 = 0.01124954223632812; + +// 0x50C178 +static const double dbl_50C178 = -0.01124954223632812; + +// 0x50C180 +static const double dbl_50C180 = 1.17999267578125; + +// 0x50C2D0 +static const double dbl_50C2D0 = -1.0; + +// 0x50C2D8 +static const double dbl_50C2D8 = 0.2; + +// 0x50C2E0 +static const double dbl_50C2E0 = 2.0; + +// 0x5197CC +static const int gPreferencesWindowFrmIds[PREFERENCES_WINDOW_FRM_COUNT] = { + 240, // prefscrn.frm - options screen + 241, // prfsldof.frm - options screen + 242, // prfbknbs.frm - options screen + 243, // prflknbs.frm - options screen + 244, // prfxin.frm - options screen + 245, // prfxout.frm - options screen + 246, // prefcvr.frm - options screen + 247, // prfsldon.frm - options screen + 8, // lilredup.frm - little red button up + 9, // lilreddn.frm - little red button down +}; + +// 0x6637E8 +static MessageList gPreferencesMessageList; + +// 0x663840 +static MessageListItem gPreferencesMessageListItem; + +// 0x6638C8 +static double gPreferencesTextBaseDelay2; + +// 0x6638D0 +static double gPreferencesBrightness1; + +// 0x6638D8 +static double gPreferencesBrightness2; + +// 0x6638E0 +static double gPreferencesTextBaseDelay1; + +// 0x6638E8 +static double gPreferencesMouseSensitivity1; + +// 0x6638F0 +static double gPreferencesMouseSensitivity2; + +// 0x6638F8 +static unsigned char* gPreferencesWindowBuffer; + +// 0x663904 +static int gPreferencesWindow; + +// 0x663924 +static int gPreferencesGameDifficulty2; + +// 0x663928 +static int gPreferencesCombatDifficulty2; + +// 0x66392C +static int gPreferencesViolenceLevel2; + +// 0x663930 +static int gPreferencesTargetHighlight2; + +// 0x663934 +static int gPreferencesCombatLooks2; + +// 0x663938 +static int gPreferencesCombatMessages2; + +// 0x66393C +static int gPreferencesCombatTaunts2; + +// 0x663940 +static int gPreferencesLanguageFilter2; + +// 0x663944 +static int gPreferencesRunning2; + +// 0x663948 +static int gPreferencesSubtitles2; + +// 0x66394C +static int gPreferencesItemHighlight2; + +// 0x663950 +static int gPreferencesCombatSpeed2; + +// 0x663954 +static int gPreferencesPlayerSpeedup2; + +// 0x663958 +static int gPreferencesMasterVolume2; + +// 0x66395C +static int gPreferencesMusicVolume2; + +// 0x663960 +static int gPreferencesSoundEffectsVolume2; + +// 0x663964 +static int gPreferencesSpeechVolume2; + +// 0x663970 +static int gPreferencesSoundEffectsVolume1; + +// 0x663974 +static int gPreferencesSubtitles1; + +// 0x663978 +static int gPreferencesLanguageFilter1; + +// 0x66397C +static int gPreferencesSpeechVolume1; + +// 0x663980 +static int gPreferencesMasterVolume1; + +// 0x663984 +static int gPreferencesPlayerSpeedup1; + +// 0x663988 +static int gPreferencesCombatTaunts1; + +// 0x663990 +static int gPreferencesMusicVolume1; + +// 0x663998 +static int gPreferencesRunning1; + +// 0x66399C +static int gPreferencesCombatSpeed1; + +// 0x6639A0 +static int _plyrspdbid; + +// 0x6639A4 +static int gPreferencesItemHighlight1; + +// 0x6639A8 +static bool _changed; + +// 0x6639AC +static int gPreferencesCombatMessages1; + +// 0x6639B0 +static int gPreferencesTargetHighlight1; + +// 0x6639B4 +static int gPreferencesCombatDifficulty1; + +// 0x6639B8 +static int gPreferencesViolenceLevel1; + +// 0x6639BC +static int gPreferencesGameDifficulty1; + +// 0x6639C0 +static int gPreferencesCombatLooks1; + +// 0x5197F8 +static PreferenceDescription gPreferenceDescriptions[PREF_COUNT] = { + { 3, 0, 76, 71, 0, 0, { 203, 204, 205, 0 }, 0, GAME_CONFIG_GAME_DIFFICULTY_KEY, 0, 0, &gPreferencesGameDifficulty1 }, + { 3, 0, 76, 149, 0, 0, { 206, 204, 208, 0 }, 0, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, 0, 0, &gPreferencesCombatDifficulty1 }, + { 4, 0, 76, 226, 0, 0, { 214, 215, 204, 216 }, 0, GAME_CONFIG_VIOLENCE_LEVEL_KEY, 0, 0, &gPreferencesViolenceLevel1 }, + { 3, 0, 76, 309, 0, 0, { 202, 201, 213, 0 }, 0, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, 0, 0, &gPreferencesTargetHighlight1 }, + { 2, 0, 76, 387, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_COMBAT_LOOKS_KEY, 0, 0, &gPreferencesCombatLooks1 }, + { 2, 0, 299, 74, 0, 0, { 211, 212, 0, 0 }, 0, GAME_CONFIG_COMBAT_MESSAGES_KEY, 0, 0, &gPreferencesCombatMessages1 }, + { 2, 0, 299, 141, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_COMBAT_TAUNTS_KEY, 0, 0, &gPreferencesCombatTaunts1 }, + { 2, 0, 299, 207, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_LANGUAGE_FILTER_KEY, 0, 0, &gPreferencesLanguageFilter1 }, + { 2, 0, 299, 271, 0, 0, { 209, 219, 0, 0 }, 0, GAME_CONFIG_RUNNING_KEY, 0, 0, &gPreferencesRunning1 }, + { 2, 0, 299, 338, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_SUBTITLES_KEY, 0, 0, &gPreferencesSubtitles1 }, + { 2, 0, 299, 404, 0, 0, { 202, 201, 0, 0 }, 0, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, 0, 0, &gPreferencesItemHighlight1 }, + { 2, 0, 374, 50, 0, 0, { 207, 210, 0, 0 }, 0, GAME_CONFIG_COMBAT_SPEED_KEY, 0.0, 50.0, &gPreferencesCombatSpeed1 }, + { 3, 0, 374, 125, 0, 0, { 217, 209, 218, 0 }, 0, GAME_CONFIG_TEXT_BASE_DELAY_KEY, 1.0, 6.0, NULL }, + { 4, 0, 374, 196, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_MASTER_VOLUME_KEY, 0, 32767.0, &gPreferencesMasterVolume1 }, + { 4, 0, 374, 247, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_MUSIC_VOLUME_KEY, 0, 32767.0, &gPreferencesMusicVolume1 }, + { 4, 0, 374, 298, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_SNDFX_VOLUME_KEY, 0, 32767.0, &gPreferencesSoundEffectsVolume1 }, + { 4, 0, 374, 349, 0, 0, { 202, 221, 209, 222 }, 0, GAME_CONFIG_SPEECH_VOLUME_KEY, 0, 32767.0, &gPreferencesSpeechVolume1 }, + { 2, 0, 374, 400, 0, 0, { 207, 223, 0, 0 }, 0, GAME_CONFIG_BRIGHTNESS_KEY, 1.0, 1.17999267578125, NULL }, + { 2, 0, 374, 451, 0, 0, { 207, 218, 0, 0 }, 0, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, 1.0, 2.5, NULL }, +}; + +static FrmImage _preferencesFrmImages[PREFERENCES_WINDOW_FRM_COUNT]; +static int _oldFont; +static bool _cycleWasEnabled; +static bool _isoWasEnabled; +static bool _mouseWasVisible; + +int preferencesInit() +{ + for (int index = 0; index < 11; index++) { + gPreferenceDescriptions[index].direction = 0; + } + + _SetSystemPrefs(); + + return 0; +} + +// 0x492AA8 +static void _SetSystemPrefs() +{ + preferencesSetDefaults(false); + + gPreferencesGameDifficulty1 = settings.preferences.game_difficulty; + gPreferencesCombatDifficulty1 = settings.preferences.combat_difficulty; + gPreferencesViolenceLevel1 = settings.preferences.violence_level; + gPreferencesTargetHighlight1 = settings.preferences.target_highlight; + gPreferencesCombatMessages1 = settings.preferences.combat_messages; + gPreferencesCombatLooks1 = settings.preferences.combat_looks; + gPreferencesCombatTaunts1 = settings.preferences.combat_taunts; + gPreferencesLanguageFilter1 = settings.preferences.language_filter; + gPreferencesRunning1 = settings.preferences.running; + gPreferencesSubtitles1 = settings.preferences.subtitles; + gPreferencesItemHighlight1 = settings.preferences.item_highlight; + gPreferencesCombatSpeed1 = settings.preferences.combat_speed; + gPreferencesTextBaseDelay1 = settings.preferences.text_base_delay; + gPreferencesPlayerSpeedup1 = settings.preferences.player_speedup; + gPreferencesMasterVolume1 = settings.sound.master_volume; + gPreferencesMusicVolume1 = settings.sound.music_volume; + gPreferencesSoundEffectsVolume1 = settings.sound.sndfx_volume; + gPreferencesSpeechVolume1 = settings.sound.speech_volume; + gPreferencesBrightness1 = settings.preferences.brightness; + gPreferencesMouseSensitivity1 = settings.preferences.mouse_sensitivity; + + _JustUpdate_(); +} + +// 0x493054 +static void _SaveSettings() +{ + gPreferencesGameDifficulty2 = gPreferencesGameDifficulty1; + gPreferencesCombatDifficulty2 = gPreferencesCombatDifficulty1; + gPreferencesViolenceLevel2 = gPreferencesViolenceLevel1; + gPreferencesTargetHighlight2 = gPreferencesTargetHighlight1; + gPreferencesCombatLooks2 = gPreferencesCombatLooks1; + gPreferencesCombatMessages2 = gPreferencesCombatMessages1; + gPreferencesCombatTaunts2 = gPreferencesCombatTaunts1; + gPreferencesLanguageFilter2 = gPreferencesLanguageFilter1; + gPreferencesRunning2 = gPreferencesRunning1; + gPreferencesSubtitles2 = gPreferencesSubtitles1; + gPreferencesItemHighlight2 = gPreferencesItemHighlight1; + gPreferencesCombatSpeed2 = gPreferencesCombatSpeed1; + gPreferencesPlayerSpeedup2 = gPreferencesPlayerSpeedup1; + gPreferencesMasterVolume2 = gPreferencesMasterVolume1; + gPreferencesTextBaseDelay2 = gPreferencesTextBaseDelay1; + gPreferencesMusicVolume2 = gPreferencesMusicVolume1; + gPreferencesBrightness2 = gPreferencesBrightness1; + gPreferencesSoundEffectsVolume2 = gPreferencesSoundEffectsVolume1; + gPreferencesMouseSensitivity2 = gPreferencesMouseSensitivity1; + gPreferencesSpeechVolume2 = gPreferencesSpeechVolume1; +} + +// 0x493128 +static void _RestoreSettings() +{ + gPreferencesGameDifficulty1 = gPreferencesGameDifficulty2; + gPreferencesCombatDifficulty1 = gPreferencesCombatDifficulty2; + gPreferencesViolenceLevel1 = gPreferencesViolenceLevel2; + gPreferencesTargetHighlight1 = gPreferencesTargetHighlight2; + gPreferencesCombatLooks1 = gPreferencesCombatLooks2; + gPreferencesCombatMessages1 = gPreferencesCombatMessages2; + gPreferencesCombatTaunts1 = gPreferencesCombatTaunts2; + gPreferencesLanguageFilter1 = gPreferencesLanguageFilter2; + gPreferencesRunning1 = gPreferencesRunning2; + gPreferencesSubtitles1 = gPreferencesSubtitles2; + gPreferencesItemHighlight1 = gPreferencesItemHighlight2; + gPreferencesCombatSpeed1 = gPreferencesCombatSpeed2; + gPreferencesPlayerSpeedup1 = gPreferencesPlayerSpeedup2; + gPreferencesMasterVolume1 = gPreferencesMasterVolume2; + gPreferencesTextBaseDelay1 = gPreferencesTextBaseDelay2; + gPreferencesMusicVolume1 = gPreferencesMusicVolume2; + gPreferencesBrightness1 = gPreferencesBrightness2; + gPreferencesSoundEffectsVolume1 = gPreferencesSoundEffectsVolume2; + gPreferencesMouseSensitivity1 = gPreferencesMouseSensitivity2; + gPreferencesSpeechVolume1 = gPreferencesSpeechVolume2; + + _JustUpdate_(); +} + +// 0x492F60 +static void preferencesSetDefaults(bool a1) +{ + gPreferencesCombatDifficulty1 = COMBAT_DIFFICULTY_NORMAL; + gPreferencesViolenceLevel1 = VIOLENCE_LEVEL_MAXIMUM_BLOOD; + gPreferencesTargetHighlight1 = TARGET_HIGHLIGHT_TARGETING_ONLY; + gPreferencesCombatMessages1 = 1; + gPreferencesCombatLooks1 = 0; + gPreferencesCombatTaunts1 = 1; + gPreferencesRunning1 = 0; + gPreferencesSubtitles1 = 0; + gPreferencesItemHighlight1 = 1; + gPreferencesCombatSpeed1 = 0; + gPreferencesPlayerSpeedup1 = 0; + gPreferencesTextBaseDelay1 = 3.5; + gPreferencesBrightness1 = 1.0; + gPreferencesMouseSensitivity1 = 1.0; + gPreferencesGameDifficulty1 = 1; + gPreferencesLanguageFilter1 = 0; + gPreferencesMasterVolume1 = 22281; + gPreferencesMusicVolume1 = 22281; + gPreferencesSoundEffectsVolume1 = 22281; + gPreferencesSpeechVolume1 = 22281; + + if (a1) { + for (int index = 0; index < PREF_COUNT; index++) { + _UpdateThing(index); + } + _win_set_button_rest_state(_plyrspdbid, gPreferencesPlayerSpeedup1, 0); + windowRefresh(gPreferencesWindow); + _changed = true; + } +} + +// 0x4931F8 +static void _JustUpdate_() +{ + gPreferencesGameDifficulty1 = std::clamp(gPreferencesGameDifficulty1, 0, 2); + gPreferencesCombatDifficulty1 = std::clamp(gPreferencesCombatDifficulty1, 0, 2); + gPreferencesViolenceLevel1 = std::clamp(gPreferencesViolenceLevel1, 0, 3); + gPreferencesTargetHighlight1 = std::clamp(gPreferencesTargetHighlight1, 0, 2); + gPreferencesCombatMessages1 = std::clamp(gPreferencesCombatMessages1, 0, 1); + gPreferencesCombatLooks1 = std::clamp(gPreferencesCombatLooks1, 0, 1); + gPreferencesCombatTaunts1 = std::clamp(gPreferencesCombatTaunts1, 0, 1); + gPreferencesLanguageFilter1 = std::clamp(gPreferencesLanguageFilter1, 0, 1); + gPreferencesRunning1 = std::clamp(gPreferencesRunning1, 0, 1); + gPreferencesSubtitles1 = std::clamp(gPreferencesSubtitles1, 0, 1); + gPreferencesItemHighlight1 = std::clamp(gPreferencesItemHighlight1, 0, 1); + gPreferencesCombatSpeed1 = std::clamp(gPreferencesCombatSpeed1, 0, 50); + gPreferencesPlayerSpeedup1 = std::clamp(gPreferencesPlayerSpeedup1, 0, 1); + gPreferencesTextBaseDelay1 = std::clamp(gPreferencesTextBaseDelay1, 6.0, 10.0); + gPreferencesMasterVolume1 = std::clamp(gPreferencesMasterVolume1, 0, VOLUME_MAX); + gPreferencesMusicVolume1 = std::clamp(gPreferencesMusicVolume1, 0, VOLUME_MAX); + gPreferencesSoundEffectsVolume1 = std::clamp(gPreferencesSoundEffectsVolume1, 0, VOLUME_MAX); + gPreferencesSpeechVolume1 = std::clamp(gPreferencesSpeechVolume1, 0, VOLUME_MAX); + gPreferencesBrightness1 = std::clamp(gPreferencesBrightness1, 1.0, 1.17999267578125); + gPreferencesMouseSensitivity1 = std::clamp(gPreferencesMouseSensitivity1, 1.0, 2.5); + + textObjectsSetBaseDelay(gPreferencesTextBaseDelay1); + gameMouseLoadItemHighlight(); + + double textLineDelay = (gPreferencesTextBaseDelay1 + (-1.0)) * 0.2 * 2.0; + textLineDelay = std::clamp(textLineDelay, 0.0, 2.0); + + textObjectsSetLineDelay(textLineDelay); + aiMessageListReloadIfNeeded(); + _scr_message_free(); + gameSoundSetMasterVolume(gPreferencesMasterVolume1); + backgroundSoundSetVolume(gPreferencesMusicVolume1); + soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); + speechSetVolume(gPreferencesSpeechVolume1); + mouseSetSensitivity(gPreferencesMouseSensitivity1); + colorSetBrightness(gPreferencesBrightness1); +} + +// 0x491A68 +static void _UpdateThing(int index) +{ + fontSetCurrent(101); + + PreferenceDescription* meta = &(gPreferenceDescriptions[index]); + + if (index >= FIRST_PRIMARY_PREF && index <= LAST_PRIMARY_PREF) { + int primaryOptionIndex = index - FIRST_PRIMARY_PREF; + + int offsets[PRIMARY_PREF_COUNT]; + memcpy(offsets, dword_48FC1C, sizeof(dword_48FC1C)); + + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * offsets[primaryOptionIndex] + 23, 160, 54, 640, gPreferencesWindowBuffer + 640 * offsets[primaryOptionIndex] + 23, 640); + + for (int valueIndex = 0; valueIndex < meta->valuesCount; valueIndex++) { + const char* text = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, meta->labelIds[valueIndex]); + + char copy[100]; // TODO: Size is probably wrong. + strcpy(copy, text); + + int x = meta->knobX + word_48FBF6[valueIndex]; + int len = fontGetStringWidth(copy); + switch (valueIndex) { + case 0: + x -= fontGetStringWidth(copy); + meta->minX = x; + break; + case 1: + x -= len / 2; + meta->maxX = x + len; + break; + case 2: + case 3: + meta->maxX = x + len; + break; + } + + char* p = copy; + while (*p != '\0' && *p != ' ') { + p++; + } + + int y = meta->knobY + word_48FBFE[valueIndex]; + const char* s; + if (*p != '\0') { + *p = '\0'; + fontDrawText(gPreferencesWindowBuffer + 640 * y + x, copy, 640, 640, _colorTable[18979]); + s = p + 1; + y += fontGetLineHeight(); + } else { + s = copy; + } + + fontDrawText(gPreferencesWindowBuffer + 640 * y + x, s, 640, 640, _colorTable[18979]); + } + + int value = *(meta->valuePtr); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_PRIMARY_SWITCH].getData() + (46 * 47) * value, 46, 47, 46, gPreferencesWindowBuffer + 640 * meta->knobY + meta->knobX, 640); + } else if (index >= FIRST_SECONDARY_PREF && index <= LAST_SECONDARY_PREF) { + int secondaryOptionIndex = index - FIRST_SECONDARY_PREF; + + int offsets[SECONDARY_PREF_COUNT]; + memcpy(offsets, dword_48FC30, sizeof(dword_48FC30)); + + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * offsets[secondaryOptionIndex] + 251, 113, 34, 640, gPreferencesWindowBuffer + 640 * offsets[secondaryOptionIndex] + 251, 640); + + // Secondary options are booleans, so it's index is also it's value. + for (int value = 0; value < 2; value++) { + const char* text = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, meta->labelIds[value]); + + int x; + if (value) { + x = meta->knobX + word_48FC06[value]; + meta->maxX = x + fontGetStringWidth(text); + } else { + x = meta->knobX + word_48FC06[value] - fontGetStringWidth(text); + meta->minX = x; + } + fontDrawText(gPreferencesWindowBuffer + 640 * (meta->knobY - 5) + x, text, 640, 640, _colorTable[18979]); + } + + int value = *(meta->valuePtr); + if (index == PREF_COMBAT_MESSAGES) { + value ^= 1; + } + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_SECONDARY_SWITCH].getData() + (22 * 25) * value, 22, 25, 22, gPreferencesWindowBuffer + 640 * meta->knobY + meta->knobX, 640); + } else if (index >= FIRST_RANGE_PREF && index <= LAST_RANGE_PREF) { + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + 640 * (meta->knobY - 12) + 384, 240, 24, 640, gPreferencesWindowBuffer + 640 * (meta->knobY - 12) + 384, 640); + switch (index) { + case PREF_COMBAT_SPEED: + if (1) { + double value = *meta->valuePtr; + value = std::clamp(value, 0.0, 50.0); + + int x = (int)((value - meta->minValue) * 219.0 / (meta->maxValue - meta->minValue) + 384.0); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); + } + break; + case PREF_TEXT_BASE_DELAY: + if (1) { + gPreferencesTextBaseDelay1 = std::clamp(gPreferencesTextBaseDelay1, 1.0, 6.0); + + int x = (int)((6.0 - gPreferencesTextBaseDelay1) * 43.8 + 384.0); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); + + double value = (gPreferencesTextBaseDelay1 - 1.0) * 0.2 * 2.0; + value = std::clamp(value, 0.0, 2.0); + + textObjectsSetBaseDelay(gPreferencesTextBaseDelay1); + textObjectsSetLineDelay(value); + } + break; + case PREF_MASTER_VOLUME: + case PREF_MUSIC_VOLUME: + case PREF_SFX_VOLUME: + case PREF_SPEECH_VOLUME: + if (1) { + double value = *meta->valuePtr; + value = std::clamp(value, meta->minValue, meta->maxValue); + + int x = (int)((value - meta->minValue) * 219.0 / (meta->maxValue - meta->minValue) + 384.0); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); + + switch (index) { + case PREF_MASTER_VOLUME: + gameSoundSetMasterVolume(gPreferencesMasterVolume1); + break; + case PREF_MUSIC_VOLUME: + backgroundSoundSetVolume(gPreferencesMusicVolume1); + break; + case PREF_SFX_VOLUME: + soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); + break; + case PREF_SPEECH_VOLUME: + speechSetVolume(gPreferencesSpeechVolume1); + break; + } + } + break; + case PREF_BRIGHTNESS: + if (1) { + gPreferencesBrightness1 = std::clamp(gPreferencesBrightness1, 1.0, 1.17999267578125); + + int x = (int)((gPreferencesBrightness1 - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); + + colorSetBrightness(gPreferencesBrightness1); + } + break; + case PREF_MOUSE_SENSITIVIY: + if (1) { + gPreferencesMouseSensitivity1 = std::clamp(gPreferencesMouseSensitivity1, 1.0, 2.5); + + int x = (int)((gPreferencesMouseSensitivity1 - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_OFF].getData(), 21, 12, 21, gPreferencesWindowBuffer + 640 * meta->knobY + x, 640); + + mouseSetSensitivity(gPreferencesMouseSensitivity1); + } + break; + } + + for (int optionIndex = 0; optionIndex < meta->valuesCount; optionIndex++) { + const char* str = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, meta->labelIds[optionIndex]); + + int x; + switch (optionIndex) { + case 0: + // 0x4926AA + x = 384; + // TODO: Incomplete. + break; + case 1: + // 0x4926F3 + switch (meta->valuesCount) { + case 2: + x = 624 - fontGetStringWidth(str); + break; + case 3: + // This code path does not use floating-point arithmetic + x = 504 - fontGetStringWidth(str) / 2 - 2; + break; + case 4: + // Uses floating-point arithmetic + x = 444 + fontGetStringWidth(str) / 2 - 8; + break; + } + break; + case 2: + // 0x492766 + switch (meta->valuesCount) { + case 3: + x = 624 - fontGetStringWidth(str); + break; + case 4: + // Uses floating-point arithmetic + x = 564 - fontGetStringWidth(str) - 4; + break; + } + break; + case 3: + // 0x49279E + x = 624 - fontGetStringWidth(str); + break; + } + fontDrawText(gPreferencesWindowBuffer + 640 * (meta->knobY - 12) + x, str, 640, 640, _colorTable[18979]); + } + } else { + // return false; + } + + // TODO: Incomplete. + + // return true; +} + +// 0x492CB0 +int _SavePrefs(bool save) +{ + settings.preferences.game_difficulty = gPreferencesGameDifficulty1; + settings.preferences.combat_difficulty = gPreferencesCombatDifficulty1; + settings.preferences.violence_level = gPreferencesViolenceLevel1; + settings.preferences.target_highlight = gPreferencesTargetHighlight1; + settings.preferences.combat_messages = gPreferencesCombatMessages1; + settings.preferences.combat_looks = gPreferencesCombatLooks1; + settings.preferences.combat_taunts = gPreferencesCombatTaunts1; + settings.preferences.language_filter = gPreferencesLanguageFilter1; + settings.preferences.running = gPreferencesRunning1; + settings.preferences.subtitles = gPreferencesSubtitles1; + settings.preferences.item_highlight = gPreferencesItemHighlight1; + settings.preferences.combat_speed = gPreferencesCombatSpeed1; + settings.preferences.text_base_delay = gPreferencesTextBaseDelay1; + + double textLineDelay = (gPreferencesTextBaseDelay1 + dbl_50C2D0) * dbl_50C2D8 * dbl_50C2E0; + if (textLineDelay >= 0.0) { + if (textLineDelay > dbl_50C2E0) { + textLineDelay = 2.0; + } + + settings.preferences.text_line_delay = textLineDelay; + } else { + settings.preferences.text_line_delay = 0.0; + } + + settings.preferences.player_speedup = gPreferencesPlayerSpeedup1; + settings.sound.master_volume = gPreferencesMasterVolume1; + settings.sound.music_volume = gPreferencesMusicVolume1; + settings.sound.sndfx_volume = gPreferencesSoundEffectsVolume1; + settings.sound.speech_volume = gPreferencesSpeechVolume1; + + settings.preferences.brightness = gPreferencesBrightness1; + settings.preferences.mouse_sensitivity = gPreferencesMouseSensitivity1; + + if (save) { + settingsSave(); + } + + return 0; +} + +// 0x493224 +int preferencesSave(File* stream) +{ + float textBaseDelay = (float)gPreferencesTextBaseDelay1; + float brightness = (float)gPreferencesBrightness1; + float mouseSensitivity = (float)gPreferencesMouseSensitivity1; + + if (fileWriteInt32(stream, gPreferencesGameDifficulty1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesCombatDifficulty1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesViolenceLevel1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesTargetHighlight1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesCombatLooks1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesCombatMessages1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesCombatTaunts1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesLanguageFilter1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesRunning1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesSubtitles1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesItemHighlight1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesCombatSpeed1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesPlayerSpeedup1) == -1) goto err; + if (fileWriteFloat(stream, textBaseDelay) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesMasterVolume1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesMusicVolume1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesSoundEffectsVolume1) == -1) goto err; + if (fileWriteInt32(stream, gPreferencesSpeechVolume1) == -1) goto err; + if (fileWriteFloat(stream, brightness) == -1) goto err; + if (fileWriteFloat(stream, mouseSensitivity) == -1) goto err; + + return 0; + +err: + + debugPrint("\nOPTION MENU: Error save option data!\n"); + + return -1; +} + +// 0x49340C +int preferencesLoad(File* stream) +{ + float textBaseDelay; + float brightness; + float mouseSensitivity; + + preferencesSetDefaults(false); + + if (fileReadInt32(stream, &gPreferencesGameDifficulty1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesCombatDifficulty1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesViolenceLevel1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesTargetHighlight1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesCombatLooks1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesCombatMessages1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesCombatTaunts1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesLanguageFilter1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesRunning1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesSubtitles1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesItemHighlight1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesCombatSpeed1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesPlayerSpeedup1) == -1) goto err; + if (fileReadFloat(stream, &textBaseDelay) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesMasterVolume1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesMusicVolume1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesSoundEffectsVolume1) == -1) goto err; + if (fileReadInt32(stream, &gPreferencesSpeechVolume1) == -1) goto err; + if (fileReadFloat(stream, &brightness) == -1) goto err; + if (fileReadFloat(stream, &mouseSensitivity) == -1) goto err; + + gPreferencesBrightness1 = brightness; + gPreferencesMouseSensitivity1 = mouseSensitivity; + gPreferencesTextBaseDelay1 = textBaseDelay; + + _JustUpdate_(); + _SavePrefs(0); + + return 0; + +err: + + debugPrint("\nOPTION MENU: Error loading option data!, using defaults.\n"); + + preferencesSetDefaults(false); + _JustUpdate_(); + _SavePrefs(0); + + return -1; +} + +// 0x4928E4 +void brightnessIncrease() +{ + gPreferencesBrightness1 = settings.preferences.brightness; + + if (gPreferencesBrightness1 < dbl_50C168) { + gPreferencesBrightness1 += dbl_50C170; + + if (gPreferencesBrightness1 >= 1.0) { + if (gPreferencesBrightness1 > dbl_50C168) { + gPreferencesBrightness1 = dbl_50C168; + } + } else { + gPreferencesBrightness1 = 1.0; + } + + colorSetBrightness(gPreferencesBrightness1); + + settings.preferences.brightness = gPreferencesBrightness1; + + settingsSave(); + } +} + +// 0x4929C8 +void brightnessDecrease() +{ + gPreferencesBrightness1 = settings.preferences.brightness; + + if (gPreferencesBrightness1 > 1.0) { + gPreferencesBrightness1 += dbl_50C178; + + if (gPreferencesBrightness1 >= 1.0) { + if (gPreferencesBrightness1 > dbl_50C180) { + gPreferencesBrightness1 = dbl_50C180; + } + } else { + gPreferencesBrightness1 = 1.0; + } + + colorSetBrightness(gPreferencesBrightness1); + + settings.preferences.brightness = gPreferencesBrightness1; + + settingsSave(); + } +} + +// 0x4908A0 +static int preferencesWindowInit() +{ + int i; + int fid; + char* messageItemText; + int x; + int y; + int width; + int height; + int messageItemId; + int btn; + + if (!messageListInit(&gPreferencesMessageList)) { + return -1; + } + + char path[COMPAT_MAX_PATH]; + snprintf(path, sizeof(path), "%s%s", asc_5186C8, "options.msg"); + if (!messageListLoad(&gPreferencesMessageList, path)) { + return -1; + } + + gameMouseSetCursor(MOUSE_CURSOR_ARROW); + + _oldFont = fontGetCurrent(); + + _isoWasEnabled = isoDisable(); + + _cycleWasEnabled = colorCycleEnabled(); + if (_cycleWasEnabled) { + colorCycleDisable(); + } + + _mouseWasVisible = gameMouseObjectsIsVisible(); + if (_mouseWasVisible) { + gameMouseObjectsHide(); + } + + _SaveSettings(); + + for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) { + fid = buildFid(OBJ_TYPE_INTERFACE, gPreferencesWindowFrmIds[i], 0, 0, 0); + if (!_preferencesFrmImages[i].lock(fid)) { + while (--i >= 0) { + _preferencesFrmImages[i].unlock(); + } + return -1; + } + } + + _changed = false; + + int preferencesWindowX = (screenGetWidth() - PREFERENCES_WINDOW_WIDTH) / 2; + int preferencesWindowY = (screenGetHeight() - PREFERENCES_WINDOW_HEIGHT) / 2; + gPreferencesWindow = windowCreate(preferencesWindowX, + preferencesWindowY, + PREFERENCES_WINDOW_WIDTH, + PREFERENCES_WINDOW_HEIGHT, + 256, + WINDOW_MODAL | WINDOW_DONT_MOVE_TOP); + if (gPreferencesWindow == -1) { + for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) { + _preferencesFrmImages[i].unlock(); + } + return -1; + } + + gPreferencesWindowBuffer = windowGetBuffer(gPreferencesWindow); + memcpy(gPreferencesWindowBuffer, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getWidth() * _preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getHeight()); + + fontSetCurrent(104); + + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 100); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 10 + 74, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + + fontSetCurrent(103); + + messageItemId = 101; + for (i = 0; i < PRIMARY_PREF_COUNT; i++) { + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, messageItemId++); + x = 99 - fontGetStringWidth(messageItemText) / 2; + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row1Ytab[i] + x, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + } + + for (i = 0; i < SECONDARY_PREF_COUNT; i++) { + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, messageItemId++); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row2Ytab[i] + 206, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + } + + for (i = 0; i < RANGE_PREF_COUNT; i++) { + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, messageItemId++); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * _row3Ytab[i] + 384, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + } + + // DEFAULT + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 120); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 43, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + + // DONE + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 4); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 169, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + + // CANCEL + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 121); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 449 + 283, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + + // Affect player speed + fontSetCurrent(101); + messageItemText = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, 122); + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * 72 + 405, messageItemText, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + + for (i = 0; i < PREF_COUNT; i++) { + _UpdateThing(i); + } + + for (i = 0; i < PREF_COUNT; i++) { + int mouseEnterEventCode; + int mouseExitEventCode; + int mouseDownEventCode; + int mouseUpEventCode; + + if (i >= FIRST_RANGE_PREF) { + x = 384; + y = gPreferenceDescriptions[i].knobY - 12; + width = 240; + height = 23; + mouseEnterEventCode = 526; + mouseExitEventCode = 526; + mouseDownEventCode = 505 + i; + mouseUpEventCode = 526; + + } else if (i >= FIRST_SECONDARY_PREF) { + x = gPreferenceDescriptions[i].minX; + y = gPreferenceDescriptions[i].knobY - 5; + width = gPreferenceDescriptions[i].maxX - x; + height = 28; + mouseEnterEventCode = -1; + mouseExitEventCode = -1; + mouseDownEventCode = -1; + mouseUpEventCode = 505 + i; + } else { + x = gPreferenceDescriptions[i].minX; + y = gPreferenceDescriptions[i].knobY - 4; + width = gPreferenceDescriptions[i].maxX - x; + height = 48; + mouseEnterEventCode = -1; + mouseExitEventCode = -1; + mouseDownEventCode = -1; + mouseUpEventCode = 505 + i; + } + + gPreferenceDescriptions[i].btn = buttonCreate(gPreferencesWindow, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, NULL, NULL, NULL, 32); + } + + _plyrspdbid = buttonCreate(gPreferencesWindow, + 383, + 68, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_OFF].getWidth(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_ON].getHeight(), + -1, + -1, + 524, + 524, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_OFF].getData(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_CHECKBOX_ON].getData(), + NULL, + BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01 | BUTTON_FLAG_0x02); + if (_plyrspdbid != -1) { + _win_set_button_rest_state(_plyrspdbid, gPreferencesPlayerSpeedup1, 0); + } + + buttonSetCallbacks(_plyrspdbid, _gsound_med_butt_press, _gsound_med_butt_press); + + // DEFAULT + btn = buttonCreate(gPreferencesWindow, + 23, + 450, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), + -1, + -1, + -1, + 527, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), + NULL, + BUTTON_FLAG_TRANSPARENT); + if (btn != -1) { + buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); + } + + // DONE + btn = buttonCreate(gPreferencesWindow, + 148, + 450, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), + -1, + -1, + -1, + 504, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), + NULL, + BUTTON_FLAG_TRANSPARENT); + if (btn != -1) { + buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); + } + + // CANCEL + btn = buttonCreate(gPreferencesWindow, + 263, + 450, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getWidth(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getHeight(), + -1, + -1, + -1, + 528, + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_UP].getData(), + _preferencesFrmImages[PREFERENCES_WINDOW_FRM_LITTLE_RED_BUTTON_DOWN].getData(), + NULL, + BUTTON_FLAG_TRANSPARENT); + if (btn != -1) { + buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release); + } + + fontSetCurrent(101); + + windowRefresh(gPreferencesWindow); + + return 0; +} + +// 0x492870 +static int preferencesWindowFree() +{ + if (_changed) { + _SavePrefs(1); + _JustUpdate_(); + _combat_highlight_change(); + } + + windowDestroy(gPreferencesWindow); + + for (int index = 0; index < PREFERENCES_WINDOW_FRM_COUNT; index++) { + _preferencesFrmImages[index].unlock(); + } + + if (_mouseWasVisible) { + gameMouseObjectsShow(); + } + + if (_cycleWasEnabled) { + colorCycleEnable(); + } + + if (_isoWasEnabled) { + isoEnable(); + } + + fontSetCurrent(_oldFont); + + messageListFree(&gPreferencesMessageList); + + return 0; +} + +// 0x490798 +int doPreferences(bool animated) +{ + ScopedGameMode gm(GameMode::kPreferences); + + if (preferencesWindowInit() == -1) { + debugPrint("\nPREFERENCE MENU: Error loading preference dialog data!\n"); + return -1; + } + + bool cursorWasHidden = cursorIsHidden(); + if (cursorWasHidden) { + mouseShowCursor(); + } + + if (animated) { + colorPaletteLoad("color.pal"); + paletteFadeTo(_cmap); + } + + int rc = -1; + while (rc == -1) { + sharedFpsLimiter.mark(); + + int eventCode = inputGetInput(); + + switch (eventCode) { + case KEY_RETURN: + case KEY_UPPERCASE_P: + case KEY_LOWERCASE_P: + soundPlayFile("ib1p1xx1"); + // FALLTHROUGH + case 504: + rc = 1; + break; + case KEY_CTRL_Q: + case KEY_CTRL_X: + case KEY_F10: + showQuitConfirmationDialog(); + break; + case KEY_EQUAL: + case KEY_PLUS: + brightnessIncrease(); + break; + case KEY_MINUS: + case KEY_UNDERSCORE: + brightnessDecrease(); + break; + case KEY_F12: + takeScreenshot(); + break; + case 527: + preferencesSetDefaults(true); + break; + default: + if (eventCode == KEY_ESCAPE || eventCode == 528 || _game_user_wants_to_quit != 0) { + _RestoreSettings(); + rc = 0; + } else if (eventCode >= 505 && eventCode <= 524) { + _DoThing(eventCode); + } + break; + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + if (animated) { + paletteFadeTo(gPaletteBlack); + } + + if (cursorWasHidden) { + mouseHideCursor(); + } + + preferencesWindowFree(); + + return rc; +} + +// 0x490E8C +static void _DoThing(int eventCode) +{ + int x; + int y; + mouseGetPositionInWindow(gPreferencesWindow, &x, &y); + + // This preference index also contains out-of-bounds value 19, + // which is the only preference expressed as checkbox. + int preferenceIndex = eventCode - 505; + + if (preferenceIndex >= FIRST_PRIMARY_PREF && preferenceIndex <= LAST_PRIMARY_PREF) { + PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); + int* valuePtr = meta->valuePtr; + int value = *valuePtr; + bool valueChanged = false; + + int v1 = meta->knobX + 23; + int v2 = meta->knobY + 21; + + if (sqrt(pow((double)x - (double)v1, 2) + pow((double)y - (double)v2, 2)) > 16.0) { + if (y > meta->knobY) { + int v14 = meta->knobY + word_48FBFE[0]; + if (y >= v14 && y <= v14 + fontGetLineHeight()) { + if (x >= meta->minX && x <= meta->knobX) { + *valuePtr = 0; + meta->direction = 0; + valueChanged = true; + } else { + if (meta->valuesCount >= 3 && x >= meta->knobX + word_48FBF6[2] && x <= meta->maxX) { + *valuePtr = 2; + meta->direction = 0; + valueChanged = true; + } + } + } + } else { + if (x >= meta->knobX + 9 && x <= meta->knobX + 37) { + *valuePtr = 1; + if (value != 0) { + meta->direction = 1; + } else { + meta->direction = 0; + } + valueChanged = true; + } + } + + if (meta->valuesCount == 4) { + int v19 = meta->knobY + word_48FBFE[3]; + if (y >= v19 && y <= v19 + 2 * fontGetLineHeight() && x >= meta->knobX + word_48FBF6[3] && x <= meta->maxX) { + *valuePtr = 3; + meta->direction = 1; + valueChanged = true; + } + } + } else { + if (meta->direction != 0) { + if (value == 0) { + meta->direction = 0; + } + } else { + if (value == meta->valuesCount - 1) { + meta->direction = 1; + } + } + + if (meta->direction != 0) { + *valuePtr = value - 1; + } else { + *valuePtr = value + 1; + } + + valueChanged = true; + } + + if (valueChanged) { + soundPlayFile("ib3p1xx1"); + inputBlockForTocks(70); + soundPlayFile("ib3lu1x1"); + _UpdateThing(preferenceIndex); + windowRefresh(gPreferencesWindow); + _changed = true; + return; + } + } else if (preferenceIndex >= FIRST_SECONDARY_PREF && preferenceIndex <= LAST_SECONDARY_PREF) { + PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); + int* valuePtr = meta->valuePtr; + int value = *valuePtr; + bool valueChanged = false; + + int v1 = meta->knobX + 11; + int v2 = meta->knobY + 12; + + if (sqrt(pow((double)x - (double)v1, 2) + pow((double)y - (double)v2, 2)) > 10.0) { + int v23 = meta->knobY - 5; + if (y >= v23 && y <= v23 + fontGetLineHeight() + 2) { + if (x >= meta->minX && x <= meta->knobX) { + *valuePtr = preferenceIndex == PREF_COMBAT_MESSAGES ? 1 : 0; + valueChanged = true; + } else if (x >= meta->knobX + 22.0 && x <= meta->maxX) { + *valuePtr = preferenceIndex == PREF_COMBAT_MESSAGES ? 0 : 1; + valueChanged = true; + } + } + } else { + *valuePtr ^= 1; + valueChanged = true; + } + + if (valueChanged) { + soundPlayFile("ib2p1xx1"); + inputBlockForTocks(70); + soundPlayFile("ib2lu1x1"); + _UpdateThing(preferenceIndex); + windowRefresh(gPreferencesWindow); + _changed = true; + return; + } + } else if (preferenceIndex >= FIRST_RANGE_PREF && preferenceIndex <= LAST_RANGE_PREF) { + PreferenceDescription* meta = &(gPreferenceDescriptions[preferenceIndex]); + int* valuePtr = meta->valuePtr; + + soundPlayFile("ib1p1xx1"); + + double value; + switch (preferenceIndex) { + case PREF_TEXT_BASE_DELAY: + value = 6.0 - gPreferencesTextBaseDelay1 + 1.0; + break; + case PREF_BRIGHTNESS: + value = gPreferencesBrightness1; + break; + case PREF_MOUSE_SENSITIVIY: + value = gPreferencesMouseSensitivity1; + break; + default: + value = *valuePtr; + break; + } + + int knobX = (int)(219.0 / (meta->maxValue - meta->minValue)); + int v31 = (int)((value - meta->minValue) * (219.0 / (meta->maxValue - meta->minValue)) + 384.0); + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + PREFERENCES_WINDOW_WIDTH * meta->knobY + 384, 240, 12, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + 384, PREFERENCES_WINDOW_WIDTH); + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_ON].getData(), 21, 12, 21, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + v31, PREFERENCES_WINDOW_WIDTH); + + windowRefresh(gPreferencesWindow); + + int sfxVolumeExample = 0; + int speechVolumeExample = 0; + while (true) { + sharedFpsLimiter.mark(); + + inputGetInput(); + + int tick = getTicks(); + + mouseGetPositionInWindow(gPreferencesWindow, &x, &y); + + if (mouseGetEvent() & 0x10) { + soundPlayFile("ib1lu1x1"); + _UpdateThing(preferenceIndex); + windowRefresh(gPreferencesWindow); + renderPresent(); + _changed = true; + return; + } + + if (v31 + 14 > x) { + if (v31 + 6 > x) { + v31 = x - 6; + if (v31 < 384) { + v31 = 384; + } + } + } else { + v31 = x - 6; + if (v31 > 603) { + v31 = 603; + } + } + + double newValue = ((double)v31 - 384.0) / (219.0 / (meta->maxValue - meta->minValue)) + meta->minValue; + + int v52 = 0; + + switch (preferenceIndex) { + case PREF_COMBAT_SPEED: + *meta->valuePtr = (int)newValue; + break; + case PREF_TEXT_BASE_DELAY: + gPreferencesTextBaseDelay1 = 6.0 - newValue + 1.0; + break; + case PREF_MASTER_VOLUME: + *meta->valuePtr = (int)newValue; + gameSoundSetMasterVolume(gPreferencesMasterVolume1); + v52 = 1; + break; + case PREF_MUSIC_VOLUME: + *meta->valuePtr = (int)newValue; + backgroundSoundSetVolume(gPreferencesMusicVolume1); + v52 = 1; + break; + case PREF_SFX_VOLUME: + *meta->valuePtr = (int)newValue; + soundEffectsSetVolume(gPreferencesSoundEffectsVolume1); + v52 = 1; + if (sfxVolumeExample == 0) { + soundPlayFile("butin1"); + sfxVolumeExample = 7; + } else { + sfxVolumeExample--; + } + break; + case PREF_SPEECH_VOLUME: + *meta->valuePtr = (int)newValue; + speechSetVolume(gPreferencesSpeechVolume1); + v52 = 1; + if (speechVolumeExample == 0) { + speechLoad("narrator\\options", 12, 13, 15); + speechVolumeExample = 40; + } else { + speechVolumeExample--; + } + break; + case PREF_BRIGHTNESS: + gPreferencesBrightness1 = newValue; + colorSetBrightness(newValue); + break; + case PREF_MOUSE_SENSITIVIY: + gPreferencesMouseSensitivity1 = newValue; + break; + } + + if (v52) { + int off = PREFERENCES_WINDOW_WIDTH * (meta->knobY - 12) + 384; + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + off, 240, 24, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + off, PREFERENCES_WINDOW_WIDTH); + + for (int optionIndex = 0; optionIndex < meta->valuesCount; optionIndex++) { + const char* str = getmsg(&gPreferencesMessageList, &gPreferencesMessageListItem, meta->labelIds[optionIndex]); + + int x; + switch (optionIndex) { + case 0: + // 0x4926AA + x = 384; + // TODO: Incomplete. + break; + case 1: + // 0x4926F3 + switch (meta->valuesCount) { + case 2: + x = 624 - fontGetStringWidth(str); + break; + case 3: + // This code path does not use floating-point arithmetic + x = 504 - fontGetStringWidth(str) / 2 - 2; + break; + case 4: + // Uses floating-point arithmetic + x = 444 + fontGetStringWidth(str) / 2 - 8; + break; + } + break; + case 2: + // 0x492766 + switch (meta->valuesCount) { + case 3: + x = 624 - fontGetStringWidth(str); + break; + case 4: + // Uses floating-point arithmetic + x = 564 - fontGetStringWidth(str) - 4; + break; + } + break; + case 3: + // 0x49279E + x = 624 - fontGetStringWidth(str); + break; + } + fontDrawText(gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * (meta->knobY - 12) + x, str, PREFERENCES_WINDOW_WIDTH, PREFERENCES_WINDOW_WIDTH, _colorTable[18979]); + } + } else { + int off = PREFERENCES_WINDOW_WIDTH * meta->knobY + 384; + blitBufferToBuffer(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_BACKGROUND].getData() + off, 240, 12, PREFERENCES_WINDOW_WIDTH, gPreferencesWindowBuffer + off, PREFERENCES_WINDOW_WIDTH); + } + + blitBufferToBufferTrans(_preferencesFrmImages[PREFERENCES_WINDOW_FRM_KNOB_ON].getData(), 21, 12, 21, gPreferencesWindowBuffer + PREFERENCES_WINDOW_WIDTH * meta->knobY + v31, PREFERENCES_WINDOW_WIDTH); + windowRefresh(gPreferencesWindow); + + while (getTicksSince(tick) < 35) + ; + + renderPresent(); + sharedFpsLimiter.throttle(); + } + } else if (preferenceIndex == 19) { + gPreferencesPlayerSpeedup1 ^= 1; + } + + _changed = true; +} + +} // namespace fallout diff --git a/src/preferences.h b/src/preferences.h new file mode 100644 index 0000000..f9db7ec --- /dev/null +++ b/src/preferences.h @@ -0,0 +1,17 @@ +#ifndef FALLOUT_PREFERENCES_H_ +#define FALLOUT_PREFERENCES_H_ + +#include "db.h" + +namespace fallout { + +int preferencesInit(); +int doPreferences(bool animated); +int preferencesSave(File* stream); +int preferencesLoad(File* stream); +void brightnessIncrease(); +void brightnessDecrease(); + +} // namespace fallout + +#endif /* FALLOUT_PREFERENCES_H_ */ From 1570f860e8a3aa63d4026e6edd5f6b38ea49399e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 17 Feb 2023 15:53:42 +0300 Subject: [PATCH 39/50] Remove unnecessary calls --- src/preferences.cc | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/preferences.cc b/src/preferences.cc index 80a71ef..4cbd94b 100644 --- a/src/preferences.cc +++ b/src/preferences.cc @@ -6,7 +6,6 @@ #include "color.h" #include "combat.h" #include "combat_ai.h" -#include "cycle.h" #include "debug.h" #include "draw.h" #include "game.h" @@ -15,7 +14,6 @@ #include "graph_lib.h" #include "input.h" #include "kb.h" -#include "map.h" #include "message.h" #include "mouse.h" #include "palette.h" @@ -392,9 +390,6 @@ static PreferenceDescription gPreferenceDescriptions[PREF_COUNT] = { static FrmImage _preferencesFrmImages[PREFERENCES_WINDOW_FRM_COUNT]; static int _oldFont; -static bool _cycleWasEnabled; -static bool _isoWasEnabled; -static bool _mouseWasVisible; int preferencesInit() { @@ -981,22 +976,8 @@ static int preferencesWindowInit() return -1; } - gameMouseSetCursor(MOUSE_CURSOR_ARROW); - _oldFont = fontGetCurrent(); - _isoWasEnabled = isoDisable(); - - _cycleWasEnabled = colorCycleEnabled(); - if (_cycleWasEnabled) { - colorCycleDisable(); - } - - _mouseWasVisible = gameMouseObjectsIsVisible(); - if (_mouseWasVisible) { - gameMouseObjectsHide(); - } - _SaveSettings(); for (i = 0; i < PREFERENCES_WINDOW_FRM_COUNT; i++) { @@ -1210,18 +1191,6 @@ static int preferencesWindowFree() _preferencesFrmImages[index].unlock(); } - if (_mouseWasVisible) { - gameMouseObjectsShow(); - } - - if (_cycleWasEnabled) { - colorCycleEnable(); - } - - if (_isoWasEnabled) { - isoEnable(); - } - fontSetCurrent(_oldFont); messageListFree(&gPreferencesMessageList); From 3e8227a62bbd3aa034990740e4112e3ad6977f24 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 17 Feb 2023 15:55:14 +0300 Subject: [PATCH 40/50] Extract mainmenu --- CMakeLists.txt | 2 + src/main.cc | 397 +---------------------------------------------- src/mainmenu.cc | 399 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mainmenu.h | 28 ++++ 4 files changed, 430 insertions(+), 396 deletions(-) create mode 100644 src/mainmenu.cc create mode 100644 src/mainmenu.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b9ae928..0f55093 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/loadsave.h" "src/main.cc" "src/main.h" + "src/mainmenu.cc" + "src/mainmenu.h" "src/map_defs.h" "src/map.cc" "src/map.h" diff --git a/src/main.cc b/src/main.cc index aff1459..a016f16 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,5 @@ #include "main.h" -#include #include #include @@ -21,6 +20,7 @@ #include "input.h" #include "kb.h" #include "loadsave.h" +#include "mainmenu.h" #include "map.h" #include "mouse.h" #include "object.h" @@ -35,7 +35,6 @@ #include "sfall_config.h" #include "svga.h" #include "text_font.h" -#include "version.h" #include "window.h" #include "window_manager.h" #include "window_manager_private.h" @@ -44,35 +43,9 @@ namespace fallout { -#define MAIN_MENU_WINDOW_WIDTH 640 -#define MAIN_MENU_WINDOW_HEIGHT 480 - #define DEATH_WINDOW_WIDTH 640 #define DEATH_WINDOW_HEIGHT 480 -typedef enum MainMenuButton { - MAIN_MENU_BUTTON_INTRO, - MAIN_MENU_BUTTON_NEW_GAME, - MAIN_MENU_BUTTON_LOAD_GAME, - MAIN_MENU_BUTTON_OPTIONS, - MAIN_MENU_BUTTON_CREDITS, - MAIN_MENU_BUTTON_EXIT, - MAIN_MENU_BUTTON_COUNT, -} MainMenuButton; - -typedef enum MainMenuOption { - MAIN_MENU_INTRO, - MAIN_MENU_NEW_GAME, - MAIN_MENU_LOAD_GAME, - MAIN_MENU_SCREENSAVER, - MAIN_MENU_TIMEOUT, - MAIN_MENU_CREDITS, - MAIN_MENU_QUOTES, - MAIN_MENU_EXIT, - MAIN_MENU_SELFRUN, - MAIN_MENU_OPTIONS, -} MainMenuOption; - static bool falloutInit(int argc, char** argv); static int main_reset_system(); static void main_exit_system(); @@ -87,14 +60,6 @@ static void showDeath(); static void _main_death_voiceover_callback(); static int _mainDeathGrabTextFile(const char* fileName, char* dest); static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* count); -static int mainMenuWindowInit(); -static void mainMenuWindowFree(); -static void mainMenuWindowHide(bool animate); -static void mainMenuWindowUnhide(bool animate); -static int _main_menu_is_enabled(); -static int mainMenuWindowHandleEvents(); -static int main_menu_fatal_error(); -static void main_menu_play_sound(const char* fileName); // 0x5194C8 static char _mainMap[] = "artemple.map"; @@ -124,54 +89,9 @@ static bool _main_show_death_scene = false; // 0x5194EC static bool gMainMenuScreensaverCycle = false; -// 0x5194F0 -static int gMainMenuWindow = -1; - -// 0x5194F4 -static unsigned char* gMainMenuWindowBuffer = NULL; - -// 0x519504 -static bool _in_main_menu = false; - -// 0x519508 -static bool gMainMenuWindowInitialized = false; - -// 0x51950C -static unsigned int gMainMenuScreensaverDelay = 120000; - -// 0x519510 -static const int gMainMenuButtonKeyBindings[MAIN_MENU_BUTTON_COUNT] = { - KEY_LOWERCASE_I, // intro - KEY_LOWERCASE_N, // new game - KEY_LOWERCASE_L, // load game - KEY_LOWERCASE_O, // options - KEY_LOWERCASE_C, // credits - KEY_LOWERCASE_E, // exit -}; - -// 0x519528 -static const int _return_values[MAIN_MENU_BUTTON_COUNT] = { - MAIN_MENU_INTRO, - MAIN_MENU_NEW_GAME, - MAIN_MENU_LOAD_GAME, - MAIN_MENU_OPTIONS, - MAIN_MENU_CREDITS, - MAIN_MENU_EXIT, -}; - // 0x614838 static bool _main_death_voiceover_done; -// 0x614840 -static int gMainMenuButtons[MAIN_MENU_BUTTON_COUNT]; - -// 0x614858 -static bool gMainMenuWindowHidden; - -static FrmImage _mainMenuBackgroundFrmImage; -static FrmImage _mainMenuButtonNormalFrmImage; -static FrmImage _mainMenuButtonPressedFrmImage; - // 0x48099C int falloutMain(int argc, char** argv) { @@ -790,319 +710,4 @@ static int _mainDeathWordWrap(char* text, int width, short* beginnings, short* c return 0; } -// 0x481650 -static int mainMenuWindowInit() -{ - int fid; - MessageListItem msg; - int len; - - if (gMainMenuWindowInitialized) { - return 0; - } - - colorPaletteLoad("color.pal"); - - int mainMenuWindowX = (screenGetWidth() - MAIN_MENU_WINDOW_WIDTH) / 2; - int mainMenuWindowY = (screenGetHeight() - MAIN_MENU_WINDOW_HEIGHT) / 2; - gMainMenuWindow = windowCreate(mainMenuWindowX, - mainMenuWindowY, - MAIN_MENU_WINDOW_WIDTH, - MAIN_MENU_WINDOW_HEIGHT, - 0, - WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP); - if (gMainMenuWindow == -1) { - // NOTE: Uninline. - return main_menu_fatal_error(); - } - - gMainMenuWindowBuffer = windowGetBuffer(gMainMenuWindow); - - // mainmenu.frm - int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 140, 0, 0, 0); - if (!_mainMenuBackgroundFrmImage.lock(backgroundFid)) { - // NOTE: Uninline. - return main_menu_fatal_error(); - } - - blitBufferToBuffer(_mainMenuBackgroundFrmImage.getData(), 640, 480, 640, gMainMenuWindowBuffer, 640); - _mainMenuBackgroundFrmImage.unlock(); - - int oldFont = fontGetCurrent(); - fontSetCurrent(100); - - // SFALL: Allow to change font color/flags of copyright/version text - // It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option - // 0x010000 - change the color for version string only - // 0x020000 - underline text (only for the version string) - // 0x040000 - monospace font (only for the version string) - int fontSettings = _colorTable[21091], fontSettingsSFall = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall); - if (fontSettingsSFall && !(fontSettingsSFall & 0x010000)) - fontSettings = fontSettingsSFall & 0xFF; - - // SFALL: Allow to move copyright text - int offsetX = 0, offsetY = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY); - - // Copyright. - msg.num = 20; - if (messageListGetItem(&gMiscMessageList, &msg)) { - windowDrawText(gMainMenuWindow, msg.text, 0, offsetX + 15, offsetY + 460, fontSettings | 0x06000000); - } - - // SFALL: Make sure font settings are applied when using 0x010000 flag - if (fontSettingsSFall) - fontSettings = fontSettingsSFall; - - // TODO: Allow to move version text - // Version. - char version[VERSION_MAX]; - versionGetVersion(version, sizeof(version)); - len = fontGetStringWidth(version); - windowDrawText(gMainMenuWindow, version, 0, 615 - len, 460, fontSettings | 0x06000000); - - // menuup.frm - fid = buildFid(OBJ_TYPE_INTERFACE, 299, 0, 0, 0); - if (!_mainMenuButtonNormalFrmImage.lock(fid)) { - // NOTE: Uninline. - return main_menu_fatal_error(); - } - - // menudown.frm - fid = buildFid(OBJ_TYPE_INTERFACE, 300, 0, 0, 0); - if (!_mainMenuButtonPressedFrmImage.lock(fid)) { - // NOTE: Uninline. - return main_menu_fatal_error(); - } - - for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { - gMainMenuButtons[index] = -1; - } - - // SFALL: Allow to move menu buttons - offsetX = offsetY = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY); - - for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { - gMainMenuButtons[index] = buttonCreate(gMainMenuWindow, - offsetX + 30, - offsetY + 19 + index * 42 - index, - 26, - 26, - -1, - -1, - 1111, - gMainMenuButtonKeyBindings[index], - _mainMenuButtonNormalFrmImage.getData(), - _mainMenuButtonPressedFrmImage.getData(), - 0, - BUTTON_FLAG_TRANSPARENT); - if (gMainMenuButtons[index] == -1) { - // NOTE: Uninline. - return main_menu_fatal_error(); - } - - buttonSetMask(gMainMenuButtons[index], _mainMenuButtonNormalFrmImage.getData()); - } - - fontSetCurrent(104); - - // SFALL: Allow to change font color of buttons - fontSettings = _colorTable[21091]; - fontSettingsSFall = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall); - if (fontSettingsSFall) - fontSettings = fontSettingsSFall & 0xFF; - - for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { - msg.num = 9 + index; - if (messageListGetItem(&gMiscMessageList, &msg)) { - len = fontGetStringWidth(msg.text); - fontDrawText(gMainMenuWindowBuffer + offsetX + 640 * (offsetY + 42 * index - index + 20) + 126 - (len / 2), msg.text, 640 - (126 - (len / 2)) - 1, 640, fontSettings); - } - } - - fontSetCurrent(oldFont); - - gMainMenuWindowInitialized = true; - gMainMenuWindowHidden = true; - - return 0; -} - -// 0x481968 -static void mainMenuWindowFree() -{ - if (!gMainMenuWindowInitialized) { - return; - } - - for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { - // FIXME: Why it tries to free only invalid buttons? - if (gMainMenuButtons[index] == -1) { - buttonDestroy(gMainMenuButtons[index]); - } - } - - _mainMenuButtonPressedFrmImage.unlock(); - _mainMenuButtonNormalFrmImage.unlock(); - - if (gMainMenuWindow != -1) { - windowDestroy(gMainMenuWindow); - } - - gMainMenuWindowInitialized = false; -} - -// 0x481A00 -static void mainMenuWindowHide(bool animate) -{ - if (!gMainMenuWindowInitialized) { - return; - } - - if (gMainMenuWindowHidden) { - return; - } - - soundContinueAll(); - - if (animate) { - paletteFadeTo(gPaletteBlack); - soundContinueAll(); - } - - windowHide(gMainMenuWindow); - - gMainMenuWindowHidden = true; -} - -// 0x481A48 -static void mainMenuWindowUnhide(bool animate) -{ - if (!gMainMenuWindowInitialized) { - return; - } - - if (!gMainMenuWindowHidden) { - return; - } - - windowShow(gMainMenuWindow); - - if (animate) { - colorPaletteLoad("color.pal"); - paletteFadeTo(_cmap); - } - - gMainMenuWindowHidden = false; -} - -// 0x481AA8 -static int _main_menu_is_enabled() -{ - return 1; -} - -// 0x481AEC -static int mainMenuWindowHandleEvents() -{ - _in_main_menu = true; - - bool oldCursorIsHidden = cursorIsHidden(); - if (oldCursorIsHidden) { - mouseShowCursor(); - } - - unsigned int tick = getTicks(); - - int rc = -1; - while (rc == -1) { - sharedFpsLimiter.mark(); - - int keyCode = inputGetInput(); - - for (int buttonIndex = 0; buttonIndex < MAIN_MENU_BUTTON_COUNT; buttonIndex++) { - if (keyCode == gMainMenuButtonKeyBindings[buttonIndex] || keyCode == toupper(gMainMenuButtonKeyBindings[buttonIndex])) { - // NOTE: Uninline. - main_menu_play_sound("nmselec1"); - - rc = _return_values[buttonIndex]; - - if (buttonIndex == MAIN_MENU_BUTTON_CREDITS && (gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT] != KEY_STATE_UP || gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] != KEY_STATE_UP)) { - rc = MAIN_MENU_QUOTES; - } - - break; - } - } - - if (rc == -1) { - if (keyCode == KEY_CTRL_R) { - rc = MAIN_MENU_SELFRUN; - continue; - } else if (keyCode == KEY_PLUS || keyCode == KEY_EQUAL) { - brightnessIncrease(); - } else if (keyCode == KEY_MINUS || keyCode == KEY_UNDERSCORE) { - brightnessDecrease(); - } else if (keyCode == KEY_UPPERCASE_D || keyCode == KEY_LOWERCASE_D) { - rc = MAIN_MENU_SCREENSAVER; - continue; - } else if (keyCode == 1111) { - if (!(mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT)) { - // NOTE: Uninline. - main_menu_play_sound("nmselec0"); - } - continue; - } - } - - if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit == 3) { - rc = MAIN_MENU_EXIT; - - // NOTE: Uninline. - main_menu_play_sound("nmselec1"); - break; - } else if (_game_user_wants_to_quit == 2) { - _game_user_wants_to_quit = 0; - } else { - if (getTicksSince(tick) >= gMainMenuScreensaverDelay) { - rc = MAIN_MENU_TIMEOUT; - } - } - - renderPresent(); - sharedFpsLimiter.throttle(); - } - - if (oldCursorIsHidden) { - mouseHideCursor(); - } - - _in_main_menu = false; - - return rc; -} - -// NOTE: Inlined. -// -// 0x481C88 -static int main_menu_fatal_error() -{ - mainMenuWindowFree(); - - return -1; -} - -// NOTE: Inlined. -// -// 0x481C94 -static void main_menu_play_sound(const char* fileName) -{ - soundPlayFile(fileName); -} - } // namespace fallout diff --git a/src/mainmenu.cc b/src/mainmenu.cc new file mode 100644 index 0000000..c3ac817 --- /dev/null +++ b/src/mainmenu.cc @@ -0,0 +1,399 @@ +#include "mainmenu.h" + +#include + +#include "art.h" +#include "color.h" +#include "draw.h" +#include "game.h" +#include "game_sound.h" +#include "input.h" +#include "kb.h" +#include "mouse.h" +#include "palette.h" +#include "preferences.h" +#include "sfall_config.h" +#include "svga.h" +#include "text_font.h" +#include "version.h" +#include "window_manager.h" + +namespace fallout { + +#define MAIN_MENU_WINDOW_WIDTH 640 +#define MAIN_MENU_WINDOW_HEIGHT 480 + +typedef enum MainMenuButton { + MAIN_MENU_BUTTON_INTRO, + MAIN_MENU_BUTTON_NEW_GAME, + MAIN_MENU_BUTTON_LOAD_GAME, + MAIN_MENU_BUTTON_OPTIONS, + MAIN_MENU_BUTTON_CREDITS, + MAIN_MENU_BUTTON_EXIT, + MAIN_MENU_BUTTON_COUNT, +} MainMenuButton; + +static int main_menu_fatal_error(); +static void main_menu_play_sound(const char* fileName); + +// 0x5194F0 +static int gMainMenuWindow = -1; + +// 0x5194F4 +static unsigned char* gMainMenuWindowBuffer = NULL; + +// 0x519504 +static bool _in_main_menu = false; + +// 0x519508 +static bool gMainMenuWindowInitialized = false; + +// 0x51950C +static unsigned int gMainMenuScreensaverDelay = 120000; + +// 0x519510 +static const int gMainMenuButtonKeyBindings[MAIN_MENU_BUTTON_COUNT] = { + KEY_LOWERCASE_I, // intro + KEY_LOWERCASE_N, // new game + KEY_LOWERCASE_L, // load game + KEY_LOWERCASE_O, // options + KEY_LOWERCASE_C, // credits + KEY_LOWERCASE_E, // exit +}; + +// 0x519528 +static const int _return_values[MAIN_MENU_BUTTON_COUNT] = { + MAIN_MENU_INTRO, + MAIN_MENU_NEW_GAME, + MAIN_MENU_LOAD_GAME, + MAIN_MENU_OPTIONS, + MAIN_MENU_CREDITS, + MAIN_MENU_EXIT, +}; + +// 0x614840 +static int gMainMenuButtons[MAIN_MENU_BUTTON_COUNT]; + +// 0x614858 +static bool gMainMenuWindowHidden; + +static FrmImage _mainMenuBackgroundFrmImage; +static FrmImage _mainMenuButtonNormalFrmImage; +static FrmImage _mainMenuButtonPressedFrmImage; + +// 0x481650 +int mainMenuWindowInit() +{ + int fid; + MessageListItem msg; + int len; + + if (gMainMenuWindowInitialized) { + return 0; + } + + colorPaletteLoad("color.pal"); + + int mainMenuWindowX = (screenGetWidth() - MAIN_MENU_WINDOW_WIDTH) / 2; + int mainMenuWindowY = (screenGetHeight() - MAIN_MENU_WINDOW_HEIGHT) / 2; + gMainMenuWindow = windowCreate(mainMenuWindowX, + mainMenuWindowY, + MAIN_MENU_WINDOW_WIDTH, + MAIN_MENU_WINDOW_HEIGHT, + 0, + WINDOW_HIDDEN | WINDOW_MOVE_ON_TOP); + if (gMainMenuWindow == -1) { + // NOTE: Uninline. + return main_menu_fatal_error(); + } + + gMainMenuWindowBuffer = windowGetBuffer(gMainMenuWindow); + + // mainmenu.frm + int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 140, 0, 0, 0); + if (!_mainMenuBackgroundFrmImage.lock(backgroundFid)) { + // NOTE: Uninline. + return main_menu_fatal_error(); + } + + blitBufferToBuffer(_mainMenuBackgroundFrmImage.getData(), 640, 480, 640, gMainMenuWindowBuffer, 640); + _mainMenuBackgroundFrmImage.unlock(); + + int oldFont = fontGetCurrent(); + fontSetCurrent(100); + + // SFALL: Allow to change font color/flags of copyright/version text + // It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option + // 0x010000 - change the color for version string only + // 0x020000 - underline text (only for the version string) + // 0x040000 - monospace font (only for the version string) + int fontSettings = _colorTable[21091], fontSettingsSFall = 0; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall); + if (fontSettingsSFall && !(fontSettingsSFall & 0x010000)) + fontSettings = fontSettingsSFall & 0xFF; + + // SFALL: Allow to move copyright text + int offsetX = 0, offsetY = 0; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY); + + // Copyright. + msg.num = 20; + if (messageListGetItem(&gMiscMessageList, &msg)) { + windowDrawText(gMainMenuWindow, msg.text, 0, offsetX + 15, offsetY + 460, fontSettings | 0x06000000); + } + + // SFALL: Make sure font settings are applied when using 0x010000 flag + if (fontSettingsSFall) + fontSettings = fontSettingsSFall; + + // TODO: Allow to move version text + // Version. + char version[VERSION_MAX]; + versionGetVersion(version, sizeof(version)); + len = fontGetStringWidth(version); + windowDrawText(gMainMenuWindow, version, 0, 615 - len, 460, fontSettings | 0x06000000); + + // menuup.frm + fid = buildFid(OBJ_TYPE_INTERFACE, 299, 0, 0, 0); + if (!_mainMenuButtonNormalFrmImage.lock(fid)) { + // NOTE: Uninline. + return main_menu_fatal_error(); + } + + // menudown.frm + fid = buildFid(OBJ_TYPE_INTERFACE, 300, 0, 0, 0); + if (!_mainMenuButtonPressedFrmImage.lock(fid)) { + // NOTE: Uninline. + return main_menu_fatal_error(); + } + + for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { + gMainMenuButtons[index] = -1; + } + + // SFALL: Allow to move menu buttons + offsetX = offsetY = 0; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY); + + for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { + gMainMenuButtons[index] = buttonCreate(gMainMenuWindow, + offsetX + 30, + offsetY + 19 + index * 42 - index, + 26, + 26, + -1, + -1, + 1111, + gMainMenuButtonKeyBindings[index], + _mainMenuButtonNormalFrmImage.getData(), + _mainMenuButtonPressedFrmImage.getData(), + 0, + BUTTON_FLAG_TRANSPARENT); + if (gMainMenuButtons[index] == -1) { + // NOTE: Uninline. + return main_menu_fatal_error(); + } + + buttonSetMask(gMainMenuButtons[index], _mainMenuButtonNormalFrmImage.getData()); + } + + fontSetCurrent(104); + + // SFALL: Allow to change font color of buttons + fontSettings = _colorTable[21091]; + fontSettingsSFall = 0; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall); + if (fontSettingsSFall) + fontSettings = fontSettingsSFall & 0xFF; + + for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { + msg.num = 9 + index; + if (messageListGetItem(&gMiscMessageList, &msg)) { + len = fontGetStringWidth(msg.text); + fontDrawText(gMainMenuWindowBuffer + offsetX + 640 * (offsetY + 42 * index - index + 20) + 126 - (len / 2), msg.text, 640 - (126 - (len / 2)) - 1, 640, fontSettings); + } + } + + fontSetCurrent(oldFont); + + gMainMenuWindowInitialized = true; + gMainMenuWindowHidden = true; + + return 0; +} + +// 0x481968 +void mainMenuWindowFree() +{ + if (!gMainMenuWindowInitialized) { + return; + } + + for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { + // FIXME: Why it tries to free only invalid buttons? + if (gMainMenuButtons[index] == -1) { + buttonDestroy(gMainMenuButtons[index]); + } + } + + _mainMenuButtonPressedFrmImage.unlock(); + _mainMenuButtonNormalFrmImage.unlock(); + + if (gMainMenuWindow != -1) { + windowDestroy(gMainMenuWindow); + } + + gMainMenuWindowInitialized = false; +} + +// 0x481A00 +void mainMenuWindowHide(bool animate) +{ + if (!gMainMenuWindowInitialized) { + return; + } + + if (gMainMenuWindowHidden) { + return; + } + + soundContinueAll(); + + if (animate) { + paletteFadeTo(gPaletteBlack); + soundContinueAll(); + } + + windowHide(gMainMenuWindow); + + gMainMenuWindowHidden = true; +} + +// 0x481A48 +void mainMenuWindowUnhide(bool animate) +{ + if (!gMainMenuWindowInitialized) { + return; + } + + if (!gMainMenuWindowHidden) { + return; + } + + windowShow(gMainMenuWindow); + + if (animate) { + colorPaletteLoad("color.pal"); + paletteFadeTo(_cmap); + } + + gMainMenuWindowHidden = false; +} + +// 0x481AA8 +int _main_menu_is_enabled() +{ + return 1; +} + +// 0x481AEC +int mainMenuWindowHandleEvents() +{ + _in_main_menu = true; + + bool oldCursorIsHidden = cursorIsHidden(); + if (oldCursorIsHidden) { + mouseShowCursor(); + } + + unsigned int tick = getTicks(); + + int rc = -1; + while (rc == -1) { + sharedFpsLimiter.mark(); + + int keyCode = inputGetInput(); + + for (int buttonIndex = 0; buttonIndex < MAIN_MENU_BUTTON_COUNT; buttonIndex++) { + if (keyCode == gMainMenuButtonKeyBindings[buttonIndex] || keyCode == toupper(gMainMenuButtonKeyBindings[buttonIndex])) { + // NOTE: Uninline. + main_menu_play_sound("nmselec1"); + + rc = _return_values[buttonIndex]; + + if (buttonIndex == MAIN_MENU_BUTTON_CREDITS && (gPressedPhysicalKeys[SDL_SCANCODE_RSHIFT] != KEY_STATE_UP || gPressedPhysicalKeys[SDL_SCANCODE_LSHIFT] != KEY_STATE_UP)) { + rc = MAIN_MENU_QUOTES; + } + + break; + } + } + + if (rc == -1) { + if (keyCode == KEY_CTRL_R) { + rc = MAIN_MENU_SELFRUN; + continue; + } else if (keyCode == KEY_PLUS || keyCode == KEY_EQUAL) { + brightnessIncrease(); + } else if (keyCode == KEY_MINUS || keyCode == KEY_UNDERSCORE) { + brightnessDecrease(); + } else if (keyCode == KEY_UPPERCASE_D || keyCode == KEY_LOWERCASE_D) { + rc = MAIN_MENU_SCREENSAVER; + continue; + } else if (keyCode == 1111) { + if (!(mouseGetEvent() & MOUSE_EVENT_LEFT_BUTTON_REPEAT)) { + // NOTE: Uninline. + main_menu_play_sound("nmselec0"); + } + continue; + } + } + + if (keyCode == KEY_ESCAPE || _game_user_wants_to_quit == 3) { + rc = MAIN_MENU_EXIT; + + // NOTE: Uninline. + main_menu_play_sound("nmselec1"); + break; + } else if (_game_user_wants_to_quit == 2) { + _game_user_wants_to_quit = 0; + } else { + if (getTicksSince(tick) >= gMainMenuScreensaverDelay) { + rc = MAIN_MENU_TIMEOUT; + } + } + + renderPresent(); + sharedFpsLimiter.throttle(); + } + + if (oldCursorIsHidden) { + mouseHideCursor(); + } + + _in_main_menu = false; + + return rc; +} + +// NOTE: Inlined. +// +// 0x481C88 +static int main_menu_fatal_error() +{ + mainMenuWindowFree(); + + return -1; +} + +// NOTE: Inlined. +// +// 0x481C94 +static void main_menu_play_sound(const char* fileName) +{ + soundPlayFile(fileName); +} + +} // namespace fallout diff --git a/src/mainmenu.h b/src/mainmenu.h new file mode 100644 index 0000000..d5b81a4 --- /dev/null +++ b/src/mainmenu.h @@ -0,0 +1,28 @@ +#ifndef FALLOUT_MAINMENU_H_ +#define FALLOUT_MAINMENU_H_ + +namespace fallout { + +typedef enum MainMenuOption { + MAIN_MENU_INTRO, + MAIN_MENU_NEW_GAME, + MAIN_MENU_LOAD_GAME, + MAIN_MENU_SCREENSAVER, + MAIN_MENU_TIMEOUT, + MAIN_MENU_CREDITS, + MAIN_MENU_QUOTES, + MAIN_MENU_EXIT, + MAIN_MENU_SELFRUN, + MAIN_MENU_OPTIONS, +} MainMenuOption; + +int mainMenuWindowInit(); +void mainMenuWindowFree(); +void mainMenuWindowHide(bool animate); +void mainMenuWindowUnhide(bool animate); +int _main_menu_is_enabled(); +int mainMenuWindowHandleEvents(); + +} // namespace fallout + +#endif /* FALLOUT_MAINMENU_H_ */ From 249892716e694535d22e8dfb83d1c73dd0e841c3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 18 Feb 2023 18:14:30 +0300 Subject: [PATCH 41/50] Get rid of mmx stuff --- CMakeLists.txt | 2 -- src/draw.cc | 34 +++++++++++++++++++++++++--- src/draw.h | 2 ++ src/mmx.cc | 60 -------------------------------------------------- src/mmx.h | 12 ---------- src/svga.cc | 25 --------------------- src/svga.h | 3 --- 7 files changed, 33 insertions(+), 105 deletions(-) delete mode 100644 src/mmx.cc delete mode 100644 src/mmx.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f55093..e671d8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,8 +152,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/memory.h" "src/message.cc" "src/message.h" - "src/mmx.cc" - "src/mmx.h" "src/mouse_manager.cc" "src/mouse_manager.h" "src/mouse.cc" diff --git a/src/draw.cc b/src/draw.cc index 592273f..674b0ac 100644 --- a/src/draw.cc +++ b/src/draw.cc @@ -3,7 +3,6 @@ #include #include "color.h" -#include "mmx.h" #include "svga.h" namespace fallout { @@ -208,13 +207,13 @@ void blitBufferToBufferStretchTrans(unsigned char* src, int srcWidth, int srcHei // 0x4D36D4 void blitBufferToBuffer(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch) { - mmxBlit(dest, destPitch, src, srcPitch, width, height); + srcCopy(dest, destPitch, src, srcPitch, width, height); } // 0x4D3704 void blitBufferToBufferTrans(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch) { - mmxBlitTrans(dest, destPitch, src, srcPitch, width, height); + transSrcCopy(dest, destPitch, src, srcPitch, width, height); } // 0x4D387C @@ -311,4 +310,33 @@ void bufferOutline(unsigned char* buf, int width, int height, int pitch, int col } } +// 0x4E0DB0 +void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height) +{ + for (int y = 0; y < height; y++) { + memcpy(dest, src, width); + dest += destPitch; + src += srcPitch; + } +} + +// 0x4E0ED5 +void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height) +{ + int destSkip = destPitch - width; + int srcSkip = srcPitch - width; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + unsigned char c = *src++; + if (c != 0) { + *dest = c; + } + dest++; + } + src += srcSkip; + dest += destSkip; + } +} + } // namespace fallout diff --git a/src/draw.h b/src/draw.h index 49d068b..47601c0 100644 --- a/src/draw.h +++ b/src/draw.h @@ -15,6 +15,8 @@ void _buf_texture(unsigned char* buf, int width, int height, int pitch, void* a5 void _lighten_buf(unsigned char* buf, int width, int height, int pitch); void _swap_color_buf(unsigned char* buf, int width, int height, int pitch, int color1, int color2); void bufferOutline(unsigned char* buf, int width, int height, int pitch, int a5); +void srcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height); +void transSrcCopy(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height); } // namespace fallout diff --git a/src/mmx.cc b/src/mmx.cc deleted file mode 100644 index 8a5b4d1..0000000 --- a/src/mmx.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include "mmx.h" - -#include - -#include "svga.h" - -namespace fallout { - -// Return `true` if CPU supports MMX. -// -// 0x4E08A0 -bool mmxIsSupported() -{ - return SDL_HasMMX() == SDL_TRUE; -} - -// 0x4E0DB0 -void mmxBlit(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height) -{ - if (gMmxEnabled) { - // TODO: Blit with MMX. - gMmxEnabled = false; - mmxBlit(dest, destPitch, src, srcPitch, width, height); - gMmxEnabled = true; - } else { - for (int y = 0; y < height; y++) { - memcpy(dest, src, width); - dest += destPitch; - src += srcPitch; - } - } -} - -// 0x4E0ED5 -void mmxBlitTrans(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height) -{ - if (gMmxEnabled) { - // TODO: Blit with MMX. - gMmxEnabled = false; - mmxBlitTrans(dest, destPitch, src, srcPitch, width, height); - gMmxEnabled = true; - } else { - int destSkip = destPitch - width; - int srcSkip = srcPitch - width; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - unsigned char c = *src++; - if (c != 0) { - *dest = c; - } - dest++; - } - src += srcSkip; - dest += destSkip; - } - } -} - -} // namespace fallout diff --git a/src/mmx.h b/src/mmx.h deleted file mode 100644 index 051397b..0000000 --- a/src/mmx.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef MMX_H -#define MMX_H - -namespace fallout { - -bool mmxIsSupported(); -void mmxBlit(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height); -void mmxBlitTrans(unsigned char* dest, int destPitch, unsigned char* src, int srcPitch, int width, int height); - -} // namespace fallout - -#endif /* MMX_H */ diff --git a/src/svga.cc b/src/svga.cc index bdcc9b1..e361fea 100644 --- a/src/svga.cc +++ b/src/svga.cc @@ -9,7 +9,6 @@ #include "draw.h" #include "interface.h" #include "memory.h" -#include "mmx.h" #include "mouse.h" #include "win32.h" #include "window_manager.h" @@ -20,9 +19,6 @@ namespace fallout { static bool createRenderer(int width, int height); static void destroyRenderer(); -// 0x51E2C8 -bool gMmxEnabled = true; - // screen rect Rect _scr_size; @@ -41,25 +37,6 @@ SDL_Surface* gSdlTextureSurface = NULL; // TODO: Remove once migration to update-render cycle is completed. FpsLimiter sharedFpsLimiter; -// 0x4CACD0 -void mmxSetEnabled(bool a1) -{ - // 0x51E2CC - static bool probed = false; - - // 0x6ACA20 - static bool supported; - - if (!probed) { - supported = mmxIsSupported(); - probed = true; - } - - if (supported) { - gMmxEnabled = a1; - } -} - // 0x4CAD08 int _init_mode_320_200() { @@ -165,8 +142,6 @@ int _GNW95_init_mode_ex(int width, int height, int bpp) _scr_size.right = width - 1; _scr_size.bottom = height - 1; - mmxSetEnabled(true); - _mouse_blit_trans = NULL; _scr_blit = _GNW95_ShowRect; _zero_mem = _GNW95_zero_vid_mem; diff --git a/src/svga.h b/src/svga.h index 1de0d0a..510d724 100644 --- a/src/svga.h +++ b/src/svga.h @@ -8,8 +8,6 @@ namespace fallout { -extern bool gMmxEnabled; - 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)(); @@ -21,7 +19,6 @@ extern SDL_Texture* gSdlTexture; extern SDL_Surface* gSdlTextureSurface; extern FpsLimiter sharedFpsLimiter; -void mmxSetEnabled(bool a1); int _init_mode_320_200(); int _init_mode_320_400(); int _init_mode_640_480_16(); From 81b934530319fd102c8d3eef05503d08658652f4 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sat, 18 Feb 2023 19:06:20 +0300 Subject: [PATCH 42/50] Update config --- CMakeLists.txt | 7 ++----- src/autorun.cc | 2 -- src/file_find.h | 2 -- src/platform_compat.cc | 2 -- src/win32.h | 2 -- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e671d8a..3cf9e53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,11 +284,8 @@ if(WIN32) target_compile_definitions(${EXECUTABLE_NAME} PUBLIC _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS - ) -else() - target_compile_definitions(${EXECUTABLE_NAME} PRIVATE - $<$:_DEBUG> - $<$:_DEBUG> + NOMINMAX + WIN32_LEAN_AND_MEAN ) endif() diff --git a/src/autorun.cc b/src/autorun.cc index 5bb9bed..97aabd7 100644 --- a/src/autorun.cc +++ b/src/autorun.cc @@ -1,8 +1,6 @@ #include "autorun.h" #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #endif diff --git a/src/file_find.h b/src/file_find.h index 112c51d..09b72b9 100644 --- a/src/file_find.h +++ b/src/file_find.h @@ -2,8 +2,6 @@ #define FILE_FIND_H #if defined(_WIN32) -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #else #include diff --git a/src/platform_compat.cc b/src/platform_compat.cc index 339739c..5297592 100644 --- a/src/platform_compat.cc +++ b/src/platform_compat.cc @@ -3,8 +3,6 @@ #include #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #endif diff --git a/src/win32.h b/src/win32.h index 6742458..cc5cc64 100644 --- a/src/win32.h +++ b/src/win32.h @@ -2,8 +2,6 @@ #define WIN32_H #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #endif From 6398f8a79ec2bcad38369d3fe60138f0cae31777 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 28 Feb 2023 16:11:48 +0300 Subject: [PATCH 43/50] Fix itemDropAll See #253 --- src/item.cc | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/item.cc b/src/item.cc index 9f29d36..0631d52 100644 --- a/src/item.cc +++ b/src/item.cc @@ -574,8 +574,8 @@ int itemDropAll(Object* critter, int tile) int frmId = critter->fid & 0xFFF; Inventory* inventory = &(critter->data.inventory); - for (int index = 0; index < inventory->length; index++) { - InventoryItem* inventoryItem = &(inventory->items[index]); + while (inventory->length > 0) { + InventoryItem* inventoryItem = &(inventory->items[0]); Object* item = inventoryItem->item; if (item->pid == PROTO_ID_MONEY) { if (itemRemove(critter, item, inventoryItem->quantity) != 0) { @@ -605,7 +605,19 @@ int itemDropAll(Object* critter, int tile) } } - for (int index = 0; index < inventoryItem->quantity; index++) { + // This loop is a little bit tricky. `inventoryItem` is a pointer + // to the first entry in inventory. It's `quantity` is dynamically + // decremented during `itemRemove`. It's `item` is also updated with + // a replacement (`itemRemove` creates new Object instance in + // inventory). + // + // Once entire item stack is dropped, the content pointed to by + // `inventoryItem` is also updated (see `item_compact`), it points + // to the next inventory item. It can also become dangling pointer + // (when `inventoryItem` entry is the last in inventory). + int quantity = inventoryItem->quantity; + for (int it = 0; it < quantity; it++) { + item = inventoryItem->item; if (itemRemove(critter, item, 1) != 0) { return -1; } @@ -629,7 +641,7 @@ int itemDropAll(Object* critter, int tile) } } - return -1; + return 0; } // 0x4779F0 From 117b1d7b448984c5a08f9939e4cecec4f569f75b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 28 Feb 2023 20:31:19 +0300 Subject: [PATCH 44/50] Fix iPad settings Follow-up to alexbatalov/fallout1-ce#29 --- os/ios/Info.plist | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/os/ios/Info.plist b/os/ios/Info.plist index ad64f3b..3eedf54 100644 --- a/os/ios/Info.plist +++ b/os/ios/Info.plist @@ -32,6 +32,11 @@ True UIApplicationSupportsIndirectInputEvents + UIDeviceFamily + + 1 + 2 + UIFileSharingEnabled UILaunchStoryboardName @@ -49,8 +54,6 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown From e23b39abaa9eb08c60bea27024159dcfcd812219 Mon Sep 17 00:00:00 2001 From: TomArnaez <48050423+TomArnaez@users.noreply.github.com> Date: Tue, 28 Feb 2023 19:11:08 +0000 Subject: [PATCH 45/50] Fix crash in inventory list (#256) --- src/inventory.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/inventory.cc b/src/inventory.cc index 2d1b832..eaac208 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -2694,7 +2694,9 @@ void inventoryOpenUseItemOn(Object* a1) inventoryWindowOpenContextMenu(keyCode, INVENTORY_WINDOW_TYPE_USE_ITEM_ON); } else { int inventoryItemIndex = _pud->length - (_stack_offset[_curr_stack] + keyCode - 1000 + 1); - if (inventoryItemIndex < _pud->length) { + // SFALL: Fix crash when clicking on empty space in the inventory list + // opened by "Use Inventory Item On" (backpack) action icon + if (inventoryItemIndex < _pud->length && inventoryItemIndex >= 0) { InventoryItem* inventoryItem = &(_pud->items[inventoryItemIndex]); if (isInCombat()) { if (gDude->data.critter.combat.ap >= 2) { From 6d1273d325c3fc4c1832961cbf9cf6fd4b5bddc1 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 11 Apr 2023 09:01:14 +0300 Subject: [PATCH 46/50] Improve button functions readability --- src/window_manager.cc | 300 +++++++++++++++++----------------- src/window_manager.h | 45 ++--- src/window_manager_private.cc | 123 +++++++------- src/window_manager_private.h | 22 +-- 4 files changed, 243 insertions(+), 247 deletions(-) diff --git a/src/window_manager.cc b/src/window_manager.cc index 101186a..e0f170a 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -25,8 +25,8 @@ namespace fallout { #define MAX_WINDOW_COUNT (50) -// The maximum number of radio groups. -#define RADIO_GROUP_LIST_CAPACITY (64) +// The maximum number of button groups. +#define BUTTON_GROUP_LIST_CAPACITY (64) static void windowFree(int win); static void _win_buffering(bool a1); @@ -39,13 +39,13 @@ static int paletteOpenFileImpl(const char* path, int flags); static int paletteReadFileImpl(int fd, void* buf, size_t count); static int paletteCloseFileImpl(int fd); static Button* buttonCreateInternal(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, int flags, unsigned char* up, unsigned char* dn, unsigned char* hover); -static int _GNW_check_buttons(Window* window, int* out_a2); +static int _GNW_check_buttons(Window* window, int* keyCodePtr); static bool _button_under_mouse(Button* button, Rect* rect); static void buttonFree(Button* ptr); static int button_new_id(); -static int _win_group_check_buttons(int a1, int* a2, int a3, void (*a4)(int)); +static int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func); static int _button_check_group(Button* button); -static void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6); +static void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound); static void _GNW_button_refresh(Window* window, Rect* rect); // 0x50FA30 @@ -112,7 +112,7 @@ static int _doing_refresh_all; static void* _GNW_texture; // 0x6ADF40 -static RadioGroup gRadioGroups[RADIO_GROUP_LIST_CAPACITY]; +static ButtonGroup gButtonGroups[BUTTON_GROUP_LIST_CAPACITY]; // 0x4D5C30 int windowManagerInit(VideoSystemInitProc* videoSystemInitProc, VideoSystemExitProc* videoSystemExitProc, int a3) @@ -504,15 +504,12 @@ void windowDrawBorder(int win) } // 0x4D684C -void windowDrawText(int win, const char* str, int a3, int x, int y, int a6) +void windowDrawText(int win, const char* str, int width, int x, int y, int color) { - int v7; - int v14; unsigned char* buf; - int v27; + int textColor; Window* window = windowGetWindow(win); - v7 = a3; if (!gWindowSystemInitialized) { return; @@ -522,52 +519,51 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6) return; } - if (a3 == 0) { - if (a6 & 0x040000) { - v7 = fontGetMonospacedStringWidth(str); + if (width == 0) { + if (color & 0x040000) { + width = fontGetMonospacedStringWidth(str); } else { - v7 = fontGetStringWidth(str); + width = fontGetStringWidth(str); } } - if (v7 + x > window->width) { - if (!(a6 & 0x04000000)) { + if (width + x > window->width) { + if (!(color & 0x04000000)) { return; } - v7 = window->width - x; + width = window->width - x; } buf = window->buffer + x + y * window->width; - v14 = fontGetLineHeight(); - if (v14 + y > window->height) { + if (fontGetLineHeight() + y > window->height) { return; } - if (!(a6 & 0x02000000)) { + if (!(color & 0x02000000)) { if (window->color == 256 && _GNW_texture != NULL) { - _buf_texture(buf, v7, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y); + _buf_texture(buf, width, fontGetLineHeight(), window->width, _GNW_texture, window->tx + x, window->ty + y); } else { - bufferFill(buf, v7, fontGetLineHeight(), window->width, window->color); + bufferFill(buf, width, fontGetLineHeight(), window->width, window->color); } } - if (a6 & 0xFF00) { - int t = (a6 & 0xFF00) >> 8; - v27 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[t]]; + if (color & 0xFF00) { + int t = (color & 0xFF00) >> 8; + textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[t]]; } else { - v27 = a6; + textColor = color; } - fontDrawText(buf, str, v7, window->width, v27); + fontDrawText(buf, str, width, window->width, textColor); - if (a6 & 0x01000000) { + if (color & 0x01000000) { // TODO: Check. Rect rect; rect.left = window->rect.left + x; rect.top = window->rect.top + y; - rect.right = rect.left + v7; + rect.right = rect.left + width; rect.bottom = rect.top + fontGetLineHeight(); _GNW_win_refresh(window, &rect, NULL); } @@ -628,7 +624,7 @@ void windowDrawRect(int win, int left, int top, int right, int bottom, int color } // 0x4D6CC8 -void windowFill(int win, int x, int y, int width, int height, int a6) +void windowFill(int win, int x, int y, int width, int height, int color) { Window* window = windowGetWindow(win); @@ -640,19 +636,19 @@ void windowFill(int win, int x, int y, int width, int height, int a6) return; } - if (a6 == 256) { + if (color == 256) { if (_GNW_texture != NULL) { _buf_texture(window->buffer + window->width * y + x, width, height, window->width, _GNW_texture, x + window->tx, y + window->ty); } else { - a6 = _colorTable[_GNW_wcolor[0]] & 0xFF; + color = _colorTable[_GNW_wcolor[0]] & 0xFF; } - } else if ((a6 & 0xFF00) != 0) { - int v1 = (a6 & 0xFF00) >> 8; - a6 = (a6 & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]]; + } else if ((color & 0xFF00) != 0) { + int v1 = (color & 0xFF00) >> 8; + color = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[v1]]; } - if (a6 < 256) { - bufferFill(window->buffer + window->width * y + x, width, height, window->width, a6); + if (color < 256) { + bufferFill(window->buffer + window->width * y + x, width, height, window->width, color); } } @@ -1367,12 +1363,12 @@ int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEve return -1; } - Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_0x010000, up, dn, hover); + Button* button = buttonCreateInternal(win, x, y, width, height, mouseEnterEventCode, mouseExitEventCode, mouseDownEventCode, mouseUpEventCode, flags | BUTTON_FLAG_GRAPHIC, up, dn, hover); if (button == NULL) { return -1; } - _button_draw(button, window, button->normalImage, 0, NULL, 0); + _button_draw(button, window, button->normalImage, false, NULL, false); return button->id; } @@ -1469,7 +1465,7 @@ int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, in return -1; } - _button_draw(button, window, button->normalImage, 0, NULL, 0); + _button_draw(button, window, button->normalImage, false, NULL, false); return button->id; } @@ -1494,7 +1490,7 @@ int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down } // 0x4D86A8 -int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5) +int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw) { if (!gWindowSystemInitialized) { return -1; @@ -1510,7 +1506,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, return -1; } - if (!(button->flags & BUTTON_FLAG_0x010000)) { + if (!(button->flags & BUTTON_FLAG_GRAPHIC)) { return -1; } @@ -1527,7 +1523,7 @@ int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, button->pressedImage = down; button->hoverImage = hover; - _button_draw(button, window, button->currentImage, a5, NULL, 0); + _button_draw(button, window, button->currentImage, draw, NULL, false); return 0; } @@ -1590,7 +1586,7 @@ int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int right // These callbacks can be triggered several times during tracking if mouse leaves button's rectangle without releasing mouse buttons. // // 0x4D87F8 -int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed) +int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc) { if (!gWindowSystemInitialized) { return -1; @@ -1601,8 +1597,8 @@ int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnp return -1; } - button->onPressed = onPressed; - button->onUnpressed = onUnpressed; + button->pressSoundFunc = pressSoundFunc; + button->releaseSoundFunc = releaseSoundFunc; return 0; } @@ -1676,9 +1672,9 @@ Button* buttonCreateInternal(int win, int x, int y, int width, int height, int m button->leftMouseUpProc = NULL; button->rightMouseDownProc = NULL; button->rightMouseUpProc = NULL; - button->onPressed = NULL; - button->onUnpressed = NULL; - button->radioGroup = NULL; + button->pressSoundFunc = NULL; + button->releaseSoundFunc = NULL; + button->buttonGroup = NULL; button->prev = NULL; button->next = window->buttonListHead; @@ -1702,7 +1698,7 @@ bool _win_button_down(int btn) return false; } - if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_0x020000) != 0) { + if ((button->flags & BUTTON_FLAG_0x01) != 0 && (button->flags & BUTTON_FLAG_CHECKED) != 0) { return true; } @@ -1751,10 +1747,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) *keyCodePtr = prevHoveredButton->mouseExitEventCode; } - if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) { - _button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, 1, NULL, 1); + if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) { + _button_draw(prevHoveredButton, window, prevHoveredButton->pressedImage, true, NULL, true); } else { - _button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, 1, NULL, 1); + _button_draw(prevHoveredButton, window, prevHoveredButton->normalImage, true, NULL, true); } window->hoveredButton = NULL; @@ -1778,10 +1774,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) *keyCodePtr = prevClickedButton->mouseEnterEventCode; } - if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_0x020000)) { - _button_draw(prevClickedButton, window, prevClickedButton->pressedImage, 1, NULL, 1); + if ((prevClickedButton->flags & BUTTON_FLAG_0x01) && (prevClickedButton->flags & BUTTON_FLAG_CHECKED)) { + _button_draw(prevClickedButton, window, prevClickedButton->pressedImage, true, NULL, true); } else { - _button_draw(prevClickedButton, window, prevClickedButton->normalImage, 1, NULL, 1); + _button_draw(prevClickedButton, window, prevClickedButton->normalImage, true, NULL, true); } window->hoveredButton = prevClickedButton; @@ -1812,10 +1808,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) *keyCodePtr = v28->mouseExitEventCode; } - if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_0x020000)) { - _button_draw(v28, v26, v28->pressedImage, 1, NULL, 1); + if ((v28->flags & BUTTON_FLAG_0x01) && (v28->flags & BUTTON_FLAG_CHECKED)) { + _button_draw(v28, v26, v28->pressedImage, true, NULL, true); } else { - _button_draw(v28, v26, v28->normalImage, 1, NULL, 1); + _button_draw(v28, v26, v28->normalImage, true, NULL, true); } v26->clickedButton = NULL; @@ -1857,10 +1853,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) if ((button->flags & BUTTON_FLAG_0x01) != 0) { if ((button->flags & BUTTON_FLAG_0x02) != 0) { - if ((button->flags & BUTTON_FLAG_0x020000) != 0) { + if ((button->flags & BUTTON_FLAG_CHECKED) != 0) { if (!(button->flags & BUTTON_FLAG_0x04)) { - if (button->radioGroup != NULL) { - button->radioGroup->field_4--; + if (button->buttonGroup != NULL) { + button->buttonGroup->currChecked--; } if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_DOWN) != 0) { @@ -1871,7 +1867,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) cb = button->rightMouseUpProc; } - button->flags &= ~BUTTON_FLAG_0x020000; + button->flags &= ~BUTTON_FLAG_CHECKED; } } else { if (_button_check_group(button) == -1) { @@ -1887,7 +1883,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) cb = button->rightMouseDownProc; } - button->flags |= BUTTON_FLAG_0x020000; + button->flags |= BUTTON_FLAG_CHECKED; } } } else { @@ -1905,7 +1901,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) } } - _button_draw(button, window, button->pressedImage, 1, NULL, 1); + _button_draw(button, window, button->pressedImage, true, NULL, true); break; } @@ -1916,10 +1912,10 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) if (v49->flags & BUTTON_FLAG_0x01) { if (!(v49->flags & BUTTON_FLAG_0x02)) { - if (v49->flags & BUTTON_FLAG_0x020000) { + if (v49->flags & BUTTON_FLAG_CHECKED) { if (!(v49->flags & BUTTON_FLAG_0x04)) { - if (v49->radioGroup != NULL) { - v49->radioGroup->field_4--; + if (v49->buttonGroup != NULL) { + v49->buttonGroup->currChecked--; } if ((mouseEvent & MOUSE_EVENT_LEFT_BUTTON_UP) != 0) { @@ -1930,12 +1926,12 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) cb = button->rightMouseUpProc; } - button->flags &= ~BUTTON_FLAG_0x020000; + button->flags &= ~BUTTON_FLAG_CHECKED; } } else { if (_button_check_group(v49) == -1) { button = NULL; - _button_draw(v49, window, v49->normalImage, 1, NULL, 1); + _button_draw(v49, window, v49->normalImage, true, NULL, true); break; } @@ -1947,13 +1943,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) cb = v49->rightMouseDownProc; } - v49->flags |= BUTTON_FLAG_0x020000; + v49->flags |= BUTTON_FLAG_CHECKED; } } } else { - if (v49->flags & BUTTON_FLAG_0x020000) { - if (v49->radioGroup != NULL) { - v49->radioGroup->field_4--; + if (v49->flags & BUTTON_FLAG_CHECKED) { + if (v49->buttonGroup != NULL) { + v49->buttonGroup->currChecked--; } } @@ -1967,9 +1963,9 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) } if (button->hoverImage != NULL) { - _button_draw(button, window, button->hoverImage, 1, NULL, 1); + _button_draw(button, window, button->hoverImage, true, NULL, true); } else { - _button_draw(button, window, button->normalImage, 1, NULL, 1); + _button_draw(button, window, button->normalImage, true, NULL, true); } break; } @@ -1982,7 +1978,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) cb = button->mouseEnterProc; } - _button_draw(button, window, button->hoverImage, 1, NULL, 1); + _button_draw(button, window, button->hoverImage, true, NULL, true); } break; } @@ -1995,7 +1991,7 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_DOWN) != 0 && (mouseEvent & MOUSE_EVENT_ANY_BUTTON_REPEAT) == 0) { _win_drag(window->id); - _button_draw(button, window, button->normalImage, 1, NULL, 1); + _button_draw(button, window, button->normalImage, true, NULL, true); } } else if ((window->flags & WINDOW_FLAG_0x80) != 0) { v25 |= mouseEvent << 8; @@ -2023,13 +2019,13 @@ int _GNW_check_buttons(Window* window, int* keyCodePtr) *keyCodePtr = prevHoveredButton->mouseExitEventCode; unsigned char* data; - if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_0x020000)) { + if ((prevHoveredButton->flags & BUTTON_FLAG_0x01) && (prevHoveredButton->flags & BUTTON_FLAG_CHECKED)) { data = prevHoveredButton->pressedImage; } else { data = prevHoveredButton->normalImage; } - _button_draw(prevHoveredButton, window, data, 1, NULL, 1); + _button_draw(prevHoveredButton, window, data, true, NULL, true); window->hoveredButton = NULL; } @@ -2142,7 +2138,7 @@ int buttonDestroy(int btn) // 0x4D9374 void buttonFree(Button* button) { - if ((button->flags & BUTTON_FLAG_0x010000) == 0) { + if ((button->flags & BUTTON_FLAG_GRAPHIC) == 0) { if (button->normalImage != NULL) { internal_free(button->normalImage); } @@ -2168,15 +2164,15 @@ void buttonFree(Button* button) } } - RadioGroup* radioGroup = button->radioGroup; - if (radioGroup != NULL) { - for (int index = 0; index < radioGroup->buttonsLength; index++) { - if (button == radioGroup->buttons[index]) { - for (; index < radioGroup->buttonsLength - 1; index++) { - radioGroup->buttons[index] = radioGroup->buttons[index + 1]; + ButtonGroup* buttonGroup = button->buttonGroup; + if (buttonGroup != NULL) { + for (int index = 0; index < buttonGroup->buttonsLength; index++) { + if (button == buttonGroup->buttons[index]) { + for (; index < buttonGroup->buttonsLength - 1; index++) { + buttonGroup->buttons[index] = buttonGroup->buttons[index + 1]; } - radioGroup->buttonsLength--; + buttonGroup->buttonsLength--; break; } @@ -2216,7 +2212,7 @@ int buttonEnable(int btn) if ((button->flags & BUTTON_FLAG_DISABLED) != 0) { button->flags &= ~BUTTON_FLAG_DISABLED; - _button_draw(button, window, button->currentImage, 1, NULL, 0); + _button_draw(button, window, button->currentImage, true, NULL, false); } return 0; @@ -2238,7 +2234,7 @@ int buttonDisable(int btn) if ((button->flags & BUTTON_FLAG_DISABLED) == 0) { button->flags |= BUTTON_FLAG_DISABLED; - _button_draw(button, window, button->currentImage, 1, NULL, 0); + _button_draw(button, window, button->currentImage, true, NULL, false); if (button == window->hoveredButton) { if (window->hoveredButton->mouseExitEventCode != -1) { @@ -2252,7 +2248,7 @@ int buttonDisable(int btn) } // 0x4D9554 -int _win_set_button_rest_state(int btn, bool a2, int a3) +int _win_set_button_rest_state(int btn, bool checked, int flags) { if (!gWindowSystemInitialized) { return -1; @@ -2267,30 +2263,30 @@ int _win_set_button_rest_state(int btn, bool a2, int a3) if ((button->flags & BUTTON_FLAG_0x01) != 0) { int keyCode = -1; - if ((button->flags & BUTTON_FLAG_0x020000) != 0) { - if (!a2) { - button->flags &= ~BUTTON_FLAG_0x020000; + if ((button->flags & BUTTON_FLAG_CHECKED) != 0) { + if (!checked) { + button->flags &= ~BUTTON_FLAG_CHECKED; - if ((a3 & 0x02) == 0) { - _button_draw(button, window, button->normalImage, 1, NULL, 0); + if ((flags & 0x02) == 0) { + _button_draw(button, window, button->normalImage, true, NULL, false); } - if (button->radioGroup != NULL) { - button->radioGroup->field_4--; + if (button->buttonGroup != NULL) { + button->buttonGroup->currChecked--; } keyCode = button->leftMouseUpEventCode; } } else { - if (a2) { - button->flags |= BUTTON_FLAG_0x020000; + if (checked) { + button->flags |= BUTTON_FLAG_CHECKED; - if ((a3 & 0x02) == 0) { - _button_draw(button, window, button->pressedImage, 1, NULL, 0); + if ((flags & 0x02) == 0) { + _button_draw(button, window, button->pressedImage, true, NULL, false); } - if (button->radioGroup != NULL) { - button->radioGroup->field_4++; + if (button->buttonGroup != NULL) { + button->buttonGroup->currChecked++; } keyCode = button->lefMouseDownEventCode; @@ -2298,7 +2294,7 @@ int _win_set_button_rest_state(int btn, bool a2, int a3) } if (keyCode != -1) { - if ((a3 & 0x01) != 0) { + if ((flags & 0x01) != 0) { enqueueInputEvent(keyCode); } } @@ -2308,20 +2304,20 @@ int _win_set_button_rest_state(int btn, bool a2, int a3) } // 0x4D962C -int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int)) +int _win_group_check_buttons(int buttonCount, int* btns, int maxChecked, RadioButtonCallback* func) { if (!gWindowSystemInitialized) { return -1; } - if (buttonCount >= RADIO_GROUP_BUTTON_LIST_CAPACITY) { + if (buttonCount >= BUTTON_GROUP_BUTTON_LIST_CAPACITY) { return -1; } - for (int groupIndex = 0; groupIndex < RADIO_GROUP_LIST_CAPACITY; groupIndex++) { - RadioGroup* radioGroup = &(gRadioGroups[groupIndex]); - if (radioGroup->buttonsLength == 0) { - radioGroup->field_4 = 0; + for (int groupIndex = 0; groupIndex < BUTTON_GROUP_LIST_CAPACITY; groupIndex++) { + ButtonGroup* buttonGroup = &(gButtonGroups[groupIndex]); + if (buttonGroup->buttonsLength == 0) { + buttonGroup->currChecked = 0; for (int buttonIndex = 0; buttonIndex < buttonCount; buttonIndex++) { Button* button = buttonGetButton(btns[buttonIndex], NULL); @@ -2329,18 +2325,18 @@ int _win_group_check_buttons(int buttonCount, int* btns, int a3, void (*a4)(int) return -1; } - radioGroup->buttons[buttonIndex] = button; + buttonGroup->buttons[buttonIndex] = button; - button->radioGroup = radioGroup; + button->buttonGroup = buttonGroup; - if ((button->flags & BUTTON_FLAG_0x020000) != 0) { - radioGroup->field_4++; + if ((button->flags & BUTTON_FLAG_CHECKED) != 0) { + buttonGroup->currChecked++; } } - radioGroup->buttonsLength = buttonCount; - radioGroup->field_0 = a3; - radioGroup->field_8 = a4; + buttonGroup->buttonsLength = buttonCount; + buttonGroup->maxChecked = maxChecked; + buttonGroup->func = func; return 0; } } @@ -2360,11 +2356,11 @@ int _win_group_radio_buttons(int count, int* btns) } Button* button = buttonGetButton(btns[0], NULL); - RadioGroup* radioGroup = button->radioGroup; + ButtonGroup* buttonGroup = button->buttonGroup; - for (int index = 0; index < radioGroup->buttonsLength; index++) { - Button* v1 = radioGroup->buttons[index]; - v1->flags |= BUTTON_FLAG_0x040000; + for (int index = 0; index < buttonGroup->buttonsLength; index++) { + Button* v1 = buttonGroup->buttons[index]; + v1->flags |= BUTTON_FLAG_RADIO; } return 0; @@ -2373,20 +2369,20 @@ int _win_group_radio_buttons(int count, int* btns) // 0x4D9744 int _button_check_group(Button* button) { - if (button->radioGroup == NULL) { + if (button->buttonGroup == NULL) { return 0; } - if ((button->flags & BUTTON_FLAG_0x040000) != 0) { - if (button->radioGroup->field_4 > 0) { - for (int index = 0; index < button->radioGroup->buttonsLength; index++) { - Button* v1 = button->radioGroup->buttons[index]; - if ((v1->flags & BUTTON_FLAG_0x020000) != 0) { - v1->flags &= ~BUTTON_FLAG_0x020000; + if ((button->flags & BUTTON_FLAG_RADIO) != 0) { + if (button->buttonGroup->currChecked > 0) { + for (int index = 0; index < button->buttonGroup->buttonsLength; index++) { + Button* v1 = button->buttonGroup->buttons[index]; + if ((v1->flags & BUTTON_FLAG_CHECKED) != 0) { + v1->flags &= ~BUTTON_FLAG_CHECKED; Window* window; buttonGetButton(v1->id, &window); - _button_draw(v1, window, v1->normalImage, 1, NULL, 1); + _button_draw(v1, window, v1->normalImage, true, NULL, true); if (v1->leftMouseUpProc != NULL) { v1->leftMouseUpProc(v1->id, v1->leftMouseUpEventCode); @@ -2395,30 +2391,30 @@ int _button_check_group(Button* button) } } - if ((button->flags & BUTTON_FLAG_0x020000) == 0) { - button->radioGroup->field_4++; + if ((button->flags & BUTTON_FLAG_CHECKED) == 0) { + button->buttonGroup->currChecked++; } return 0; } - if (button->radioGroup->field_4 < button->radioGroup->field_0) { - if ((button->flags & BUTTON_FLAG_0x020000) == 0) { - button->radioGroup->field_4++; + if (button->buttonGroup->currChecked < button->buttonGroup->maxChecked) { + if ((button->flags & BUTTON_FLAG_CHECKED) == 0) { + button->buttonGroup->currChecked++; } return 0; } - if (button->radioGroup->field_8 != NULL) { - button->radioGroup->field_8(button->id); + if (button->buttonGroup->func != NULL) { + button->buttonGroup->func(button->id); } return -1; } // 0x4D9808 -void _button_draw(Button* button, Window* window, unsigned char* data, int a4, Rect* a5, int a6) +void _button_draw(Button* button, Window* window, unsigned char* data, bool draw, Rect* bound, bool sound) { unsigned char* previousImage = NULL; if (data != NULL) { @@ -2427,8 +2423,8 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R rectOffset(&v2, window->rect.left, window->rect.top); Rect v3; - if (a5 != NULL) { - if (rectIntersection(&v2, a5, &v2) == -1) { + if (bound != NULL) { + if (rectIntersection(&v2, bound, &v2) == -1) { return; } @@ -2438,7 +2434,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R rectCopy(&v3, &(button->rect)); } - if (data == button->normalImage && (button->flags & BUTTON_FLAG_0x020000)) { + if (data == button->normalImage && (button->flags & BUTTON_FLAG_CHECKED)) { data = button->pressedImage; } @@ -2461,7 +2457,7 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R } if (data) { - if (a4 == 0) { + if (!draw) { int width = button->rect.right - button->rect.left + 1; if ((button->flags & BUTTON_FLAG_TRANSPARENT) != 0) { blitBufferToBufferTrans( @@ -2485,18 +2481,18 @@ void _button_draw(Button* button, Window* window, unsigned char* data, int a4, R previousImage = button->currentImage; button->currentImage = data; - if (a4 != 0) { + if (draw) { _GNW_win_refresh(window, &v2, 0); } } } - if (a6) { + if (sound) { if (previousImage != data) { - if (data == button->pressedImage && button->onPressed != NULL) { - button->onPressed(button->id, button->lefMouseDownEventCode); - } else if (data == button->normalImage && button->onUnpressed != NULL) { - button->onUnpressed(button->id, button->leftMouseUpEventCode); + if (data == button->pressedImage && button->pressSoundFunc != NULL) { + button->pressSoundFunc(button->id, button->lefMouseDownEventCode); + } else if (data == button->normalImage && button->releaseSoundFunc != NULL) { + button->releaseSoundFunc(button->id, button->leftMouseUpEventCode); } } } @@ -2513,7 +2509,7 @@ void _GNW_button_refresh(Window* window, Rect* rect) } while (button != NULL) { - _button_draw(button, window, button->currentImage, 0, rect, 0); + _button_draw(button, window, button->currentImage, false, rect, false); button = button->prev; } } @@ -2531,7 +2527,7 @@ int _win_button_press_and_release(int btn) return -1; } - _button_draw(button, window, button->pressedImage, 1, NULL, 1); + _button_draw(button, window, button->pressedImage, true, NULL, true); if (button->leftMouseDownProc != NULL) { button->leftMouseDownProc(btn, button->lefMouseDownEventCode); @@ -2545,7 +2541,7 @@ int _win_button_press_and_release(int btn) } } - _button_draw(button, window, button->normalImage, 1, NULL, 1); + _button_draw(button, window, button->normalImage, true, NULL, true); if (button->leftMouseUpProc != NULL) { button->leftMouseUpProc(btn, button->leftMouseUpEventCode); diff --git a/src/window_manager.h b/src/window_manager.h index 2275b4d..265aa7e 100644 --- a/src/window_manager.h +++ b/src/window_manager.h @@ -8,7 +8,7 @@ namespace fallout { // The maximum number of buttons in one radio group. -#define RADIO_GROUP_BUTTON_LIST_CAPACITY (64) +#define BUTTON_GROUP_BUTTON_LIST_CAPACITY (64) typedef enum WindowManagerErr { WINDOW_MANAGER_OK = 0, @@ -55,9 +55,9 @@ typedef enum ButtonFlags { BUTTON_FLAG_0x10 = 0x10, BUTTON_FLAG_TRANSPARENT = 0x20, BUTTON_FLAG_0x40 = 0x40, - BUTTON_FLAG_0x010000 = 0x010000, - BUTTON_FLAG_0x020000 = 0x020000, - BUTTON_FLAG_0x040000 = 0x040000, + BUTTON_FLAG_GRAPHIC = 0x010000, + BUTTON_FLAG_CHECKED = 0x020000, + BUTTON_FLAG_RADIO = 0x040000, BUTTON_FLAG_RIGHT_MOUSE_BUTTON_CONFIGURED = 0x080000, } ButtonFlags; @@ -66,8 +66,8 @@ typedef struct MenuPulldown { int keyCode; int itemsLength; char** items; - int field_1C; - int field_20; + int foregroundColor; + int backgroundColor; } MenuPulldown; typedef struct MenuBar { @@ -75,14 +75,14 @@ typedef struct MenuBar { Rect rect; int pulldownsLength; MenuPulldown pulldowns[15]; - int borderColor; + int foregroundColor; int backgroundColor; } MenuBar; typedef void WindowBlitProc(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch); typedef struct Button Button; -typedef struct RadioGroup RadioGroup; +typedef struct ButtonGroup ButtonGroup; typedef struct Window { int id; @@ -102,6 +102,7 @@ typedef struct Window { } Window; typedef void ButtonCallback(int btn, int keyCode); +typedef void RadioButtonCallback(int btn); typedef struct Button { int id; @@ -127,20 +128,20 @@ typedef struct Button { ButtonCallback* leftMouseUpProc; ButtonCallback* rightMouseDownProc; ButtonCallback* rightMouseUpProc; - ButtonCallback* onPressed; - ButtonCallback* onUnpressed; - RadioGroup* radioGroup; + ButtonCallback* pressSoundFunc; + ButtonCallback* releaseSoundFunc; + ButtonGroup* buttonGroup; Button* prev; Button* next; } Button; -typedef struct RadioGroup { - int field_0; - int field_4; - void (*field_8)(int); +typedef struct ButtonGroup { + int maxChecked; + int currChecked; + RadioButtonCallback* func; int buttonsLength; - Button* buttons[RADIO_GROUP_BUTTON_LIST_CAPACITY]; -} RadioGroup; + Button* buttons[BUTTON_GROUP_BUTTON_LIST_CAPACITY]; +} ButtonGroup; typedef int(VideoSystemInitProc)(); typedef void(VideoSystemExitProc)(); @@ -157,7 +158,7 @@ void windowDrawText(int win, const char* str, int a3, int x, int y, int a6); void _win_text(int win, char** fileNameList, int fileNameListLength, int maxWidth, int x, int y, int flags); void windowDrawLine(int win, int left, int top, int right, int bottom, int color); void windowDrawRect(int win, int left, int top, int right, int bottom, int color); -void windowFill(int win, int x, int y, int width, int height, int a6); +void windowFill(int win, int x, int y, int width, int height, int color); void windowShow(int win); void windowHide(int win); void windowRefresh(int win); @@ -178,10 +179,10 @@ bool showMesageBox(const char* str); int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, unsigned char* up, unsigned char* dn, unsigned char* hover, int flags); int _win_register_text_button(int win, int x, int y, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, const char* title, int flags); int _win_register_button_disable(int btn, unsigned char* up, unsigned char* down, unsigned char* hover); -int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, int a5); +int _win_register_button_image(int btn, unsigned char* up, unsigned char* down, unsigned char* hover, bool draw); int buttonSetMouseCallbacks(int btn, ButtonCallback* mouseEnterProc, ButtonCallback* mouseExitProc, ButtonCallback* mouseDownProc, ButtonCallback* mouseUpProc); int buttonSetRightMouseCallbacks(int btn, int rightMouseDownEventCode, int rightMouseUpEventCode, ButtonCallback* rightMouseDownProc, ButtonCallback* rightMouseUpProc); -int buttonSetCallbacks(int btn, ButtonCallback* onPressed, ButtonCallback* onUnpressed); +int buttonSetCallbacks(int btn, ButtonCallback* pressSoundFunc, ButtonCallback* releaseSoundFunc); int buttonSetMask(int btn, unsigned char* mask); bool _win_button_down(int btn); int buttonGetWindowId(int btn); @@ -189,8 +190,8 @@ int _win_last_button_winID(); int buttonDestroy(int btn); int buttonEnable(int btn); int buttonDisable(int btn); -int _win_set_button_rest_state(int btn, bool a2, int a3); -int _win_group_radio_buttons(int a1, int* a2); +int _win_set_button_rest_state(int btn, bool checked, int flags); +int _win_group_radio_buttons(int buttonCount, int* btns); int _win_button_press_and_release(int btn); } // namespace fallout diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index 88fe9d2..af94de7 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -79,13 +79,13 @@ static int _currx; char gProgramWindowTitle[256]; // 0x4DA6C0 -int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7) +int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color) { - return _win_list_select_at(title, fileList, fileListLength, callback, x, y, a7, 0); + return _win_list_select_at(title, fileList, fileListLength, callback, x, y, color, 0); } // 0x4DA70C -int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8) +int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start) { if (!gWindowSystemInitialized) { return -1; @@ -170,8 +170,8 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe windowWidth, _colorTable[_GNW_wcolor[0]]); - int scrollOffset = a8; - if (a8 < 0 || a8 >= itemsLength) { + int scrollOffset = start; + if (start < 0 || start >= itemsLength) { scrollOffset = 0; } @@ -189,14 +189,13 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe selectedItemIndex = 0; } - char** itemsTO = items + a8; _win_text(win, - items + a8, + items + start, itemsLength < listViewCapacity ? itemsLength : listViewCapacity, listViewWidth, listViewX, listViewY, - a7 | 0x2000000); + color | 0x2000000); _lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(), listViewWidth, @@ -452,7 +451,7 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe listViewWidth, listViewX, listViewY, - a7 | 0x2000000); + color | 0x2000000); _lighten_buf(listViewBuffer + windowWidth * selectedItemIndex * fontGetLineHeight(), listViewWidth, @@ -500,19 +499,19 @@ int _win_list_select_at(const char* title, char** items, int itemsLength, ListSe windowWidth, _colorTable[_GNW_wcolor[0]]); - int color; - if ((a7 & 0xFF00) != 0) { - int colorIndex = (a7 & 0xFF) - 1; - color = (a7 & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; + int textColor; + if ((color & 0xFF00) != 0) { + int colorIndex = (color & 0xFF) - 1; + textColor = (color & ~0xFFFF) | _colorTable[_GNW_wcolor[colorIndex]]; } else { - color = a7; + textColor = color; } fontDrawText(listViewBuffer + windowWidth * previousSelectedItemIndex * fontGetLineHeight(), items[scrollOffset + previousSelectedItemIndex], windowWidth, windowWidth, - color); + textColor); _GNW_win_refresh(window, &itemRect, NULL); } @@ -619,7 +618,7 @@ int _win_get_str(char* dest, int length, const char* title, int x, int y) } // 0x4DBA98 -int _win_msg(const char* string, int x, int y, int flags) +int _win_msg(const char* string, int x, int y, int color) { if (!gWindowSystemInitialized) { return -1; @@ -644,16 +643,16 @@ int _win_msg(const char* string, int x, int y, int flags) Window* window = windowGetWindow(win); unsigned char* windowBuffer = window->buffer; - int color; - if ((flags & 0xFF00) != 0) { - int index = (flags & 0xFF) - 1; - color = _colorTable[_GNW_wcolor[index]]; - color |= flags & ~0xFFFF; + int textColor; + if ((color & 0xFF00) != 0) { + int index = (color & 0xFF) - 1; + textColor = _colorTable[_GNW_wcolor[index]]; + textColor |= color & ~0xFFFF; } else { - color = flags; + textColor = color; } - fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, color); + fontDrawText(windowBuffer + windowWidth * 8 + 16, string, windowWidth, windowWidth, textColor); _win_register_text_button(win, windowWidth / 2 - 32, @@ -679,23 +678,23 @@ int _win_msg(const char* string, int x, int y, int flags) } // 0x4DBBC4 -int _win_pull_down(char** items, int itemsLength, int x, int y, int a5) +int _win_pull_down(char** items, int itemsLength, int x, int y, int color) { if (!gWindowSystemInitialized) { return -1; } Rect rect; - int win = _create_pull_down(items, itemsLength, x, y, a5, _colorTable[_GNW_wcolor[0]], &rect); + int win = _create_pull_down(items, itemsLength, x, y, color, _colorTable[_GNW_wcolor[0]], &rect); if (win == -1) { return -1; } - return sub_4DBD04(win, &rect, items, itemsLength, a5, _colorTable[_GNW_wcolor[0]], NULL, -1); + return process_pull_down(win, &rect, items, itemsLength, color, _colorTable[_GNW_wcolor[0]], NULL, -1); } // 0x4DBC34 -int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect) +int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect) { int windowHeight = stringListLength * fontGetLineHeight() + 16; int windowWidth = _win_width_needed(stringList, stringListLength) + 4; @@ -703,14 +702,14 @@ int _create_pull_down(char** stringList, int stringListLength, int x, int y, int return -1; } - int win = windowCreate(x, y, windowWidth, windowHeight, a6, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); + int win = windowCreate(x, y, windowWidth, windowHeight, backgroundColor, WINDOW_MODAL | WINDOW_MOVE_ON_TOP); if (win == -1) { return -1; } - _win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, a5); + _win_text(win, stringList, stringListLength, windowWidth - 4, 2, 8, foregroundColor); windowDrawRect(win, 0, 0, windowWidth - 1, windowHeight - 1, _colorTable[0]); - windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, a5); + windowDrawRect(win, 1, 1, windowWidth - 2, windowHeight - 2, foregroundColor); windowRefresh(win); windowGetRect(win, rect); @@ -841,7 +840,7 @@ void _win_debug_delete(int btn, int keyCode) } // 0x4DC674 -int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor) +int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor) { Window* window = windowGetWindow(win); @@ -878,17 +877,17 @@ int _win_register_menu_bar(int win, int x, int y, int width, int height, int bor menuBar->rect.right = right - 1; menuBar->rect.bottom = bottom - 1; menuBar->pulldownsLength = 0; - menuBar->borderColor = borderColor; + menuBar->foregroundColor = foregroundColor; menuBar->backgroundColor = backgroundColor; windowFill(win, x, y, width, height, backgroundColor); - windowDrawRect(win, x, y, right - 1, bottom - 1, borderColor); + windowDrawRect(win, x, y, right - 1, bottom - 1, foregroundColor); return 0; } // 0x4DC768 -int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8) +int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor) { Window* window = windowGetWindow(win); @@ -928,7 +927,7 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it return -1; } - windowDrawText(win, title, 0, titleX, titleY, window->menuBar->borderColor | 0x2000000); + windowDrawText(win, title, 0, titleX, titleY, window->menuBar->foregroundColor | 0x2000000); MenuPulldown* pulldown = &(window->menuBar->pulldowns[window->menuBar->pulldownsLength]); pulldown->rect.left = titleX; @@ -938,8 +937,8 @@ int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int it pulldown->keyCode = keyCode; pulldown->itemsLength = itemsLength; pulldown->items = items; - pulldown->field_1C = a7; - pulldown->field_20 = a8; + pulldown->foregroundColor = foregroundColor; + pulldown->backgroundColor = backgroundColor; window->menuBar->pulldownsLength++; @@ -1120,7 +1119,7 @@ int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textCol } // 0x4DBD04 -int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex) +int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int foregroundColor, int backgroundColor, MenuBar* menuBar, int pulldownIndex) { // TODO: Incomplete. return -1; @@ -1143,15 +1142,15 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex) pulldown->itemsLength, pulldown->rect.left, menuBar->rect.bottom + 1, - pulldown->field_1C, - pulldown->field_20, + pulldown->foregroundColor, + pulldown->backgroundColor, &rect); if (win == -1) { _curr_menu = NULL; return -1; } - keyCode = sub_4DBD04(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->field_1C, pulldown->field_20, menuBar, pulldownIndex); + keyCode = process_pull_down(win, &rect, pulldown->items, pulldown->itemsLength, pulldown->foregroundColor, pulldown->backgroundColor, menuBar, pulldownIndex); if (keyCode < -1) { pulldownIndex = -2 - keyCode; } @@ -1168,20 +1167,20 @@ int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex) return keyCode; } -// Calculates max length of string needed to represent a1 or a2. +// Calculates max length of string needed to represent `value` or `value2`. // // 0x4DD03C -size_t _calc_max_field_chars_wcursor(int a1, int a2) +size_t _calc_max_field_chars_wcursor(int value1, int value2) { char* str = (char*)internal_malloc(17); if (str == NULL) { return -1; } - snprintf(str, 17, "%d", a1); + snprintf(str, 17, "%d", value1); size_t len1 = strlen(str); - snprintf(str, 17, "%d", a2); + snprintf(str, 17, "%d", value2); size_t len2 = strlen(str); internal_free(str); @@ -1272,7 +1271,7 @@ void _tm_kill_msg() } // 0x4DD744 -void _tm_kill_out_of_order(int a1) +void _tm_kill_out_of_order(int queueIndex) { int v7; int v6; @@ -1281,16 +1280,16 @@ void _tm_kill_out_of_order(int a1) return; } - if (!_tm_index_active(a1)) { + if (!_tm_index_active(queueIndex)) { return; } - windowDestroy(_tm_queue[a1].field_4); + windowDestroy(_tm_queue[queueIndex].field_4); - _tm_location[_tm_queue[a1].field_8].field_0 = 0; + _tm_location[_tm_queue[queueIndex].field_8].field_0 = 0; - if (a1 != _tm_kill) { - v6 = a1; + if (queueIndex != _tm_kill) { + v6 = queueIndex; do { v7 = v6 - 1; if (v7 < 0) { @@ -1317,35 +1316,35 @@ void _tm_kill_out_of_order(int a1) void _tm_click_response(int btn) { int win; - int v3; + int queueIndex; if (_tm_kill == -1) { return; } win = buttonGetWindowId(btn); - v3 = _tm_kill; - while (win != _tm_queue[v3].field_4) { - v3++; - if (v3 == 5) { - v3 = 0; + queueIndex = _tm_kill; + while (win != _tm_queue[queueIndex].field_4) { + queueIndex++; + if (queueIndex == 5) { + queueIndex = 0; } - if (v3 == _tm_kill || !_tm_index_active(v3)) + if (queueIndex == _tm_kill || !_tm_index_active(queueIndex)) return; } - _tm_kill_out_of_order(v3); + _tm_kill_out_of_order(queueIndex); } // 0x4DD870 -int _tm_index_active(int a1) +int _tm_index_active(int queueIndex) { if (_tm_kill != _tm_add) { if (_tm_kill >= _tm_add) { - if (a1 >= _tm_add && a1 < _tm_kill) + if (queueIndex >= _tm_add && queueIndex < _tm_kill) return 0; - } else if (a1 < _tm_kill || a1 >= _tm_add) { + } else if (queueIndex < _tm_kill || queueIndex >= _tm_add) { return 0; } } diff --git a/src/window_manager_private.h b/src/window_manager_private.h index abbc4e8..449a2ed 100644 --- a/src/window_manager_private.h +++ b/src/window_manager_private.h @@ -13,30 +13,30 @@ typedef void(ListSelectionHandler)(char** items, int index); extern char gProgramWindowTitle[256]; -int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int a7); -int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int a7, int a8); +int _win_list_select(const char* title, char** fileList, int fileListLength, ListSelectionHandler* callback, int x, int y, int color); +int _win_list_select_at(const char* title, char** items, int itemsLength, ListSelectionHandler* callback, int x, int y, int color, int start); int _win_get_str(char* dest, int length, const char* title, int x, int y); -int _win_msg(const char* string, int x, int y, int flags); -int _win_pull_down(char** items, int itemsLength, int x, int y, int a5); -int _create_pull_down(char** stringList, int stringListLength, int x, int y, int a5, int a6, Rect* rect); +int _win_msg(const char* string, int x, int y, int color); +int _win_pull_down(char** items, int itemsLength, int x, int y, int color); +int _create_pull_down(char** stringList, int stringListLength, int x, int y, int foregroundColor, int backgroundColor, Rect* rect); int _win_debug(char* string); void _win_debug_delete(int btn, int keyCode); -int _win_register_menu_bar(int win, int x, int y, int width, int height, int borderColor, int backgroundColor); -int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int a7, int a8); +int _win_register_menu_bar(int win, int x, int y, int width, int height, int foregroundColor, int backgroundColor); +int _win_register_menu_pulldown(int win, int x, char* title, int keyCode, int itemsLength, char** items, int foregroundColor, int backgroundColor); void _win_delete_menu_bar(int win); int _find_first_letter(int ch, char** stringList, int stringListLength); int _win_width_needed(char** fileNameList, int fileNameListLength); int _win_input_str(int win, char* dest, int maxLength, int x, int y, int textColor, int backgroundColor); -int sub_4DBD04(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex); +int process_pull_down(int win, Rect* rect, char** items, int itemsLength, int a5, int a6, MenuBar* menuBar, int pulldownIndex); int _GNW_process_menu(MenuBar* menuBar, int pulldownIndex); -size_t _calc_max_field_chars_wcursor(int a1, int a2); +size_t _calc_max_field_chars_wcursor(int value1, int value2); void _GNW_intr_init(); void _GNW_intr_exit(); void _tm_watch_msgs(); void _tm_kill_msg(); -void _tm_kill_out_of_order(int a1); +void _tm_kill_out_of_order(int queueIndex); void _tm_click_response(int btn); -int _tm_index_active(int a1); +int _tm_index_active(int queueIndex); } // namespace fallout From a2eabd668bbc8354b9d707043c19021bbd77f608 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 11 Apr 2023 14:03:55 +0300 Subject: [PATCH 47/50] Fix missing speech in some Russian localizations Closes #246 --- src/sound_decoder.cc | 100 +++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/src/sound_decoder.cc b/src/sound_decoder.cc index 3b2c33a..83b020a 100644 --- a/src/sound_decoder.cc +++ b/src/sound_decoder.cc @@ -34,13 +34,15 @@ static int ReadBand_Fmt24(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt26(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt27(SoundDecoder* soundDecoder, int offset, int bits); static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits); -static int ReadBands(SoundDecoder* ptr); +static bool ReadBands(SoundDecoder* soundDecoder); static void untransform_subband0(unsigned char* a1, unsigned char* a2, int a3, int a4); static void untransform_subband(unsigned char* a1, unsigned char* a2, int a3, int a4); static void untransform_all(SoundDecoder* soundDecoder); +static bool soundDecoderFill(SoundDecoder* soundDecoder); static inline void soundDecoderRequireBits(SoundDecoder* soundDecoder, int bits); static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits); +static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits); // 0x51E328 static int gSoundDecodersCount = 0; @@ -78,7 +80,7 @@ static ReadBandFunc _ReadBand_tbl[32] = { ReadBand_Fail, ReadBand_Fmt29, ReadBand_Fail, - ReadBand_Fail, + ReadBand_Fmt31, }; // 0x6AD960 @@ -751,7 +753,7 @@ static int ReadBand_Fmt29(SoundDecoder* soundDecoder, int offset, int bits) } // 0x4D493C -static int ReadBands(SoundDecoder* soundDecoder) +static bool ReadBands(SoundDecoder* soundDecoder) { int v9; int v15; @@ -797,10 +799,10 @@ static int ReadBands(SoundDecoder* soundDecoder) fn = _ReadBand_tbl[bits]; if (!fn(soundDecoder, index, bits)) { - return 0; + return false; } } - return 1; + return true; } // 0x4D4ADC @@ -1043,53 +1045,67 @@ static void untransform_all(SoundDecoder* soundDecoder) } } +// NOTE: Inlined. +// +// 0x4D4F58 +static bool soundDecoderFill(SoundDecoder* soundDecoder) +{ + // CE: Implementation is slightly different. `ReadBands` now handles new + // Fmt31 used in some Russian localizations. The appropriate handler acts as + // both decoder and transformer, so there is no need to untransform bands + // once again. This approach assumes band 31 is never used by standard acms + // and mods. + if (ReadBands(soundDecoder)) { + untransform_all(soundDecoder); + } + + soundDecoder->file_cnt -= soundDecoder->total_samples; + soundDecoder->samp_ptr = soundDecoder->samples; + soundDecoder->samp_cnt = soundDecoder->total_samples; + + if (soundDecoder->file_cnt < 0) { + soundDecoder->samp_cnt += soundDecoder->file_cnt; + soundDecoder->file_cnt = 0; + } + + return true; +} + // 0x4D4FA0 size_t soundDecoderDecode(SoundDecoder* soundDecoder, void* buffer, size_t size) { unsigned char* dest; - unsigned char* v5; - int v6; - int v4; + unsigned char* samp_ptr; + int samp_cnt; dest = (unsigned char*)buffer; - v4 = 0; - v5 = soundDecoder->samp_ptr; - v6 = soundDecoder->samp_cnt; + samp_ptr = soundDecoder->samp_ptr; + samp_cnt = soundDecoder->samp_cnt; size_t bytesRead; for (bytesRead = 0; bytesRead < size; bytesRead += 2) { - if (!v6) { - if (!soundDecoder->file_cnt) { + if (samp_cnt == 0) { + if (soundDecoder->file_cnt == 0) { break; } - if (!ReadBands(soundDecoder)) { + // NOTE: Uninline. + if (!soundDecoderFill(soundDecoder)) { break; } - untransform_all(soundDecoder); - - soundDecoder->file_cnt -= soundDecoder->total_samples; - soundDecoder->samp_ptr = soundDecoder->samples; - soundDecoder->samp_cnt = soundDecoder->total_samples; - - if (soundDecoder->file_cnt < 0) { - soundDecoder->samp_cnt += soundDecoder->file_cnt; - soundDecoder->file_cnt = 0; - } - - v5 = soundDecoder->samp_ptr; - v6 = soundDecoder->samp_cnt; + samp_ptr = soundDecoder->samp_ptr; + samp_cnt = soundDecoder->samp_cnt; } - int v13 = *(int*)v5; - v5 += 4; - *(unsigned short*)(dest + bytesRead) = (v13 >> soundDecoder->levels) & 0xFFFF; - v6--; + int sample = *(int*)samp_ptr; + samp_ptr += 4; + *(unsigned short*)(dest + bytesRead) = (sample >> soundDecoder->levels) & 0xFFFF; + samp_cnt--; } - soundDecoder->samp_ptr = v5; - soundDecoder->samp_cnt = v6; + soundDecoder->samp_ptr = samp_ptr; + soundDecoder->samp_cnt = samp_cnt; return bytesRead; } @@ -1259,4 +1275,22 @@ static inline void soundDecoderDropBits(SoundDecoder* soundDecoder, int bits) soundDecoder->bits -= bits; } +static int ReadBand_Fmt31(SoundDecoder* soundDecoder, int offset, int bits) +{ + int* samples = (int*)soundDecoder->samples; + + int remaining_samples = soundDecoder->total_samples; + while (remaining_samples != 0) { + soundDecoderRequireBits(soundDecoder, 16); + int value = soundDecoder->hold & 0xFFFF; + soundDecoderDropBits(soundDecoder, 16); + + *samples++ = (value << 16) >> (16 - soundDecoder->levels); + + remaining_samples--; + } + + return 0; +} + } // namespace fallout From 69e4adf5b321e64b4be9db1a313c91cabe50dfd5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 11 Apr 2023 16:37:01 +0300 Subject: [PATCH 48/50] Add HQ music support See #239 --- src/audio.cc | 28 +++++----------------------- src/audio.h | 2 +- src/audio_file.cc | 28 +++++----------------------- src/audio_file.h | 2 +- src/game_sound.cc | 8 ++------ src/sound.cc | 10 +++++----- src/sound.h | 2 +- src/sound_effects_cache.cc | 2 +- src/sound_effects_cache.h | 2 +- 9 files changed, 22 insertions(+), 62 deletions(-) diff --git a/src/audio.cc b/src/audio.cc index ee71201..77000c6 100644 --- a/src/audio.cc +++ b/src/audio.cc @@ -58,7 +58,7 @@ static int audioSoundDecoderReadHandler(void* data, void* buffer, unsigned int s // AudioOpen // 0x41A2EC -int audioOpen(const char* fname, int flags) +int audioOpen(const char* fname, int* channels, int* sampleRate) { char path[80]; snprintf(path, sizeof(path), "%s", fname); @@ -70,28 +70,7 @@ int audioOpen(const char* fname, int flags) compression = 0; } - char mode[4]; - memset(mode, 0, 4); - - // NOTE: Original implementation is slightly different, it uses separate - // variable to track index where to set 't' and 'b'. - char* pm = mode; - if (flags & 1) { - *pm++ = 'w'; - } else if (flags & 2) { - *pm++ = 'w'; - *pm++ = '+'; - } else { - *pm++ = 'r'; - } - - if (flags & 0x100) { - *pm++ = 't'; - } else if (flags & 0x200) { - *pm++ = 'b'; - } - - File* stream = fileOpen(path, mode); + File* stream = fileOpen(path, "rb"); if (stream == NULL) { debugPrint("AudioOpen: Couldn't open %s for read\n", path); return -1; @@ -121,6 +100,9 @@ int audioOpen(const char* fname, int flags) audioFile->flags |= AUDIO_COMPRESSED; audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->fileSize *= 2; + + *channels = audioFile->channels; + *sampleRate = audioFile->sampleRate; } else { audioFile->fileSize = fileGetSize(stream); } diff --git a/src/audio.h b/src/audio.h index 2b96ac9..752b914 100644 --- a/src/audio.h +++ b/src/audio.h @@ -5,7 +5,7 @@ namespace fallout { typedef bool(AudioQueryCompressedFunc)(char* filePath); -int audioOpen(const char* fname, int mode); +int audioOpen(const char* fname, int* channels, int* sampleRate); int audioClose(int handle); int audioRead(int handle, void* buffer, unsigned int size); long audioSeek(int handle, long offset, int origin); diff --git a/src/audio_file.cc b/src/audio_file.cc index d1dce84..8333c5a 100644 --- a/src/audio_file.cc +++ b/src/audio_file.cc @@ -57,7 +57,7 @@ static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned i } // 0x41A88C -int audioFileOpen(const char* fname, int flags) +int audioFileOpen(const char* fname, int* channels, int* sampleRate) { char path[COMPAT_MAX_PATH]; strcpy(path, fname); @@ -69,28 +69,7 @@ int audioFileOpen(const char* fname, int flags) compression = 0; } - char mode[4]; - memset(mode, '\0', 4); - - // NOTE: Original implementation is slightly different, it uses separate - // variable to track index where to set 't' and 'b'. - char* pm = mode; - if (flags & 0x01) { - *pm++ = 'w'; - } else if (flags & 0x02) { - *pm++ = 'w'; - *pm++ = '+'; - } else { - *pm++ = 'r'; - } - - if (flags & 0x0100) { - *pm++ = 't'; - } else if (flags & 0x0200) { - *pm++ = 'b'; - } - - FILE* stream = compat_fopen(path, mode); + FILE* stream = compat_fopen(path, "rb"); if (stream == NULL) { return -1; } @@ -119,6 +98,9 @@ int audioFileOpen(const char* fname, int flags) audioFile->flags |= AUDIO_FILE_COMPRESSED; audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize)); audioFile->fileSize *= 2; + + *channels = audioFile->channels; + *sampleRate = audioFile->sampleRate; } else { audioFile->fileSize = getFileSize(stream); } diff --git a/src/audio_file.h b/src/audio_file.h index f00734b..a43bdc7 100644 --- a/src/audio_file.h +++ b/src/audio_file.h @@ -5,7 +5,7 @@ namespace fallout { typedef bool(AudioFileQueryCompressedFunc)(char* filePath); -int audioFileOpen(const char* fname, int flags); +int audioFileOpen(const char* fname, int* channels, int* sampleRate); int audioFileClose(int handle); int audioFileRead(int handle, void* buf, unsigned int size); long audioFileSeek(int handle, long offset, int origin); diff --git a/src/game_sound.cc b/src/game_sound.cc index e7a838c..b1033ed 100644 --- a/src/game_sound.cc +++ b/src/game_sound.cc @@ -157,7 +157,7 @@ static int _gsound_speech_volume_get_set(int volume); static void speechPause(); static void speechResume(); static void _gsound_bkg_proc(); -static int gameSoundFileOpen(const char* fname, int access); +static int gameSoundFileOpen(const char* fname, int* channels, int* sampleRate); static long _gsound_write_(); static long gameSoundFileTellNotImplemented(int handle); static int gameSoundFileWrite(int handle, const void* buf, unsigned int size); @@ -1548,12 +1548,8 @@ void _gsound_bkg_proc() } // 0x451A08 -int gameSoundFileOpen(const char* fname, int flags) +int gameSoundFileOpen(const char* fname, int* channels, int* sampleRate) { - if ((flags & 2) != 0) { - return -1; - } - File* stream = fileOpen(fname, "rb"); if (stream == NULL) { return -1; diff --git a/src/sound.cc b/src/sound.cc index 353b605..5b6114b 100644 --- a/src/sound.cc +++ b/src/sound.cc @@ -1,5 +1,6 @@ #include "sound.h" +#include #include #include #include @@ -8,7 +9,6 @@ #ifdef _WIN32 #include #else -#include #include #endif @@ -49,7 +49,7 @@ static long soundFileSize(int fileHandle); static long soundTellData(int fileHandle); static int soundWriteData(int fileHandle, const void* buf, unsigned int size); static int soundReadData(int fileHandle, void* buf, unsigned int size); -static int soundOpenData(const char* filePath, int flags); +static int soundOpenData(const char* filePath, int* channels, int* sampleRate); static long soundSeekData(int fileHandle, long offset, int origin); static int soundCloseData(int fileHandle); static char* soundFileManglerDefaultImpl(char* fname); @@ -223,9 +223,9 @@ static int soundReadData(int fileHandle, void* buf, unsigned int size) } // 0x4AC768 -static int soundOpenData(const char* filePath, int flags) +static int soundOpenData(const char* filePath, int* channels, int* sampleRate) { - return open(filePath, flags); + return open(filePath, _O_RDONLY | _O_BINARY); } // 0x4AC774 @@ -608,7 +608,7 @@ int soundLoad(Sound* sound, char* filePath) return gSoundLastError; } - sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), 0x0200); + sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), &(sound->channels), &(sound->rate)); if (sound->io.fd == -1) { gSoundLastError = SOUND_FILE_NOT_FOUND; return gSoundLastError; diff --git a/src/sound.h b/src/sound.h index ab81e63..427b5f8 100644 --- a/src/sound.h +++ b/src/sound.h @@ -46,7 +46,7 @@ typedef enum SoundError { SOUND_ERR_COUNT, } SoundError; -typedef int SoundOpenProc(const char* filePath, int flags); +typedef int SoundOpenProc(const char* filePath, int* channels, int* sampleRate); typedef int SoundCloseProc(int fileHandle); typedef int SoundReadProc(int fileHandle, void* buf, unsigned int size); typedef int SoundWriteProc(int fileHandle, const void* buf, unsigned int size); diff --git a/src/sound_effects_cache.cc b/src/sound_effects_cache.cc index 26aead6..30f91ae 100644 --- a/src/sound_effects_cache.cc +++ b/src/sound_effects_cache.cc @@ -154,7 +154,7 @@ void soundEffectsCacheFlush() // sfxc_cached_open // 0x4A915C -int soundEffectsCacheFileOpen(const char* fname, int mode) +int soundEffectsCacheFileOpen(const char* fname, int* channels, int* sampleRate) { if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) { return -1; diff --git a/src/sound_effects_cache.h b/src/sound_effects_cache.h index e6e0972..579c856 100644 --- a/src/sound_effects_cache.h +++ b/src/sound_effects_cache.h @@ -11,7 +11,7 @@ int soundEffectsCacheInit(int cache_size, const char* effectsPath); void soundEffectsCacheExit(); int soundEffectsCacheInitialized(); void soundEffectsCacheFlush(); -int soundEffectsCacheFileOpen(const char* fname, int mode); +int soundEffectsCacheFileOpen(const char* fname, int* channels, int* sampleRate); int soundEffectsCacheFileClose(int handle); int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size); int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size); From 11472e8be9dc3499cd7b82eb263e02035489b6ce Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 11 Apr 2023 17:27:34 +0300 Subject: [PATCH 49/50] Fix soundOpenData flags --- src/sound.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sound.cc b/src/sound.cc index 5b6114b..0576358 100644 --- a/src/sound.cc +++ b/src/sound.cc @@ -225,7 +225,15 @@ static int soundReadData(int fileHandle, void* buf, unsigned int size) // 0x4AC768 static int soundOpenData(const char* filePath, int* channels, int* sampleRate) { - return open(filePath, _O_RDONLY | _O_BINARY); + int flags; + +#ifdef _WIN32 + flags = _O_RDONLY | _O_BINARY; +#else + flags = O_RDONLY; +#endif + + return open(filePath, flags); } // 0x4AC774 From c8d45854ba8be7834f2547a1240ff1e033f13ce3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 11 Apr 2023 20:00:38 +0300 Subject: [PATCH 50/50] Normalize line endings when reading text files Closes #250 --- src/config.cc | 2 +- src/dictionary.cc | 2 +- src/platform_compat.cc | 30 ++++++++++++++++++++++++++++++ src/platform_compat.h | 2 ++ src/xfile.cc | 4 ++-- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/config.cc b/src/config.cc index 9143e55..966b0d4 100644 --- a/src/config.cc +++ b/src/config.cc @@ -289,7 +289,7 @@ bool configRead(Config* config, const char* filePath, bool isDb) } else { FILE* stream = compat_fopen(filePath, "rt"); if (stream != NULL) { - while (fgets(string, sizeof(string), stream) != NULL) { + while (compat_fgets(string, sizeof(string), stream) != NULL) { configParseLine(config, string); } diff --git a/src/dictionary.cc b/src/dictionary.cc index af873e9..31acacb 100644 --- a/src/dictionary.cc +++ b/src/dictionary.cc @@ -447,7 +447,7 @@ int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3) return -1; } - if (fgets(entry->key, keyLength + 1, stream) == NULL) { + if (compat_fgets(entry->key, keyLength + 1, stream) == NULL) { return -1; } diff --git a/src/platform_compat.cc b/src/platform_compat.cc index 5297592..b629fb6 100644 --- a/src/platform_compat.cc +++ b/src/platform_compat.cc @@ -239,6 +239,36 @@ gzFile compat_gzopen(const char* path, const char* mode) return gzopen(nativePath, mode); } +char* compat_fgets(char* buffer, int maxCount, FILE* stream) +{ + buffer = fgets(buffer, maxCount, stream); + + if (buffer != NULL) { + size_t len = strlen(buffer); + if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') { + buffer[len - 2] = '\n'; + buffer[len - 1] = '\0'; + } + } + + return buffer; +} + +char* compat_gzgets(gzFile stream, char* buffer, int maxCount) +{ + buffer = gzgets(stream, buffer, maxCount); + + if (buffer != NULL) { + size_t len = strlen(buffer); + if (len >= 2 && buffer[len - 1] == '\n' && buffer[len - 2] == '\r') { + buffer[len - 2] = '\n'; + buffer[len - 1] = '\0'; + } + } + + return buffer; +} + int compat_remove(const char* path) { char nativePath[COMPAT_MAX_PATH]; diff --git a/src/platform_compat.h b/src/platform_compat.h index be88974..9fb7574 100644 --- a/src/platform_compat.h +++ b/src/platform_compat.h @@ -35,6 +35,8 @@ int compat_mkdir(const char* path); unsigned int compat_timeGetTime(); FILE* compat_fopen(const char* path, const char* mode); gzFile compat_gzopen(const char* path, const char* mode); +char* compat_fgets(char* buffer, int maxCount, FILE* stream); +char* compat_gzgets(gzFile stream, char* buffer, int maxCount); int compat_remove(const char* path); int compat_rename(const char* oldFileName, const char* newFileName); void compat_windows_path_to_native(char* path); diff --git a/src/xfile.cc b/src/xfile.cc index f2ce72c..60bcea2 100644 --- a/src/xfile.cc +++ b/src/xfile.cc @@ -234,10 +234,10 @@ char* xfileReadString(char* string, int size, XFile* stream) result = dfileReadString(string, size, stream->dfile); break; case XFILE_TYPE_GZFILE: - result = gzgets(stream->gzfile, string, size); + result = compat_gzgets(stream->gzfile, string, size); break; default: - result = fgets(string, size, stream->file); + result = compat_fgets(string, size, stream->file); break; }