diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 4703edf5..aa9e368c 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -63,7 +63,7 @@ jobs: uses: actions/cache@v4 with: path: os/android/app/.cxx - key: android-cmake-v1 + key: android-cmake-v2 - name: Setup signing config if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != '' @@ -90,7 +90,7 @@ jobs: ios: name: iOS - runs-on: macos-12 + runs-on: macos-13 steps: - name: Clone @@ -100,7 +100,7 @@ jobs: uses: actions/cache@v4 with: path: build - key: ios-cmake-v2 + key: ios-cmake-v4 - name: Configure run: | @@ -167,7 +167,7 @@ jobs: uses: actions/cache@v4 with: path: build - key: linux-${{ matrix.arch }}-cmake-v1 + key: linux-${{ matrix.arch }}-cmake-v3 - name: Configure (x86) if: matrix.arch == 'x86' @@ -203,7 +203,7 @@ jobs: macos: name: macOS - runs-on: macos-11 + runs-on: macos-13 steps: - name: Clone @@ -213,7 +213,7 @@ jobs: uses: actions/cache@v4 with: path: build - key: macos-cmake-v4 + key: macos-cmake-v6 - name: Configure run: | @@ -265,7 +265,7 @@ jobs: uses: actions/cache@v4 with: path: build - key: windows-${{ matrix.arch }}-cmake-v1 + key: windows-${{ matrix.arch }}-cmake-v2 - name: Configure run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index bd8188e6..0c4079ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,10 @@ set(EXECUTABLE_NAME fallout2-ce) if(APPLE) if(IOS) - set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "") + set(CMAKE_OSX_DEPLOYMENT_TARGET "12" CACHE STRING "") set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "") else() - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "") set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "") endif() endif() @@ -361,8 +361,7 @@ if(APPLE) endif() add_subdirectory("third_party/fpattern") -target_link_libraries(${EXECUTABLE_NAME} ${FPATTERN_LIBRARY}) -target_include_directories(${EXECUTABLE_NAME} PRIVATE ${FPATTERN_INCLUDE_DIR}) +target_link_libraries(${EXECUTABLE_NAME} fpattern::fpattern) if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")) if((NOT ${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")) diff --git a/README.md b/README.md index a6bedc8f..e8bdaf52 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ There is also [Fallout Community Edition](https://github.com/alexbatalov/fallout ## 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](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. +You must own the game to play. Purchase your copy on [GOG](https://www.gog.com/game/fallout_2), [Epic Games](https://store.epicgames.com/p/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 diff --git a/src/animation.h b/src/animation.h index 9f0e297f..b5293a02 100644 --- a/src/animation.h +++ b/src/animation.h @@ -92,7 +92,7 @@ typedef enum AnimationType { LAST_SF_DEATH_ANIM = ANIM_FALL_FRONT_BLOOD_SF, } AnimationType; -#define FID_ANIM_TYPE(value) ((value)&0xFF0000) >> 16 +#define FID_ANIM_TYPE(value) ((value) & 0xFF0000) >> 16 // Signature of animation callback accepting 2 parameters. typedef int(AnimationCallback)(void* a1, void* a2); diff --git a/src/art.cc b/src/art.cc index 18be4546..696aa00f 100644 --- a/src/art.cc +++ b/src/art.cc @@ -525,7 +525,7 @@ int artCopyFileName(int objectType, int id, char* dest) { ArtListDescription* ptr; - if (objectType < OBJ_TYPE_ITEM && objectType >= OBJ_TYPE_COUNT) { + if (objectType < OBJ_TYPE_ITEM || objectType >= OBJ_TYPE_COUNT) { return -1; } @@ -633,11 +633,11 @@ char* artBuildFilePath(int fid) v5 = (v2 & 0xF000) >> 12; type = FID_TYPE(v2); - if (v3 >= gArtListDescriptions[type].fileNamesLength) { + if (type < OBJ_TYPE_ITEM || type >= OBJ_TYPE_COUNT) { return nullptr; } - if (type < OBJ_TYPE_ITEM || type >= OBJ_TYPE_COUNT) { + if (v3 >= gArtListDescriptions[type].fileNamesLength) { return nullptr; } diff --git a/src/character_editor.cc b/src/character_editor.cc index 49e0124a..0d989a7d 100644 --- a/src/character_editor.cc +++ b/src/character_editor.cc @@ -995,7 +995,7 @@ int characterEditorShow(bool isCreationMode) } } else if (characterEditorSelectedItem >= 61 && characterEditorSelectedItem < 79) { if (gCharacterEditorIsCreationMode) { - _win_button_press_and_release(gCharacterEditorTagSkillBtns[gCharacterEditorIsCreationMode - 61]); + _win_button_press_and_release(gCharacterEditorTagSkillBtns[characterEditorSelectedItem - 61]); windowRefresh(gCharacterEditorWindow); } else { characterEditorHandleAdjustSkillButtonPressed(keyCode); @@ -1003,7 +1003,7 @@ int characterEditorShow(bool isCreationMode) } } else if (characterEditorSelectedItem >= 82 && characterEditorSelectedItem < 98) { if (gCharacterEditorIsCreationMode) { - _win_button_press_and_release(gCharacterEditorOptionalTraitBtns[gCharacterEditorIsCreationMode - 82]); + _win_button_press_and_release(gCharacterEditorOptionalTraitBtns[characterEditorSelectedItem - 82]); windowRefresh(gCharacterEditorWindow); } } @@ -1018,7 +1018,7 @@ int characterEditorShow(bool isCreationMode) } } else if (characterEditorSelectedItem >= 61 && characterEditorSelectedItem < 79) { if (gCharacterEditorIsCreationMode) { - _win_button_press_and_release(gCharacterEditorTagSkillBtns[gCharacterEditorIsCreationMode - 61]); + _win_button_press_and_release(gCharacterEditorTagSkillBtns[characterEditorSelectedItem - 61]); windowRefresh(gCharacterEditorWindow); } else { characterEditorHandleAdjustSkillButtonPressed(keyCode); @@ -1026,7 +1026,7 @@ int characterEditorShow(bool isCreationMode) } } else if (characterEditorSelectedItem >= 82 && characterEditorSelectedItem < 98) { if (gCharacterEditorIsCreationMode) { - _win_button_press_and_release(gCharacterEditorOptionalTraitBtns[gCharacterEditorIsCreationMode - 82]); + _win_button_press_and_release(gCharacterEditorOptionalTraitBtns[characterEditorSelectedItem - 82]); windowRefresh(gCharacterEditorWindow); } } @@ -1877,7 +1877,7 @@ static void characterEditorWindowFree() fontSetCurrent(gCharacterEditorOldFont); - if (gCharacterEditorIsCreationMode == 1) { + if (gCharacterEditorIsCreationMode) { skillsSetTagged(gCharacterEditorTempTaggedSkills, 3); traitsSetSelected(gCharacterEditorTempTraits[0], gCharacterEditorTempTraits[1]); characterEditorSelectedItem = 0; @@ -2366,7 +2366,7 @@ static void characterEditorDrawPcStats() char formattedValueBuffer[16]; char stringBuffer[128]; - if (gCharacterEditorIsCreationMode == 1) { + if (gCharacterEditorIsCreationMode) { return; } @@ -2930,7 +2930,7 @@ static void characterEditorDrawSkills(int a1) selectedSkill = characterEditorSelectedItem - EDITOR_FIRST_SKILL; } - if (gCharacterEditorIsCreationMode == 0 && a1 == 0) { + if (!gCharacterEditorIsCreationMode && a1 == 0) { buttonDestroy(gCharacterEditorSliderPlusBtn); buttonDestroy(gCharacterEditorSliderMinusBtn); gCharacterEditorSliderMinusBtn = -1; @@ -4868,7 +4868,7 @@ static void characterEditorRestorePlayer() } } - if (gCharacterEditorIsCreationMode == 1) { + if (gCharacterEditorIsCreationMode) { v3 -= gCharacterEditorIsCreationMode; } @@ -5373,7 +5373,7 @@ static void characterEditorDrawOptionalTraits() double step; double y; - if (gCharacterEditorIsCreationMode != 1) { + if (!gCharacterEditorIsCreationMode) { return; } @@ -7250,7 +7250,7 @@ static int customKarmaFolderGetFrmId() return entry.frmId; } } - return gCustomKarmaFolderDescriptions.end()->frmId; + return gCustomKarmaFolderDescriptions.back().frmId; } static void customTownReputationInit() diff --git a/src/combat.cc b/src/combat.cc index 63d48059..825a5734 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -5475,11 +5475,13 @@ static void _draw_loc_(int eventCode, int color) // 0x426218 static int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hitMode) { + *hitLocation = HIT_LOCATION_TORSO; + if (critter == nullptr) { return 0; } - if (critter->pid >> 24 != OBJ_TYPE_CRITTER) { + if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) { return 0; } diff --git a/src/debug.cc b/src/debug.cc index abd96f04..a268853a 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -138,7 +138,7 @@ int debugPrint(const char* format, ...) rc = gDebugPrintProc(string); } else { -#ifdef _DEBUG +#ifndef NDEBUG SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, format, args); #endif rc = -1; diff --git a/src/dfile.cc b/src/dfile.cc index bf3dd79c..a9048617 100644 --- a/src/dfile.cc +++ b/src/dfile.cc @@ -7,7 +7,7 @@ #include -#include +#include #include "platform_compat.h" diff --git a/src/file_find.cc b/src/file_find.cc index 1d7da82b..8671df32 100644 --- a/src/file_find.cc +++ b/src/file_find.cc @@ -3,7 +3,7 @@ #include #include -#include +#include namespace fallout { @@ -30,6 +30,7 @@ bool fileFindFirst(const char* path, DirectoryFileFindData* findData) char basePath[COMPAT_MAX_PATH]; compat_makepath(basePath, drive, dir, nullptr, nullptr); + compat_resolve_path(basePath); findData->dir = opendir(basePath); if (findData->dir == nullptr) { diff --git a/src/game_sound.cc b/src/game_sound.cc index a40afd22..7ed4833b 100644 --- a/src/game_sound.cc +++ b/src/game_sound.cc @@ -107,10 +107,10 @@ static int _background_loop_requested = -1; static char* _sound_sfx_path = _aSoundSfx; // 0x518E78 -static char* _sound_music_path1 = _aSoundMusic_0; +static char* _sound_music_path1 = nullptr; // 0x518E7C -static char* _sound_music_path2 = _aSoundMusic_0; +static char* _sound_music_path2 = nullptr; // 0x518E80 static char* _sound_speech_path = _aSoundSpeech_0; @@ -391,6 +391,9 @@ int gameSoundExit() audioFileExit(); audioExit(); + internal_free(_sound_music_path1); + internal_free(_sound_music_path2); + gGameSoundInitialized = false; return 0; @@ -1957,12 +1960,15 @@ int _gsound_get_music_path(char** out_value, const char* key) char* copy; char* value; - configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, out_value); + if (!configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, &value)) { + *out_value = internal_strdup(_aSoundMusic_0); + return 0; + } - value = *out_value; len = strlen(value); if (value[len - 1] == '\\' || value[len - 1] == '/') { + *out_value = internal_strdup(value); return 0; } @@ -1978,7 +1984,9 @@ int _gsound_get_music_path(char** out_value, const char* key) copy[len] = '\\'; copy[len + 1] = '\0'; - if (configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, copy) != 1) { + if (!configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, copy)) { + internal_free(copy); + if (gGameSoundDebugEnabled) { debugPrint("config_set_string failed in gsound_music_path.\n"); } @@ -1986,16 +1994,20 @@ int _gsound_get_music_path(char** out_value, const char* key) return -1; } - if (configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, out_value)) { + if (!configGetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, key, &value)) { internal_free(copy); - return 0; + + if (gGameSoundDebugEnabled) { + debugPrint("config_get_string failed in gsound_music_path.\n"); + } + + return -1; } - if (gGameSoundDebugEnabled) { - debugPrint("config_get_string failed in gsound_music_path.\n"); - } + internal_free(copy); - return -1; + *out_value = internal_strdup(value); + return 0; } // 0x452378 diff --git a/src/lips.cc b/src/lips.cc index 411c8b39..6b640a55 100644 --- a/src/lips.cc +++ b/src/lips.cc @@ -15,7 +15,7 @@ namespace fallout { -static char* _lips_fix_string(const char* fileName, size_t length); +static char* lips_fix_string(const char* fileName, size_t length); static int lipsReadV1(LipsData* a1, File* stream); static int _lips_make_speech(); @@ -65,14 +65,15 @@ static int _speechStartTime = 0; // 0x613CA0 static char _lips_subdir_name[14]; -// 0x613CAE -static char _tmp_str[50]; - // 0x47AAC0 -static char* _lips_fix_string(const char* fileName, size_t length) +static char* lips_fix_string(const char* fileName, size_t length) { - strncpy(_tmp_str, fileName, length); - return _tmp_str; + // 0x613CAE + static char tmp_str[50]; + + strncpy(tmp_str, fileName, length); + tmp_str[length] = '\0'; + return tmp_str; } // 0x47AAD8 @@ -212,7 +213,7 @@ static int lipsReadV1(LipsData* lipsData, File* stream) if (fileReadInt32(stream, &(lipsData->field_44)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_48)) == -1) return -1; if (fileReadInt32(stream, &(lipsData->field_4C)) == -1) return -1; - if (fileReadFixedLengthString(stream, lipsData->field_50, 8) == -1) return -1; + if (fileReadFixedLengthString(stream, lipsData->file_name, 8) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_58, 4) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_5C, 4) == -1) return -1; if (fileReadFixedLengthString(stream, lipsData->field_60, 4) == -1) return -1; @@ -235,7 +236,7 @@ int lipsLoad(const char* audioFileName, const char* headFileName) { char* sep; int i; - char v60[16]; + char audioBaseName[16]; SpeechMarker* speech_marker; SpeechMarker* prev_speech_marker; @@ -254,16 +255,16 @@ int lipsLoad(const char* audioFileName, const char* headFileName) *sep = '\0'; } - strcpy(v60, audioFileName); + strcpy(audioBaseName, audioFileName); - sep = strchr(v60, '.'); + sep = strchr(audioBaseName, '.'); if (sep != nullptr) { *sep = '\0'; } - strcpy(gLipsData.field_50, v60); + strncpy(gLipsData.file_name, audioBaseName, sizeof(gLipsData.file_name)); - strcat(path, _lips_fix_string(gLipsData.field_50, sizeof(gLipsData.field_50))); + strcat(path, lips_fix_string(gLipsData.file_name, sizeof(gLipsData.file_name))); strcat(path, "."); strcat(path, gLipsData.field_60); @@ -296,7 +297,7 @@ int lipsLoad(const char* audioFileName, const char* headFileName) if (fileReadInt32(stream, &(gLipsData.field_24)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_28)) == -1) return -1; if (fileReadInt32(stream, &(gLipsData.field_2C)) == -1) return -1; - if (fileReadFixedLengthString(stream, gLipsData.field_50, 8) == -1) return -1; + if (fileReadFixedLengthString(stream, gLipsData.file_name, 8) == -1) return -1; if (fileReadFixedLengthString(stream, gLipsData.field_58, 4) == -1) return -1; } else { debugPrint("\nError: Lips file WRONG version: %s!", path); @@ -405,7 +406,7 @@ static int _lips_make_speech() } char path[COMPAT_MAX_PATH]; - char* v1 = _lips_fix_string(gLipsData.field_50, sizeof(gLipsData.field_50)); + char* v1 = lips_fix_string(gLipsData.file_name, sizeof(gLipsData.file_name)); snprintf(path, sizeof(path), "%s%s\\%s.%s", "SOUND\\SPEECH\\", _lips_subdir_name, v1, "ACM"); if (gLipsData.sound != nullptr) { diff --git a/src/lips.h b/src/lips.h index 99552f77..dd69ffe9 100644 --- a/src/lips.h +++ b/src/lips.h @@ -40,7 +40,7 @@ typedef struct LipsData { int field_44; int field_48; int field_4C; - char field_50[8]; + char file_name[8]; char field_58[4]; char field_5C[4]; char field_60[4]; diff --git a/src/loadsave.cc b/src/loadsave.cc index 3fbe54d0..9423db80 100644 --- a/src/loadsave.cc +++ b/src/loadsave.cc @@ -390,6 +390,16 @@ int lsgSaveGame(int mode) fileClose(_flptr); } + if (!messageListInit(&gLoadSaveMessageList)) { + return -1; + } + + char path[COMPAT_MAX_PATH]; + snprintf(path, sizeof(path), "%s%s", asc_5186C8, "LSGAME.MSG"); + if (!messageListLoad(&gLoadSaveMessageList, path)) { + return -1; + } + _snapshotBuf = nullptr; int v6 = _QuickSnapShot(); if (v6 == 1) { @@ -409,16 +419,6 @@ int lsgSaveGame(int mode) return 1; } - if (!messageListInit(&gLoadSaveMessageList)) { - return -1; - } - - char path[COMPAT_MAX_PATH]; - snprintf(path, sizeof(path), "%s%s", asc_5186C8, "LSGAME.MSG"); - if (!messageListLoad(&gLoadSaveMessageList, path)) { - return -1; - } - soundPlayFile("iisxxxx1"); // Error saving game! diff --git a/src/obj_types.h b/src/obj_types.h index 8638f909..c1d1867c 100644 --- a/src/obj_types.h +++ b/src/obj_types.h @@ -29,7 +29,7 @@ enum { OBJ_TYPE_COUNT, }; -#define FID_TYPE(value) ((value)&0xF000000) >> 24 +#define FID_TYPE(value) ((value) & 0xF000000) >> 24 #define PID_TYPE(value) (value) >> 24 #define SID_TYPE(value) (value) >> 24 diff --git a/src/window_manager_private.cc b/src/window_manager_private.cc index bf3deccb..71ff89de 100644 --- a/src/window_manager_private.cc +++ b/src/window_manager_private.cc @@ -1294,12 +1294,9 @@ int win_get_num_i(int* value, int min, int max, bool clear, const char* title, i "Cancel", 0); - char* hint = (char*)internal_malloc(80); - if (hint == nullptr) { - return -1; - } + char hint[80]; - sprintf(hint, "Please enter a number between %d and %d.", min, max); + snprintf(hint, sizeof(hint), "Please enter a number between %d and %d.", min, max); windowRefresh(win); int rc; @@ -1317,7 +1314,6 @@ int win_get_num_i(int* value, int min, int max, bool clear, const char* title, i *value = original; } - internal_free(hint); windowDestroy(win); return rc; diff --git a/third_party/fpattern/CMakeLists.txt b/third_party/fpattern/CMakeLists.txt index 7f813a81..723b9af6 100644 --- a/third_party/fpattern/CMakeLists.txt +++ b/third_party/fpattern/CMakeLists.txt @@ -1,30 +1,10 @@ include(FetchContent) FetchContent_Declare(fpattern - GIT_REPOSITORY "https://github.com/Loadmaster/fpattern" - GIT_TAG "v1.9" + GIT_REPOSITORY "https://github.com/alexbatalov/fpattern" + GIT_TAG 8523173ec252c3b796fcdfca0fcc6329642fbbe3 # v1.9 + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE ) -FetchContent_GetProperties(fpattern) -if (NOT fpattern_POPULATED) - FetchContent_Populate(fpattern) -endif() - -if(MSVC) - set(CMAKE_DEBUG_POSTFIX "d") -endif() - -add_library(fpattern STATIC - "${fpattern_SOURCE_DIR}/debug.h" - "${fpattern_SOURCE_DIR}/fpattern.c" - "${fpattern_SOURCE_DIR}/fpattern.h" -) - -if(NOT WIN32) - target_compile_definitions(fpattern PRIVATE - "-Dunix" - ) -endif() - -set(FPATTERN_LIBRARY "fpattern" PARENT_SCOPE) -set(FPATTERN_INCLUDE_DIR "${fpattern_SOURCE_DIR}" PARENT_SCOPE) +FetchContent_MakeAvailable(fpattern)