18 Commits

Author SHA1 Message Date
Alexander Batalov
4c983a6307 Pump events more aggressively 2025-01-29 21:42:50 +03:00
Alexander Batalov
6a8950c4f2 Fix splash screen on macOS 2025-01-29 00:04:28 +03:00
Alexander Batalov
f9af163495 Fix negative weapon ammo (#444) 2025-01-28 22:27:43 +03:00
Ryan Deering
9d6f4d9686 Fix mouse sensitivity (#416) 2025-01-28 22:27:10 +03:00
Alexander Batalov
2e14e4620e Remove text floaters after moving to another map or elevation
Fixes #401
2025-01-21 22:58:04 +03:00
Alexander Batalov
95d92d8bd0 Fix world map tabs
Closes #419
2025-01-21 21:31:56 +03:00
Alexander Batalov
d586e81827 Migrate fpattern (#442) 2025-01-14 08:45:02 +03:00
Alexander Batalov
67375a5857 Update macOS/iOS deployment target (#440) 2025-01-13 21:22:30 +03:00
Alexander Batalov
3dbea2e400 Use snprintf in win_get_num_i 2025-01-13 20:33:11 +03:00
Graham Gower
bd7321b128 Fix buffer overrun for audio files with 8 char names (#378)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
2025-01-13 20:17:13 +03:00
Vasilii Rogin
ad7b0e56ab Fix default hit location on called shots (#429)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
2025-01-13 18:41:18 +03:00
Alexander V. Nikolaev
e770e64a48 Ensure fileFindFirst path is resolved (#369) 2025-01-13 18:05:12 +03:00
Jan Šimek
5956227dcb Fix debug print on non-Windows platforms and Quick-save message (#400) 2025-01-13 02:33:14 +03:00
Condratiy Lenovin
f443e365cc Add Epic Games Store link (#420) 2025-01-13 02:19:19 +03:00
Walter Agazzi
b443e21c6e Fix wrong variable usage in character_editor (#432) 2025-01-13 02:11:48 +03:00
Vasilii Rogin
19ed168d42 Pvs studio fixes (#434) 2025-01-13 01:55:32 +03:00
Vasilii Rogin
939211d640 Copy sound string because dictionary can re-allocate it (#436)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
2025-01-12 23:59:34 +03:00
Alexander Batalov
0a9aaab4d1 Update macos runner (#437) 2025-01-12 22:04:21 +03:00
20 changed files with 123 additions and 105 deletions

View File

@@ -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 != ''
@@ -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'
@@ -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: |

View File

@@ -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 "Linux") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") AND (NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD"))
add_subdirectory("third_party/zlib")

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -7,7 +7,7 @@
#include <algorithm>
#include <fpattern.h>
#include <fpattern/fpattern.h>
#include "platform_compat.h"

View File

@@ -3,7 +3,7 @@
#include <stddef.h>
#include <string.h>
#include <fpattern.h>
#include <fpattern/fpattern.h>
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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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];

View File

@@ -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!

View File

@@ -1255,6 +1255,9 @@ int mapHandleTransition()
} else {
if (!isInCombat()) {
if (gMapTransition.map != gMapHeader.field_34 || gElevation == gMapTransition.elevation) {
// SFALL: Remove text floaters after moving to another map.
textObjectsReset();
mapLoadById(gMapTransition.map);
}

View File

@@ -700,7 +700,7 @@ void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons)
// 0x4CAC3C
void mouseSetSensitivity(double value)
{
if (value > 0 && value < 2.0) {
if (value >= 1.0 && value <= 2.5) {
gMouseSensitivity = value;
}
}

View File

@@ -449,6 +449,12 @@ int objectRead(Object* obj, File* stream)
if (PID_TYPE(obj->pid) == 0 && !(gMapHeader.flags & 0x01)) {
_object_fix_weapon_ammo(obj);
}
if (PID_TYPE(obj->pid) == OBJ_TYPE_ITEM
&& itemGetType(obj) == ITEM_TYPE_WEAPON
&& obj->data.item.weapon.ammoQuantity < 0) {
obj->data.item.weapon.ammoQuantity = 0;
}
}
return 0;
@@ -1476,6 +1482,9 @@ int objectSetLocation(Object* obj, int tile, int elevation, Rect* rect)
}
if (elevation != oldElevation) {
// SFALL: Remove text floaters after moving to another elevation.
textObjectsReset();
mapSetElevation(elevation);
tileSetCenter(tile, TILE_SET_CENTER_REFRESH_WINDOW | TILE_SET_CENTER_FLAG_IGNORE_SCROLL_RESTRICTIONS);
if (isInCombat()) {

View File

@@ -150,6 +150,15 @@ int _GNW95_init_mode_ex(int width, int height, int bpp)
return -1;
}
// macOS seems to require dequeuing NSApp events in order for window to
// become visible. There is no concrete number of calls required to make
// it happen. Sadly there is no particular event to watch for because SDL
// marks window as shown immediately after creation (see
// `SDL_FinishWindowCreation`).
for (int i = 0; i < 10; i++) {
SDL_PumpEvents();
}
_scr_size.left = 0;
_scr_size.top = 0;
_scr_size.right = width - 1;

View File

@@ -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;

View File

@@ -4380,14 +4380,10 @@ static void wmPartyWalkingStep()
// 0x4C219C
static void wmInterfaceScrollTabsStart(int delta)
{
for (int index = 0; index < 7; index++) {
buttonDisable(wmTownMapSubButtonIds[index]);
}
wmGenData.oldTabsOffsetY = wmGenData.tabsOffsetY;
// SFALL: Fix world map cities list scrolling bug that might leave buttons
// in the disabled state.
if (delta >= 0) {
if (wmGenData.oldTabsOffsetY < wmGenData.tabsBackgroundFrmImage.getHeight() - 230) {
if (wmGenData.tabsOffsetY < wmGenData.tabsBackgroundFrmImage.getHeight() - 230) {
wmGenData.oldTabsOffsetY = std::min(wmGenData.tabsOffsetY + 7 * delta, wmGenData.tabsBackgroundFrmImage.getHeight() - 230);
wmGenData.tabsScrollingDelta = delta;
}
@@ -4398,7 +4394,14 @@ static void wmInterfaceScrollTabsStart(int delta)
}
}
// NOTE: Uninline.
if (wmGenData.tabsScrollingDelta == 0) {
return;
}
for (int index = 0; index < 7; index++) {
buttonDisable(wmTownMapSubButtonIds[index]);
}
wmInterfaceScrollTabsUpdate();
}
@@ -6247,7 +6250,10 @@ static int wmRefreshTabs()
unsigned char* v13;
FrmImage labelFrm;
blitBufferToBufferTrans(wmGenData.tabsBackgroundFrmImage.getData() + wmGenData.tabsBackgroundFrmImage.getWidth() * wmGenData.tabsOffsetY + 9,
// CE: Skip first empty tab (original code does this in the
// `wmInterfaceInit`).
unsigned char* src = wmGenData.tabsBackgroundFrmImage.getData() + wmGenData.tabsBackgroundFrmImage.getWidth() * 27;
blitBufferToBufferTrans(src + wmGenData.tabsBackgroundFrmImage.getWidth() * wmGenData.tabsOffsetY + 9,
119,
178,
wmGenData.tabsBackgroundFrmImage.getWidth(),

View File

@@ -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)