From fe4ae03ac2e90041d7f580e44da98dcc5d48a9f8 Mon Sep 17 00:00:00 2001 From: ianpatt Date: Thu, 21 Mar 2024 23:24:41 -0700 Subject: [PATCH] make loader version-independent --- sfse/CMakeLists.txt | 4 +- sfse/sfse.cpp | 8 ++ sfse/sfse.def | 3 +- {sfse_common => sfse}/sfse_version.rc | 64 ++++++------- sfse_common/CMakeLists.txt | 2 - sfse_common/CoreInfo.h | 15 +++ sfse_common/sfse_version.h | 3 + sfse_loader/CMakeLists.txt | 12 +-- sfse_loader/IdentifyEXE.cpp | 130 ++++++++++++++------------ sfse_loader/IdentifyEXE.h | 9 +- sfse_loader/LoaderError.cpp | 3 +- sfse_loader/main.cpp | 46 ++++++++- sfse_loader/sfse_loader_version.rc | 30 ++++++ sfse_whatsnew.txt | 3 + 14 files changed, 221 insertions(+), 111 deletions(-) rename {sfse_common => sfse}/sfse_version.rc (77%) create mode 100644 sfse_common/CoreInfo.h create mode 100644 sfse_loader/sfse_loader_version.rc diff --git a/sfse/CMakeLists.txt b/sfse/CMakeLists.txt index ae5f78b..33c0610 100644 --- a/sfse/CMakeLists.txt +++ b/sfse/CMakeLists.txt @@ -44,7 +44,7 @@ source_group( FILES sfse.cpp sfse.def - ${sfse_common_SOURCE_DIR}/sfse_version.rc + sfse_version.rc ) source_group( @@ -145,7 +145,7 @@ add_library( ${headers} ${sources} sfse.def - ${sfse_common_SOURCE_DIR}/sfse_version.rc + sfse_version.rc ) add_library(sfse64::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) diff --git a/sfse/sfse.cpp b/sfse/sfse.cpp index f54bf62..4d90a77 100644 --- a/sfse/sfse.cpp +++ b/sfse/sfse.cpp @@ -6,6 +6,7 @@ #include "sfse_common/Utilities.h" #include "sfse_common/SafeWrite.h" #include "sfse_common/BranchTrampoline.h" +#include "sfse_common/CoreInfo.h" #include "PluginManager.h" #include "Hooks_Version.h" @@ -175,4 +176,11 @@ extern "C" { return TRUE; } + + __declspec(dllexport) SFSECoreVersionData SFSECore_Version = + { + SFSECoreVersionData::kVersion, + + RUNTIME_VERSION, + }; } diff --git a/sfse/sfse.def b/sfse/sfse.def index 3604ffa..54e59f8 100644 --- a/sfse/sfse.def +++ b/sfse/sfse.def @@ -1,2 +1,3 @@ EXPORTS -StartSFSE @1 \ No newline at end of file +StartSFSE @1 +SFSECore_Version diff --git a/sfse_common/sfse_version.rc b/sfse/sfse_version.rc similarity index 77% rename from sfse_common/sfse_version.rc rename to sfse/sfse_version.rc index 91d1309..1ee1e79 100644 --- a/sfse_common/sfse_version.rc +++ b/sfse/sfse_version.rc @@ -1,32 +1,32 @@ -#include "sfse_version.h" - -1 VERSIONINFO - FILEVERSION 0,SFSE_VERSION_INTEGER,SFSE_VERSION_INTEGER_MINOR,SFSE_VERSION_INTEGER_BETA - PRODUCTVERSION 0,SFSE_VERSION_INTEGER,SFSE_VERSION_INTEGER_MINOR,SFSE_VERSION_INTEGER_BETA - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "FileDescription", "A component of the Starfield Script Extender" - VALUE "FileVersion", SFSE_VERSION_VERSTRING - VALUE "InternalName", "SFSE" - VALUE "LegalCopyright", "Copyright (C) 2006-2023" - VALUE "ProductName", "SFSE" - VALUE "ProductVersion", SFSE_VERSION_VERSTRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END +#include "sfse_common/sfse_version.h" + +1 VERSIONINFO + FILEVERSION 0,SFSE_VERSION_INTEGER,SFSE_VERSION_INTEGER_MINOR,SFSE_VERSION_INTEGER_BETA + PRODUCTVERSION 0,SFSE_VERSION_INTEGER,SFSE_VERSION_INTEGER_MINOR,SFSE_VERSION_INTEGER_BETA + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Starfield Script Extender core DLL" + VALUE "FileVersion", SFSE_VERSION_VERSTRING + VALUE "InternalName", "SFSE" + VALUE "LegalCopyright", "Copyright (C) 2006-2024" + VALUE "ProductName", "SFSE" + VALUE "ProductVersion", SFSE_VERSION_VERSTRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/sfse_common/CMakeLists.txt b/sfse_common/CMakeLists.txt index e759bbf..eaa66fb 100644 --- a/sfse_common/CMakeLists.txt +++ b/sfse_common/CMakeLists.txt @@ -29,7 +29,6 @@ source_group( FILES ${headers} ${sources} - sfse_version.rc ) # ---- Create library ---- @@ -39,7 +38,6 @@ add_library( STATIC ${headers} ${sources} - sfse_version.rc ) add_library(sfse::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) diff --git a/sfse_common/CoreInfo.h b/sfse_common/CoreInfo.h new file mode 100644 index 0000000..f1be52d --- /dev/null +++ b/sfse_common/CoreInfo.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct SFSECoreVersionData +{ + enum + { + kVersion = 1, + }; + + std::uint32_t dataVersion; + + std::uint32_t runtimeVersion; +}; diff --git a/sfse_common/sfse_version.h b/sfse_common/sfse_version.h index 2e60ac1..de40b3a 100644 --- a/sfse_common/sfse_version.h +++ b/sfse_common/sfse_version.h @@ -60,4 +60,7 @@ #error unknown runtime type #endif +#define LOADER_VERSION_1_0_0 MAKE_EXE_VERSION(1, 0, 0) +#define LOADER_VERSION LOADER_VERSION_1_0_0 + #endif /* __SFSE_VERSION_H__ */ diff --git a/sfse_loader/CMakeLists.txt b/sfse_loader/CMakeLists.txt index 6675a3d..f1681f9 100644 --- a/sfse_loader/CMakeLists.txt +++ b/sfse_loader/CMakeLists.txt @@ -6,7 +6,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/versioning.cmake) project( sfse_loader - VERSION ${SFSE_VERSION_MAJOR}.${SFSE_VERSION_MINOR}.${SFSE_VERSION_PATCH} + VERSION 1.0.0 LANGUAGES CXX ) @@ -40,7 +40,7 @@ source_group( FILES ${headers} ${sources} - ${sfse_common_SOURCE_DIR}/sfse_version.rc + sfse_loader_version.rc sfse_loader.manifest ) @@ -50,7 +50,7 @@ add_executable( ${PROJECT_NAME} ${headers} ${sources} - ${sfse_common_SOURCE_DIR}/sfse_version.rc + sfse_loader_version.rc sfse_loader.manifest ) @@ -58,12 +58,6 @@ add_executable(sfse::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/configuration.cmake) -target_compile_definitions( - ${PROJECT_NAME} - PRIVATE - RUNTIME_VERSION=${RUNTIME_VERSION_PACKED} -) - target_compile_features( ${PROJECT_NAME} PUBLIC diff --git a/sfse_loader/IdentifyEXE.cpp b/sfse_loader/IdentifyEXE.cpp index 70a07a2..9781699 100644 --- a/sfse_loader/IdentifyEXE.cpp +++ b/sfse_loader/IdentifyEXE.cpp @@ -291,6 +291,7 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, _MESSAGE("product name = %s", productName.c_str()); hookInfo->version = version; + hookInfo->packedVersion = MAKE_EXE_VERSION(version >> 48, version >> 32, version >> 16); if(productName == "SFSE") { @@ -331,16 +332,78 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, bool result = false; + if(isEditor) + { + switch(hookInfo->procType) + { + case kProcType_Steam: + case kProcType_Normal: + case kProcType_WinStore: + case kProcType_GOG: + *dllSuffix = ""; + + result = true; + + break; + case kProcType_Unknown: + default: + PrintLoaderError("Unsupported editor executable type."); + break; + } + } + else + { + char versionStr[256]; + sprintf_s(versionStr, "%d_%d_%d", hookInfo->getVersionMajor(), hookInfo->getVersionMinor(), hookInfo->getVersionBuild()); + *dllSuffix = versionStr; + + switch(hookInfo->procType) + { + case kProcType_Steam: + case kProcType_Normal: + result = true; + + break; + + case kProcType_GOG: + *dllSuffix += "_gog"; + + result = true; + break; + + case kProcType_WinStore: + *dllSuffix += "_winstore"; + + result = true; + + break; + + case kProcType_Packed: + PrintLoaderError("Packed versions of Starfield are not supported."); + break; + + case kProcType_Unknown: + default: + PrintLoaderError("Unknown executable type."); + break; + } + } + + return result; +} + +bool VersionCheck(const ProcHookInfo & procInfo, u64 RUNTIME_VERSION) +{ const u64 kCurVersion = (u64(GET_EXE_VERSION_MAJOR(RUNTIME_VERSION)) << 48) | (u64(GET_EXE_VERSION_MINOR(RUNTIME_VERSION)) << 32) | (u64(GET_EXE_VERSION_BUILD(RUNTIME_VERSION)) << 16); // convert version resource to internal version format - u32 versionInternal = MAKE_EXE_VERSION(version >> 48, version >> 32, version >> 16); + u32 versionInternal = MAKE_EXE_VERSION(procInfo.version >> 48, procInfo.version >> 32, procInfo.version >> 16); // version mismatch could mean exe type mismatch - if(version != kCurVersion) + if(procInfo.version != kCurVersion) { #if GET_EXE_VERSION_SUB(RUNTIME_VERSION) == RUNTIME_TYPE_BETHESDA const int expectedProcType = kProcType_Steam; @@ -355,7 +418,7 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, // we only care about steam/gog for this check const char * foundProcTypeName = nullptr; - switch(hookInfo->procType) + switch(procInfo.procType) { case kProcType_Steam: foundProcTypeName = "Steam"; @@ -366,7 +429,7 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, break; } - if(foundProcTypeName && (hookInfo->procType != expectedProcType)) + if(foundProcTypeName && (procInfo.procType != expectedProcType)) { // different build PrintLoaderError( @@ -382,7 +445,7 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, } } - if(version < kCurVersion) + if(procInfo.version < kCurVersion) { #if SFSE_TARGETING_BETA_VERSION if(versionInternal == CURRENT_RELEASE_RUNTIME) @@ -402,7 +465,7 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, SFSE_VERSION_INTEGER, SFSE_VERSION_INTEGER_MINOR, SFSE_VERSION_INTEGER_BETA); #endif } - else if(version > kCurVersion) + else if(procInfo.version > kCurVersion) { PrintLoaderError( "You are using a newer version of Starfield than this version of SFSE supports.\n" @@ -414,59 +477,6 @@ bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, GET_EXE_VERSION_MAJOR(versionInternal), GET_EXE_VERSION_MINOR(versionInternal), GET_EXE_VERSION_BUILD(versionInternal), SFSE_VERSION_INTEGER, SFSE_VERSION_INTEGER_MINOR, SFSE_VERSION_INTEGER_BETA); } - else if(isEditor) - { - switch(hookInfo->procType) - { - case kProcType_Steam: - case kProcType_Normal: - case kProcType_WinStore: - case kProcType_GOG: - *dllSuffix = ""; - - result = true; - - break; - case kProcType_Unknown: - default: - PrintLoaderError("Unsupported editor executable type."); - break; - } - } - else - { - char versionStr[256]; - sprintf_s(versionStr, "%d_%d_%d", GET_EXE_VERSION_MAJOR(versionInternal), GET_EXE_VERSION_MINOR(versionInternal), GET_EXE_VERSION_BUILD(versionInternal)); - - switch(hookInfo->procType) - { - case kProcType_Steam: - case kProcType_Normal: - case kProcType_GOG: - *dllSuffix = versionStr; - - result = true; - - break; - - case kProcType_WinStore: - *dllSuffix = versionStr; - *dllSuffix += "_winstore"; - - result = true; - - break; - - case kProcType_Packed: - PrintLoaderError("Packed versions of Starfield are not supported."); - break; - - case kProcType_Unknown: - default: - PrintLoaderError("Unknown executable type."); - break; - } - } - return result; + return true; } diff --git a/sfse_loader/IdentifyEXE.h b/sfse_loader/IdentifyEXE.h index 6986f85..1ad87a8 100644 --- a/sfse_loader/IdentifyEXE.h +++ b/sfse_loader/IdentifyEXE.h @@ -18,8 +18,13 @@ enum struct ProcHookInfo { - u64 version; - u32 procType; + u64 version; // version from resource + u32 packedVersion; // internal packed version number + u32 procType; // kProcType_* + + u16 getVersionMajor() { return u16(version >> 48); } + u16 getVersionMinor() { return u16(version >> 32); } + u16 getVersionBuild() { return u16(version >> 16); } }; bool IdentifyEXE(const char * procName, bool isEditor, std::string * dllSuffix, ProcHookInfo * hookInfo); diff --git a/sfse_loader/LoaderError.cpp b/sfse_loader/LoaderError.cpp index bb72923..d8cd004 100644 --- a/sfse_loader/LoaderError.cpp +++ b/sfse_loader/LoaderError.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "sfse_common/Log.h" void PrintLoaderError(const char * fmt, ...) { @@ -10,7 +11,7 @@ void PrintLoaderError(const char * fmt, ...) va_list args; va_start(args, fmt); -// gLog.Log(IDebugLog::kLevel_FatalError, fmt, args); + DebugLog::log(DebugLog::kLevel_FatalError, fmt, args); va_end(args); va_start(args, fmt); diff --git a/sfse_loader/main.cpp b/sfse_loader/main.cpp index 25f5c87..3dc9a99 100644 --- a/sfse_loader/main.cpp +++ b/sfse_loader/main.cpp @@ -6,6 +6,7 @@ #include "sfse_common/sfse_version.h" #include "sfse_common/Utilities.h" #include "sfse_common/FileStream.h" +#include "sfse_common/CoreInfo.h" #include "LoaderError.h" #include "IdentifyEXE.h" #include "Inject.h" @@ -22,7 +23,7 @@ int main(int argc, char ** argv) GetSystemTime(&now); _MESSAGE("SFSE loader: initialize (version = %d.%d.%d %08X %04d-%02d-%02d %02d:%02d:%02d, os = %s)", - SFSE_VERSION_INTEGER, SFSE_VERSION_INTEGER_MINOR, SFSE_VERSION_INTEGER_BETA, RUNTIME_VERSION, + SFSE_VERSION_INTEGER, SFSE_VERSION_INTEGER_MINOR, SFSE_VERSION_INTEGER_BETA, LOADER_VERSION, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, getOSInfoStr().c_str()); @@ -182,7 +183,48 @@ int main(int argc, char ** argv) if(!tempFile.open(dllPath.c_str())) { - PrintLoaderError("Couldn't find SFSE DLL (%s). Please make sure you have installed SFSE correctly and are running it from your Starfield folder.", dllPath.c_str()); + PrintLoaderError( + "Couldn't find SFSE DLL (%s).\n" + "Either you have not installed SFSE correctly, or a new version of Starfield has been released.\n" + "Please make sure you have installed SFSE correctly and are running it from your Starfield folder.\n" + "If a game patch was released since you last ran the game, please check the website for updates.\n" + "Runtime: %d.%d.%d", dllPath.c_str(), procHookInfo.getVersionMajor(), procHookInfo.getVersionMinor(), procHookInfo.getVersionBuild()); + return 1; + } + } + + // check to make sure the dll makes sense + { + bool dllOK = false; + u32 dllVersion = 0; + + HMODULE resourceHandle = (HMODULE)LoadLibraryEx(dllPath.c_str(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if(resourceHandle) + { + if(is64BitDLL(resourceHandle)) + { + auto * version = (const SFSECoreVersionData *)getResourceLibraryProcAddress(resourceHandle, "SFSECore_Version"); + if(version) + { + dllVersion = version->runtimeVersion; + + if( (version->dataVersion == SFSECoreVersionData::kVersion) && + (version->runtimeVersion == procHookInfo.packedVersion)) + { + dllOK = true; + } + } + } + + FreeLibrary(resourceHandle); + } + + if(!dllOK) + { + PrintLoaderError( + "Bad SFSE DLL (%s).\n" + "Do not rename files; it will not magically make anything work.\n" + "%08X %08X", dllPath.c_str(), procHookInfo.packedVersion, dllVersion); return 1; } } diff --git a/sfse_loader/sfse_loader_version.rc b/sfse_loader/sfse_loader_version.rc new file mode 100644 index 0000000..eae5831 --- /dev/null +++ b/sfse_loader/sfse_loader_version.rc @@ -0,0 +1,30 @@ +1 VERSIONINFO + FILEVERSION 0,1,0,0 + PRODUCTVERSION 0,1,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Starfield Script Extender (SFSE) Loader" + VALUE "FileVersion", "1.0.0" + VALUE "InternalName", "SFSE" + VALUE "LegalCopyright", "Copyright (C) 2006-2024" + VALUE "ProductName", "SFSE" + VALUE "ProductVersion", "1.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/sfse_whatsnew.txt b/sfse_whatsnew.txt index f545c4f..b5edbfb 100644 --- a/sfse_whatsnew.txt +++ b/sfse_whatsnew.txt @@ -1,3 +1,6 @@ +0.2.6 +- the loader has been made version-independent due to continued problems with false positives. it should not need to be updated. this means that we lose out on a bit of diagnostic information (the loader no longer knows the latest version of the game). feel free to blame microsoft for this. + 0.2.5 - support for 1.10.31