Fix global script loading on non-Windows (#347)

* Fix global script loading on non-Windows

Previously, global scripts in .dats and in local FS didn't work due to path separator confusion.

Now:
 * Hardcode global script path to "scripts\gl*.int" (windows separator)
 * Use Windows fpattern matching inside of dFile (.dats, which always use windows separators)
 * Use \-paths when listing found global scripts

The awkward part is having to vendor a copy of fpattern to force it to use "windows mode".  It's possible that we could use this everywhere since windows allows / as separator, but that could cause bugs where we're using fpattern on the native FS.  For now keeping the dFile implementation separate makes sense.
This commit is contained in:
Mike Klaas
2026-04-01 20:20:57 -07:00
committed by GitHub
parent 8d0009aa45
commit b97c9864aa
12 changed files with 1156 additions and 64 deletions
+1
View File
@@ -434,6 +434,7 @@ endif()
add_subdirectory("third_party/fpattern")
target_link_libraries(${EXECUTABLE_NAME} fpattern::fpattern)
target_link_libraries(${EXECUTABLE_NAME} fpattern_windows::fpattern_windows)
target_link_libraries(${EXECUTABLE_NAME} ${ZLIB_LIBRARIES})
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${ZLIB_INCLUDE_DIRS})
-5
View File
@@ -197,11 +197,6 @@ NumbersInDialogue=0
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[Scripts]
;Comma-separated list of masked paths to load global scripts from
;Only use single backslash \ as the directory separator
;Paths outside of scripts folder are supported
;GlobalScriptPaths=scripts\gl_*.int,scripts\sfall\gl*.int
;Uncomment the option to specify an additional directory for ini files used by scripts
;The game will search for ini files first relative to this directory and then relative to the root directory if not found
;The path length is limited to 61 characters
+2 -14
View File
@@ -30,22 +30,10 @@ compile.exe -q -p -l -O2 -d -s -n -I<sfall_headers_id> <script_name.ssl>
- Install [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=BGforge.bgforge-mls)
## Run test script
1. Move compiled `.int` file into game folder as `data/scripts/gl_<script_name>.int`
1. Move compiled `.int` file into game folder as `scripts/gl_<script_name>.int`
2. Change `ddraw.ini` and add this section:
```ini
[Scripts]
GlobalScriptPaths=data/scripts/gl*.int
```
(or add new path using comma as separator)
Note that on non-Windows it have to be `/` as folder separator
3. Run game, check that game displays message about tests
2. Run game, check that game displays message about tests
+4 -3
View File
@@ -7,7 +7,7 @@
#include <algorithm>
#include <fpattern/fpattern.h>
#include "fpattern_windows.h"
#include "platform_compat.h"
@@ -201,9 +201,10 @@ bool dbaseClose(DBase* dbase)
// 0x4E5308
bool dbaseFindFirstEntry(DBase* dbase, DFileFindData* findFileData, const char* pattern)
{
// .dat files always have windows style paths
for (int index = 0; index < dbase->entriesLength; index++) {
DBaseEntry* entry = &(dbase->entries[index]);
if (fpattern_match(pattern, entry->path)) {
if (fpattern_windows_match(pattern, entry->path)) {
strcpy(findFileData->fileName, entry->path);
strcpy(findFileData->pattern, pattern);
findFileData->index = index;
@@ -219,7 +220,7 @@ bool dbaseFindNextEntry(DBase* dbase, DFileFindData* findFileData)
{
for (int index = findFileData->index + 1; index < dbase->entriesLength; index++) {
DBaseEntry* entry = &(dbase->entries[index]);
if (fpattern_match(findFileData->pattern, entry->path)) {
if (fpattern_windows_match(findFileData->pattern, entry->path)) {
strcpy(findFileData->fileName, entry->path);
findFileData->index = index;
return true;
+16
View File
@@ -0,0 +1,16 @@
#ifndef FALLOUT_FPATTERN_WINDOWS_H_
#define FALLOUT_FPATTERN_WINDOWS_H_
#ifdef __cplusplus
extern "C" {
#endif
int fpattern_windows_isvalid(const char* pat);
int fpattern_windows_match(const char* pat, const char* fname);
int fpattern_windows_matchn(const char* pat, const char* fname);
#ifdef __cplusplus
}
#endif
#endif
-2
View File
@@ -62,8 +62,6 @@ bool sfallConfigInit(int argc, char** argv)
configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CITIES_LIMIT_FIX, true);
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, "");
configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, "");
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PIPBOY_AVAILABLE_AT_GAMESTART, 0);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_WALK_DISTANCE, 5);
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_AUTO_OPEN_DOORS, 0);
-1
View File
@@ -71,7 +71,6 @@ namespace fallout {
#define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList"
#define SFALL_CONFIG_NUMBERS_IS_DIALOG_KEY "NumbersInDialogue"
#define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder"
#define SFALL_CONFIG_GLOBAL_SCRIPT_PATHS "GlobalScriptPaths"
#define SFALL_CONFIG_AUTO_QUICK_SAVE "AutoQuickSave"
#define SFALL_CONFIG_VERSION_STRING "VersionString"
#define SFALL_CONFIG_CONFIG_FILE "ConfigFile"
+11 -31
View File
@@ -37,39 +37,19 @@ bool sfall_gl_scr_init()
return false;
}
char* paths;
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, &paths);
char* curr = paths;
while (curr != nullptr && *curr != '\0') {
char* end = strchr(curr, ',');
if (end != nullptr) {
*end = '\0';
// CE: always use "scripts\gl*.int" as global script path
const char* scriptPath = "scripts\\gl*.int";
const char* dir = "scripts";
char** files;
int filesLength = fileNameListInit(scriptPath, &files);
if (filesLength != 0) {
for (int index = 0; index < filesLength; index++) {
char path[COMPAT_MAX_PATH];
snprintf(path, sizeof(path), "%s\\%s", dir, files[index]);
state->paths.push_back(std::string { path });
}
char drive[COMPAT_MAX_DRIVE];
char dir[COMPAT_MAX_DIR];
compat_splitpath(curr, drive, dir, nullptr, nullptr);
char** files;
int filesLength = fileNameListInit(curr, &files);
if (filesLength != 0) {
for (int index = 0; index < filesLength; index++) {
char path[COMPAT_MAX_PATH];
compat_makepath(path, drive, dir, files[index], nullptr);
state->paths.push_back(std::string { path });
}
fileNameListFree(&files, 0);
}
if (end != nullptr) {
*end = ',';
curr = end + 1;
} else {
curr = nullptr;
}
fileNameListFree(&files, 0);
}
std::sort(state->paths.begin(), state->paths.end());
+30 -8
View File
@@ -1,11 +1,33 @@
include(FetchContent)
cmake_minimum_required(VERSION 3.16)
FetchContent_Declare(fpattern
GIT_REPOSITORY "https://github.com/alexbatalov/fpattern"
GIT_TAG 8523173ec252c3b796fcdfca0fcc6329642fbbe3 # v1.9
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
EXCLUDE_FROM_ALL
project(fpattern
LANGUAGES C
VERSION "1.0.0"
)
FetchContent_MakeAvailable(fpattern)
add_library(fpattern-static src/fpattern.c)
add_library(fpattern::fpattern ALIAS fpattern-static)
if(NOT WIN32)
target_compile_definitions(fpattern-static PRIVATE
unix=1
)
endif()
target_include_directories(fpattern-static PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
add_library(fpattern-windows-static src/fpattern.c)
add_library(fpattern_windows::fpattern_windows ALIAS fpattern-windows-static)
target_compile_definitions(fpattern-windows-static PRIVATE
FPAT_WINDOWS_PATHS
fpattern_isvalid=fpattern_windows_isvalid
fpattern_match=fpattern_windows_match
fpattern_matchn=fpattern_windows_matchn
)
target_include_directories(fpattern-windows-static PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
+51
View File
@@ -0,0 +1,51 @@
/******************************************************************************
* debug.h
*
* Copyright ©1997-2015 by David R. Tribble, all rights reserved.
*/
#ifndef drt_debug_h
#define drt_debug_h 1
#ifdef __cplusplus
extern "C"
{
#endif
/* Identification */
#ifndef NO_H_IDENT
static const char drt_debug_h_id[] =
"@(#)drt/src/lib/debug.h $Revision: 1.4 $ $Date: 2001/11/12 06:00:00 $";
#endif
/*==============================================================================
* Debug macros
*/
#ifndef DEBUG
#define DEBUG 0
#endif
#if DEBUG-0 <= 0
#undef DEBUG
#define DEBUG 0
#endif
#if DEBUG
#define DL(e) (opt_debug ? (void)(e) : (void)0)
#else
#define DL(e) ((void)0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* drt_debug_h */
/* End debug.h */
+217
View File
@@ -0,0 +1,217 @@
/******************************************************************************
* fpattern.h
* Functions for matching filename patterns to filenames.
*
* Usage
* Filename patterns are composed of regular (printable) characters which
* may comprise a filename, as well as special pattern matching characters:
*
* . Matches a period (.).
* Note that a period in a filename is not treated any
* differently than any other character.
*
* ? Any.
* Matches any single character except '/' or '\'.
*
* * Closure.
* Matches zero or more occurences of any characters other
* than '/' or '\'. Leading '*' characters are allowed.
*
* SUB Substitute (control-Z).
* Similar to '*', this matches zero or more occurences of
* any characters other than '/', '\', or '.'. Leading
* '^Z' characters are allowed.
*
* [ab] Set.
* Matches the single character 'a' or 'b'.
* If the dash '-' character is to be included, it must
* immediately follow the opening bracket '['. If the
* closing bracket ']' character is to be included, it must
* be preceded by a quote '`'.
*
* [a-z] Range.
* Matches a single character in the range 'a' to 'z'.
* Ranges and sets may be combined within the same set of
* brackets.
*
* [!R] Exclusive range.
* Matches a single character not in the range 'R'.
* If range 'R' includes the dash '-' character, the dash
* must immediately follow the caret '!'.
*
* ! Not.
* Makes the following pattern (up to the next '/') match
* any filename except those what it would normally match.
*
* / Path separator (UNIX and DOS).
* Matches a '/' or '\' pathname (directory) separator.
* Multiple separators are treated like a single separator.
* A leading separator indicates an absolute pathname.
*
* \ Path separator (DOS).
* Same as the '/' character. Note that this character
* must be escaped if used within string constants ("\\").
*
* \ Quote (UNIX).
* Makes the next character a regular (nonspecial)
* character. Note that to match the quote character
* itself, it must be quoted. Note that this character
* must be escaped if used within string constants ("\\").
*
* ` Quote (DOS).
* Makes the next character a regular (nonspecial)
* character. Note that to match the quote character
* itself, it must be quoted.
*
* Upper and lower case alphabetic characters are considered identical,
* i.e., 'a' and 'A' match each other. (What constitutes a lowercase
* letter depends on the current locale settings.)
*
* Spaces and control characters are treated as normal characters.
*
* Examples
* The following patterns in the left column will match the filenames in
* the middle column and will not match filenames in the right column:
*
* Pattern Will Match Will Not Match
* ------- ---------- --------------
* a a (only) (anything else)
* a. a. (only) (anything else)
* a?c abc, acc, arc, a.c a, ac, abbc
* a*c ac, abc, abbc, acc, a.c a, ab, acb, bac
* a* a, ab, abb, a., a.b b, ba
* * a, ab, abb, a., .foo, a.foo (nothing)
* *. a., ab., abb., a.foo. a, ab, a.foo, .foo
* *.* a., a.b, ah.bc.foo a
* ^Z a, ab, abb a., .foo, a.foo
* ^Z. a., ab., abb. a, .foo, a.foo
* ^Z.* a, a., .foo, a.foo ab, abb
* *2.c 2.c, 12.c, foo2.c, foo.12.c 2x.c
* a[b-z]c abc, acc, azc (only) (anything else)
* [ab0-9]x ax, bx, 0x, 9x zx
* a[-.]b a-b, a.b (only) (anything else)
* a[!a-z]b a0b, a.b, a@b aab, azb, aa0b
* a[!-b]x a0x, a+x, acx a-x, abx, axxx
* a[-!b]x a-x, a!x, abx (only) (anything else)
* a[`]]x a]x (only) (anything else)
* a``x a`x (only) (anything else)
* oh`! oh! (only) (anything else)
* is`?it is?it (only) (anything else)
* !a?c a, ac, ab, abb, acb, a.foo abc, a.c, azc
*
* History
* 1.0, 1997-01-03, David Tribble.
* First cut.
*
* 1.1, 1997-01-03, David Tribble.
* Added '^Z' pattern character.
* Added fpattern_matchn().
*
* 1.2, 1997-01-26, David Tribble.
* Changed range negation character from '^' to '!', ala Unix.
*
* 1.3, 1997-08-02, David Tribble.
* Added 'FPAT_XXX' macro constants.
*
* 1.4, 2001-11-21, David Tribble.
* Revised slightly for Win32 compilations.
*
* Limitations
* This code is copyrighted by the author, but permission is hereby granted
* for its unlimited use provided that the original copyright and
* authorship notices are retained intact.
*
* Queries about this source code can be sent to <david@tribble.com>.
*
* Copyright ©1997-2001 by David R. Tribble, all rights reserved.
*/
#ifndef drt_fpattern_h
#define drt_fpattern_h 1
#ifdef __cplusplus
extern "C"
{
#endif
/* Identification */
#ifndef NO_H_IDENT
static const char drt_fpattern_h_id[] =
"@(#)drt/src/lib/fpattern.h $Revision: 1.4 $ $Date: 2001/11/12 06:00:00 $";
#endif
/* Manifest constants */
#define FPAT_QUOTE '\\' /* Quotes a special char */
#define FPAT_QUOTE2 '`' /* Quotes a special char */
#define FPAT_DEL '/' /* Path delimiter */
#define FPAT_DEL2 '\\' /* Path delimiter */
#define FPAT_DOT '.' /* Dot char */
#define FPAT_NOT '!' /* Exclusion */
#define FPAT_ANY '?' /* Any one char */
#define FPAT_CLOS '*' /* Zero or more chars */
#define FPAT_CLOSP '\x1A' /* Zero or more nondelimiters */
#define FPAT_SET_L '[' /* Set/range open bracket */
#define FPAT_SET_R ']' /* Set/range close bracket */
#define FPAT_SET_NOT '!' /* Set exclusion */
#define FPAT_SET_THRU '-' /* Set range of chars */
/* Model-dependent extern aliases */
#ifdef __MSDOS__
#if defined(__SMALL__)
#define fpattern_isvalid Sfpattern_isvalid
#define fpattern_match Sfpattern_match
#define fpattern_matchn Sfpattern_matchn
#elif defined(__LARGE__)
#define fpattern_isvalid Lfpattern_isvalid
#define fpattern_match Lfpattern_match
#define fpattern_matchn Lfpattern_matchn
#elif defined(__COMPACT__)
#define fpattern_isvalid Cfpattern_isvalid
#define fpattern_match Cfpattern_match
#define fpattern_matchn Cfpattern_matchn
#elif defined(__MEDIUM__)
#define fpattern_isvalid Mfpattern_isvalid
#define fpattern_match Mfpattern_match
#define fpattern_matchn Mfpattern_matchn
#elif defined(__HUGE__)
#define fpattern_isvalid Hfpattern_isvalid
#define fpattern_match Hfpattern_match
#define fpattern_matchn Hfpattern_matchn
#elif defined(__TINY__)
#define fpattern_isvalid Tfpattern_isvalid
#define fpattern_match Tfpattern_match
#define fpattern_matchn Tfpattern_matchn
#else
/* Memory model is not defined, use extern names as is. */
#endif
#endif /* __MSDOS__ */
/* Public variables */
/* (None) */
/* Public functions */
extern int fpattern_isvalid(const char *pat);
extern int fpattern_match(const char *pat, const char *fname);
extern int fpattern_matchn(const char *pat, const char *fname);
#ifdef __cplusplus
}
#endif
#endif /* drt_fpattern_h */
/* End fpattern.h */
File diff suppressed because it is too large Load Diff