From b7a4bda3d6b2c18fad834ea768333656a195ff26 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Sat, 14 Jun 2025 22:58:12 +0200 Subject: [PATCH 1/3] [GEN][ZH] Prevent AMD/ATI driver crash on game launch by temporarily unloading and renaming dbghelp.dll --- CMakeLists.txt | 1 - .../Source/WWVegas/WWLib/CMakeLists.txt | 7 + .../Source/WWVegas/WWLib/DbgHelpGuard.cpp | 63 ++++ .../Source/WWVegas/WWLib/DbgHelpGuard.h | 47 +++ .../Source/WWVegas/WWLib/DbgHelpLoader.cpp | 283 ++++++++++++++++++ .../Source/WWVegas/WWLib/DbgHelpLoader.h | 187 ++++++++++++ .../Source/WWVegas/WWLib/MallocAllocator.h | 129 ++++++++ .../WWVegas/WWLib/ScopedFileRenamer.cpp | 98 ++++++ .../Source/WWVegas/WWLib/ScopedFileRenamer.h | 47 +++ Core/Tools/ImagePacker/CMakeLists.txt | 1 - Core/Tools/MapCacheBuilder/CMakeLists.txt | 1 - Core/Tools/PATCHGET/CMakeLists.txt | 1 - .../Source/Common/System/StackDump.cpp | 122 ++++---- .../Source/WWVegas/WW3D2/dx8wrapper.cpp | 32 +- Generals/Code/Main/CMakeLists.txt | 1 - Generals/Code/Tools/GUIEdit/CMakeLists.txt | 1 - .../Code/Tools/ParticleEditor/CMakeLists.txt | 1 - Generals/Code/Tools/W3DView/CMakeLists.txt | 1 - .../Code/Tools/WorldBuilder/CMakeLists.txt | 1 - .../Source/Common/System/StackDump.cpp | 122 ++++---- .../Source/WWVegas/WW3D2/dx8wrapper.cpp | 32 +- GeneralsMD/Code/Main/CMakeLists.txt | 1 - GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt | 1 - .../Code/Tools/ParticleEditor/CMakeLists.txt | 1 - GeneralsMD/Code/Tools/W3DView/CMakeLists.txt | 1 - .../Code/Tools/WorldBuilder/CMakeLists.txt | 1 - GeneralsMD/Code/Tools/wdump/CMakeLists.txt | 1 - cmake/dbghelp.cmake | 7 - 28 files changed, 1007 insertions(+), 184 deletions(-) create mode 100644 Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp create mode 100644 Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h create mode 100644 Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp create mode 100644 Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h create mode 100644 Core/Libraries/Source/WWVegas/WWLib/MallocAllocator.h create mode 100644 Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp create mode 100644 Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h delete mode 100644 cmake/dbghelp.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b9bb8bf6b..7ef78f39bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,6 @@ if((WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows") AND ${CMAKE_SIZEOF_VOID_P} EQU include(cmake/miles.cmake) include(cmake/bink.cmake) include(cmake/dx8.cmake) - include(cmake/dbghelp.cmake) endif() # Define a dummy stlport target when not on VC6. diff --git a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt index 88a0c0b3ab..ad507d6b68 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt +++ b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt @@ -32,6 +32,10 @@ set(WWLIB_SRC #crcstraw.h cstraw.cpp cstraw.h + DbgHelpGuard.cpp + DbgHelpGuard.h + DbgHelpLoader.cpp + DbgHelpLoader.h Except.cpp Except.h FastAllocator.cpp @@ -66,6 +70,7 @@ set(WWLIB_SRC #lzostraw.cpp #lzostraw.h #lzo_conf.h + MallocAllocator.h #md5.cpp #md5.h mempool.h @@ -98,6 +103,8 @@ set(WWLIB_SRC #regexpr.cpp #regexpr.h #search.h + ScopedFileRenamer.cpp + ScopedFileRenamer.h sharebuf.h Signaler.h simplevec.h diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp new file mode 100644 index 0000000000..9a0acc8214 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp @@ -0,0 +1,63 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "DbgHelpGuard.h" + +#include "DbgHelpLoader.h" + + +DbgHelpGuard::DbgHelpGuard() +{ + deactivate(); +} + +DbgHelpGuard::~DbgHelpGuard() +{ + reactivate(); +} + +void DbgHelpGuard::deactivate() +{ + DbgHelpLoader::blockLoad(); + + if (DbgHelpLoader::isLoadedFromSystem()) + { + // This is ok. Do nothing. + } + else if (DbgHelpLoader::isLoaded()) + { + DbgHelpLoader::unload(); + m_wasLoaded = true; + m_dbgHelpRenamer.rename("dbghelp.dll", "dbghelp.dll.bak"); + } + else + { + m_dbgHelpRenamer.rename("dbghelp.dll", "dbghelp.dll.bak"); + } +} + +void DbgHelpGuard::reactivate() +{ + m_dbgHelpRenamer.revert(); + DbgHelpLoader::unblockLoad(); + + if (m_wasLoaded) + { + DbgHelpLoader::load(); + } +} diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h new file mode 100644 index 0000000000..243f4ed1cf --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h @@ -0,0 +1,47 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#pragma once + +#include "always.h" + +#include "ScopedFileRenamer.h" + + +// This class temporarily unloads dbghelp.dll and prevents it from loading during its lifetime. +// This helps avoid crashing on boot using recent AMD/ATI drivers, which attempt to load and use +// dbghelp.dll from the game install directory but are unable to do so correctly because +// the dbghelp.dll that ships with the game is very old and the AMD/ATI code does not handle +// that correctly. This workaround is not required if the dbghelp.dll was loaded from the system +// directory. + +class DbgHelpGuard +{ +public: + + DbgHelpGuard(); + ~DbgHelpGuard(); + + void deactivate(); + void reactivate(); + +private: + + ScopedFileRenamer m_dbgHelpRenamer; + bool m_wasLoaded; +}; diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp new file mode 100644 index 0000000000..b99ae06be7 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp @@ -0,0 +1,283 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "DbgHelpLoader.h" + + +int DbgHelpLoader::BlockLoadCounter = 0; +DbgHelpLoader* DbgHelpLoader::Inst = NULL; + +DbgHelpLoader::DbgHelpLoader() + : m_symInitialize(NULL) + , m_symCleanup(NULL) + , m_symLoadModule(NULL) + , m_symUnloadModule(NULL) + , m_symGetModuleBase(NULL) + , m_symGetSymFromAddr(NULL) + , m_symGetLineFromAddr(NULL) + , m_symSetOptions(NULL) + , m_symFunctionTableAccess(NULL) + , m_stackWalk(NULL) + , m_dllModule(HMODULE(0)) + , m_failed(false) + , m_loadedFromSystem(false) +{ +} + +DbgHelpLoader::~DbgHelpLoader() +{ +} + +bool DbgHelpLoader::isLoaded() +{ + return Inst != NULL && Inst->m_dllModule != HMODULE(0); +} + +bool DbgHelpLoader::isLoadedFromSystem() +{ + return Inst != NULL && Inst->m_loadedFromSystem; +} + +void DbgHelpLoader::blockLoad() +{ + ++BlockLoadCounter; +} + +bool DbgHelpLoader::unblockLoad() +{ + return --BlockLoadCounter == 0; +} + +bool DbgHelpLoader::load() +{ + if (BlockLoadCounter > 0) + return false; + + if (Inst == NULL) + { + // Cannot use new/delete here when this is loaded during game memory initialization. + void* p = ::malloc(sizeof(DbgHelpLoader)); + Inst = new (p) DbgHelpLoader(); + } + + // Optimization: return early if it failed before. + if (Inst->m_failed) + return false; + + // Try load dbghelp.dll from the system directory first. + char dllFilename[MAX_PATH]; + ::GetSystemDirectoryA(dllFilename, ARRAY_SIZE(dllFilename)); + strcat(dllFilename, "\\dbghelp.dll"); + + Inst->m_dllModule = ::LoadLibraryA(dllFilename); + if (Inst->m_dllModule == HMODULE(0)) + { + // Not found. Try load dbghelp.dll from the work directory. + Inst->m_dllModule = ::LoadLibraryA("dbghelp.dll"); + if (Inst->m_dllModule == HMODULE(0)) + { + Inst->m_failed = true; + return false; + } + } + else + { + Inst->m_loadedFromSystem = true; + } + + Inst->m_symInitialize = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymInitialize")); + Inst->m_symCleanup = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymCleanup")); + Inst->m_symLoadModule = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymLoadModule")); + Inst->m_symUnloadModule = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymUnloadModule")); + Inst->m_symGetModuleBase = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymGetModuleBase")); + Inst->m_symGetSymFromAddr = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymGetSymFromAddr")); + Inst->m_symGetLineFromAddr = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymGetLineFromAddr")); + Inst->m_symSetOptions = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymSetOptions")); + Inst->m_symFunctionTableAccess = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "SymFunctionTableAccess")); + Inst->m_stackWalk = reinterpret_cast(::GetProcAddress(Inst->m_dllModule, "StackWalk")); + + if (Inst->m_symInitialize == NULL || Inst->m_symCleanup == NULL) + { + unload(); + Inst->m_failed = true; + return false; + } + + return true; +} + +bool DbgHelpLoader::reload() +{ + unload(); + return load(); +} + +void DbgHelpLoader::unload() +{ + if (Inst == NULL) + return; + + while (!Inst->m_initializedProcesses.empty()) + { + symCleanup(*Inst->m_initializedProcesses.begin()); + } + + if (Inst->m_dllModule != HMODULE(0)) + { + ::FreeLibrary(Inst->m_dllModule); + Inst->m_dllModule = HMODULE(0); + } + + Inst->~DbgHelpLoader(); + ::free(Inst); + Inst = NULL; +} + +BOOL DbgHelpLoader::symInitialize( + HANDLE hProcess, + LPSTR UserSearchPath, + BOOL fInvadeProcess) +{ + if (Inst == NULL) + return FALSE; + + if (Inst->m_initializedProcesses.find(hProcess) != Inst->m_initializedProcesses.end()) + return FALSE; + + if (Inst->m_symInitialize) + { + if (Inst->m_symInitialize(hProcess, UserSearchPath, fInvadeProcess) != FALSE) + { + Inst->m_initializedProcesses.insert(hProcess); + return TRUE; + } + } + + return FALSE; +} + +BOOL DbgHelpLoader::symCleanup( + HANDLE hProcess) +{ + if (Inst == NULL) + return FALSE; + + // @todo stl::find_and_erase + Processes::iterator it = Inst->m_initializedProcesses.find(hProcess); + if (it != Inst->m_initializedProcesses.end()) + Inst->m_initializedProcesses.erase(it); + + if (Inst->m_symCleanup) + return Inst->m_symCleanup(hProcess); + + return FALSE; +} + +BOOL DbgHelpLoader::symLoadModule( + HANDLE hProcess, + HANDLE hFile, + LPSTR ImageName, + LPSTR ModuleName, + DWORD BaseOfDll, + DWORD SizeOfDll) +{ + if (Inst != NULL && Inst->m_symLoadModule) + return Inst->m_symLoadModule(hProcess, hFile, ImageName, ModuleName, BaseOfDll, SizeOfDll); + + return FALSE; +} + +DWORD DbgHelpLoader::symGetModuleBase( + HANDLE hProcess, + DWORD dwAddr) +{ + if (Inst != NULL && Inst->m_symGetModuleBase) + return Inst->m_symGetModuleBase(hProcess, dwAddr); + + return 0u; +} + +BOOL DbgHelpLoader::symUnloadModule( + HANDLE hProcess, + DWORD BaseOfDll) +{ + if (Inst != NULL && Inst->m_symUnloadModule) + return Inst->m_symUnloadModule(hProcess, BaseOfDll); + + return FALSE; +} + +BOOL DbgHelpLoader::symGetSymFromAddr( + HANDLE hProcess, + DWORD Address, + LPDWORD Displacement, + PIMAGEHLP_SYMBOL Symbol) +{ + if (Inst != NULL && Inst->m_symGetSymFromAddr) + return Inst->m_symGetSymFromAddr(hProcess, Address, Displacement, Symbol); + + return FALSE; +} + +BOOL DbgHelpLoader::symGetLineFromAddr( + HANDLE hProcess, + DWORD dwAddr, + PDWORD pdwDisplacement, + PIMAGEHLP_LINE Line) +{ + if (Inst != NULL && Inst->m_symGetLineFromAddr) + return Inst->m_symGetLineFromAddr(hProcess, dwAddr, pdwDisplacement, Line); + + return FALSE; +} + +DWORD DbgHelpLoader::symSetOptions( + DWORD SymOptions) +{ + if (Inst != NULL && Inst->m_symSetOptions) + return Inst->m_symSetOptions(SymOptions); + + return 0u; +} + +LPVOID DbgHelpLoader::symFunctionTableAccess( + HANDLE hProcess, + DWORD AddrBase) +{ + if (Inst != NULL && Inst->m_symFunctionTableAccess) + return Inst->m_symFunctionTableAccess(hProcess, AddrBase); + + return NULL; +} + +BOOL DbgHelpLoader::stackWalk( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME StackFrame, + LPVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE TranslateAddress) +{ + if (Inst != NULL && Inst->m_stackWalk) + return Inst->m_stackWalk(MachineType, hProcess, hThread, StackFrame, ContextRecord, ReadMemoryRoutine, FunctionTableAccessRoutine, GetModuleBaseRoutine, TranslateAddress); + + return FALSE; +} diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h new file mode 100644 index 0000000000..8b6079e3c8 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h @@ -0,0 +1,187 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#pragma once + +#include "always.h" + +#include +#include // Must be included after Windows.h +#include + +#include "MallocAllocator.h" + +// This static class can load and unload dbghelp.dll +// Internally it must not use new and delete because it can be created during game memory initialization. + +class DbgHelpLoader +{ +private: + + static int BlockLoadCounter; + static DbgHelpLoader* Inst; // Is singleton class + + DbgHelpLoader(); + ~DbgHelpLoader(); + +public: + + // Returns whether dbghelp.dll is loaded + static bool isLoaded(); + + // Returns whether dbghelp.dll is loaded from the system directory + static bool isLoadedFromSystem(); + + // Blocks loading a dbghelp.dll + static void blockLoad(); + + // Unblocks loading a dbghelp.dll. Returns true if unblocked. + static bool unblockLoad(); + + static bool load(); + static bool reload(); + static void unload(); + + static BOOL WINAPI symInitialize( + HANDLE hProcess, + LPSTR UserSearchPath, + BOOL fInvadeProcess); + + static BOOL WINAPI symCleanup( + HANDLE hProcess); + + static BOOL WINAPI symLoadModule( + HANDLE hProcess, + HANDLE hFile, + LPSTR ImageName, + LPSTR ModuleName, + DWORD BaseOfDll, + DWORD SizeOfDll); + + static DWORD WINAPI symGetModuleBase( + HANDLE hProcess, + DWORD dwAddr); + + static BOOL WINAPI symUnloadModule( + HANDLE hProcess, + DWORD BaseOfDll); + + static BOOL WINAPI symGetSymFromAddr( + HANDLE hProcess, + DWORD Address, + LPDWORD Displacement, + PIMAGEHLP_SYMBOL Symbol); + + static BOOL WINAPI symGetLineFromAddr( + HANDLE hProcess, + DWORD dwAddr, + PDWORD pdwDisplacement, + PIMAGEHLP_LINE Line); + + static DWORD WINAPI symSetOptions( + DWORD SymOptions); + + static LPVOID WINAPI symFunctionTableAccess( + HANDLE hProcess, + DWORD AddrBase); + + static BOOL WINAPI stackWalk( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME StackFrame, + LPVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE TranslateAddress); + +private: + + typedef BOOL (WINAPI *SymInitialize_t) ( + HANDLE hProcess, + LPSTR UserSearchPath, + BOOL fInvadeProcess); + + typedef BOOL (WINAPI *SymCleanup_t) ( + HANDLE hProcess); + + typedef BOOL (WINAPI *SymLoadModule_t) ( + HANDLE hProcess, + HANDLE hFile, + LPSTR ImageName, + LPSTR ModuleName, + DWORD BaseOfDll, + DWORD SizeOfDll); + + typedef DWORD (WINAPI *SymGetModuleBase_t) ( + HANDLE hProcess, + DWORD dwAddr); + + typedef BOOL (WINAPI *SymUnloadModule_t) ( + HANDLE hProcess, + DWORD BaseOfDll); + + typedef BOOL (WINAPI *SymGetSymFromAddr_t) ( + HANDLE hProcess, + DWORD Address, + LPDWORD Displacement, + PIMAGEHLP_SYMBOL Symbol); + + typedef BOOL (WINAPI* SymGetLineFromAddr_t) ( + HANDLE hProcess, + DWORD dwAddr, + PDWORD pdwDisplacement, + PIMAGEHLP_LINE Line); + + typedef DWORD (WINAPI *SymSetOptions_t) ( + DWORD SymOptions); + + typedef LPVOID (WINAPI *SymFunctionTableAccess_t) ( + HANDLE hProcess, + DWORD AddrBase); + + typedef BOOL (WINAPI *StackWalk_t) ( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME StackFrame, + LPVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE TranslateAddress); + + SymInitialize_t m_symInitialize; + SymCleanup_t m_symCleanup; + SymLoadModule_t m_symLoadModule; + SymUnloadModule_t m_symUnloadModule; + SymGetModuleBase_t m_symGetModuleBase; + SymGetSymFromAddr_t m_symGetSymFromAddr; + SymGetLineFromAddr_t m_symGetLineFromAddr; + SymSetOptions_t m_symSetOptions; + SymFunctionTableAccess_t m_symFunctionTableAccess; + StackWalk_t m_stackWalk; + + typedef std::set, stl::malloc_allocator > Processes; + + Processes m_initializedProcesses; + HMODULE m_dllModule; + bool m_failed; + bool m_loadedFromSystem; +}; diff --git a/Core/Libraries/Source/WWVegas/WWLib/MallocAllocator.h b/Core/Libraries/Source/WWVegas/WWLib/MallocAllocator.h new file mode 100644 index 0000000000..e465b0ad5d --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/MallocAllocator.h @@ -0,0 +1,129 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#pragma once + +#include // malloc, free +#include // std::size_t, std::ptrdiff_t +#include // std::bad_alloc + + +namespace stl +{ + +// STL allocator that uses malloc and free. Useful if allocations are meant to bypass new and delete. + +template +class malloc_allocator +{ +public: + + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + template + struct rebind + { + typedef malloc_allocator other; + }; + + malloc_allocator() throw() {} + +#if !(defined(_MSC_VER) && _MSC_VER < 1300) + malloc_allocator(const malloc_allocator&) throw() {} +#endif + + template + malloc_allocator(const malloc_allocator&) throw() {} + + ~malloc_allocator() throw() {} + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pointer allocate(size_type n, const void* = 0) + { + if (n > max_size()) + throw std::bad_alloc(); + + void* p = ::malloc(n * sizeof(T)); + if (!p) + throw std::bad_alloc(); + return static_cast(p); + } + + void deallocate(pointer p, size_type) + { + ::free(p); + } + + void construct(pointer p, const T& val) + { + new (static_cast(p)) T(val); + } + + void destroy(pointer p) + { + p->~T(); + } + + size_type max_size() const throw() + { + return ~size_type(0) / sizeof(T); + } +}; + +// Allocators of same type are always equal +template +bool operator==(const malloc_allocator&, const malloc_allocator&) throw() { + return true; +} + +template +bool operator!=(const malloc_allocator&, const malloc_allocator&) throw() { + return false; +} + +} // namespace stl + + +#if defined(USING_STLPORT) + +// This tells STLport how to rebind malloc_allocator +namespace std +{ + template + struct __stl_alloc_rebind_helper; + + template + inline stl::malloc_allocator& __stl_alloc_rebind(stl::malloc_allocator& a, const Tp2*) { + return *reinterpret_cast*>(&a); + } + + template + inline const stl::malloc_allocator& __stl_alloc_rebind(const stl::malloc_allocator& a, const Tp2*) { + return *reinterpret_cast*>(&a); + } +} + +#endif diff --git a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp new file mode 100644 index 0000000000..d13c089426 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp @@ -0,0 +1,98 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "ScopedFileRenamer.h" + + +ScopedFileRenamer::ScopedFileRenamer() + : m_file(NULL) +{ +} + +ScopedFileRenamer::ScopedFileRenamer(const char* oldName, const char* newName) + : m_file(NULL) +{ + this->rename(oldName, newName); +} + +ScopedFileRenamer::~ScopedFileRenamer() +{ + revert(); +} + +bool ScopedFileRenamer::rename(const char* oldName, const char* newName) +{ + revert(); + + m_oldName = oldName; + m_newName = newName; + + if (0 == ::rename(m_oldName.c_str(), m_newName.c_str())) + { + // Creates an empty *.tmp dummy file to remember that this program has renamed the file. + // If the program would crash before the file was renamed back to the previous name, then + // the rename condition will be able to recover the next time this code runs successfully. + std::string tmpFilename = createTmpName(); + m_file = ::fopen(tmpFilename.c_str(), "wb"); + + return true; + } + + return false; +} + +bool ScopedFileRenamer::revert() +{ + if (m_oldName.empty()) + return false; + + bool success = false; + std::string tmpName; + + if (m_file == NULL) + { + tmpName = createTmpName(); + m_file = ::fopen(tmpName.c_str(), "rb"); + } + + if (m_file != NULL) + { + ::fclose(m_file); + m_file = NULL; + + if (0 == ::rename(m_newName.c_str(), m_oldName.c_str())) + success = true; + + if (tmpName.empty()) + tmpName = createTmpName(); + + ::remove(tmpName.c_str()); + } + + m_oldName.clear(); + m_newName.clear(); + + return success; +} + +std::string ScopedFileRenamer::createTmpName() const +{ + std::string tmpFilename = m_oldName; + tmpFilename.append(".tmp"); + return tmpFilename; +} diff --git a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h new file mode 100644 index 0000000000..594192eceb --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h @@ -0,0 +1,47 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2025 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#pragma once + +#include "always.h" + +#include +#include + + +class ScopedFileRenamer +{ +public: + + ScopedFileRenamer(); + ScopedFileRenamer(const char* oldName, const char* newName); + ~ScopedFileRenamer(); + + bool rename(const char* oldName, const char* newName); + bool revert(); + +private: + + std::string createTmpName() const; + +private: + + std::string m_oldName; + std::string m_newName; + FILE* m_file; +}; diff --git a/Core/Tools/ImagePacker/CMakeLists.txt b/Core/Tools/ImagePacker/CMakeLists.txt index 3b30d71bb9..f1f52a59ed 100644 --- a/Core/Tools/ImagePacker/CMakeLists.txt +++ b/Core/Tools/ImagePacker/CMakeLists.txt @@ -30,7 +30,6 @@ target_link_libraries(corei_imagepacker INTERFACE comctl32 core_debug core_profile - dbghelplib imm32 vfw32 winmm diff --git a/Core/Tools/MapCacheBuilder/CMakeLists.txt b/Core/Tools/MapCacheBuilder/CMakeLists.txt index 5d390853a8..bdb38951a5 100644 --- a/Core/Tools/MapCacheBuilder/CMakeLists.txt +++ b/Core/Tools/MapCacheBuilder/CMakeLists.txt @@ -17,7 +17,6 @@ target_link_libraries(corei_mapcachebuilder INTERFACE comctl32 core_debug core_profile - dbghelplib imm32 vfw32 winmm diff --git a/Core/Tools/PATCHGET/CMakeLists.txt b/Core/Tools/PATCHGET/CMakeLists.txt index 265a74c785..d2822c3ddb 100644 --- a/Core/Tools/PATCHGET/CMakeLists.txt +++ b/Core/Tools/PATCHGET/CMakeLists.txt @@ -25,7 +25,6 @@ target_link_libraries(corei_patchgrabber INTERFACE comctl32 core_debug core_profile - dbghelplib gamespy::gamespy imm32 vfw32 diff --git a/Generals/Code/GameEngine/Source/Common/System/StackDump.cpp b/Generals/Code/GameEngine/Source/Common/System/StackDump.cpp index 1c0a319e14..f5bcc80893 100644 --- a/Generals/Code/GameEngine/Source/Common/System/StackDump.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/StackDump.cpp @@ -31,6 +31,7 @@ #include "Common/StackDump.h" #include "Common/Debug.h" +#include "DbgHelpLoader.h" //***************************************************************************** // Prototypes @@ -45,14 +46,6 @@ void WriteStackLine(void*address, void (*callback)(const char*)); // Mis-named globals :-) //***************************************************************************** static CONTEXT gsContext; -static Bool gsInit=FALSE; - -BOOL (__stdcall *gsSymGetLineFromAddr)( - IN HANDLE hProcess, - IN DWORD dwAddr, - OUT PDWORD pdwDisplacement, - OUT PIMAGEHLP_LINE Line - ); //***************************************************************************** @@ -72,7 +65,8 @@ void StackDump(void (*callback)(const char*)) callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; DWORD myeip,myesp,myebp; @@ -101,7 +95,8 @@ void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; MakeStackTrace(eip,esp,ebp, 0, callback); } @@ -111,27 +106,20 @@ void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const //***************************************************************************** BOOL InitSymbolInfo() { - if (gsInit == TRUE) + if (DbgHelpLoader::isLoaded()) return TRUE; - gsInit = TRUE; + if (!DbgHelpLoader::load()) + return FALSE; atexit(UninitSymbolInfo); - // See if we have the line from address function - // We use GetProcAddress to stop link failures at dll loadup - HINSTANCE hInstDebugHlp = GetModuleHandle("dbghelp.dll"); - - gsSymGetLineFromAddr = (BOOL (__stdcall *)( IN HANDLE,IN DWORD,OUT PDWORD,OUT PIMAGEHLP_LINE)) - GetProcAddress(hInstDebugHlp , "SymGetLineFromAddr"); - char pathname[_MAX_PATH+1]; char drive[10]; char directory[_MAX_PATH+1]; HANDLE process; - - ::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); + DbgHelpLoader::symSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); process = GetCurrentProcess(); @@ -145,18 +133,18 @@ BOOL InitSymbolInfo() // append the current directory to build a search path for SymInit ::lstrcat(pathname, ";.;"); - if(::SymInitialize(process, pathname, FALSE)) + if(DbgHelpLoader::symInitialize(process, pathname, FALSE)) { // regenerate the name of the app ::GetModuleFileName(NULL, pathname, _MAX_PATH); - if(::SymLoadModule(process, NULL, pathname, NULL, 0, 0)) + if(DbgHelpLoader::symLoadModule(process, NULL, pathname, NULL, 0, 0)) { //Load any other relevant modules (ie dlls) here return TRUE; } - ::SymCleanup(process); } + DbgHelpLoader::unload(); return(FALSE); } @@ -165,14 +153,7 @@ BOOL InitSymbolInfo() //***************************************************************************** void UninitSymbolInfo(void) { - if (gsInit == FALSE) - { - return; - } - - gsInit = FALSE; - - ::SymCleanup(GetCurrentProcess()); + DbgHelpLoader::unload(); } @@ -217,14 +198,14 @@ stack_frame.AddrFrame.Offset = myebp; unsigned int skip = skipFrames; while (b_ret&&skip) { - b_ret = StackWalk( IMAGE_FILE_MACHINE_I386, + b_ret = DbgHelpLoader::stackWalk( IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL); skip--; } @@ -233,14 +214,14 @@ stack_frame.AddrFrame.Offset = myebp; while(b_ret&&skip) { - b_ret = StackWalk( IMAGE_FILE_MACHINE_I386, + b_ret = DbgHelpLoader::stackWalk( IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL); @@ -256,7 +237,9 @@ stack_frame.AddrFrame.Offset = myebp; //***************************************************************************** void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address) { - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; + if (name) { strcpy(name, ""); @@ -285,8 +268,8 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l psymbol->SizeOfStruct = sizeof(symbol_buffer); psymbol->MaxNameLength = 512; - if (SymGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol)) - { + if (DbgHelpLoader::symGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol)) + { if (name) { strcpy(name, psymbol->Name); @@ -294,32 +277,27 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l } // Get line now - if (gsSymGetLineFromAddr) - { - // Unsupported for win95/98 at least with my current dbghelp.dll - IMAGEHLP_LINE line; - memset(&line,0,sizeof(line)); - line.SizeOfStruct = sizeof(line); + IMAGEHLP_LINE line; + memset(&line,0,sizeof(line)); + line.SizeOfStruct = sizeof(line); - - if (gsSymGetLineFromAddr(process, (DWORD) pointer, &displacement, &line)) + if (DbgHelpLoader::symGetLineFromAddr(process, (DWORD) pointer, &displacement, &line)) + { + if (filename) + { + strcpy(filename, line.FileName); + } + if (linenumber) { - if (filename) - { - strcpy(filename, line.FileName); - } - if (linenumber) - { - *linenumber = (unsigned int)line.LineNumber; - } - if (address) - { - *address = (unsigned int)line.Address; - } - } + *linenumber = (unsigned int)line.LineNumber; + } + if (address) + { + *address = (unsigned int)line.Address; + } } - } + } } @@ -328,7 +306,8 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l //***************************************************************************** void FillStackAddresses(void**addresses, unsigned int count, unsigned int skip) { - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; STACKFRAME stack_frame; @@ -378,28 +357,28 @@ stack_frame.AddrFrame.Offset = myebp; // Skip some? while (stillgoing&&skip) { - stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386, + stillgoing = DbgHelpLoader::stackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL) != 0; skip--; } while(stillgoing&&count) { - stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386, + stillgoing = DbgHelpLoader::stackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL) != 0; if (stillgoing) { @@ -438,7 +417,8 @@ void StackDumpFromAddresses(void**addresses, unsigned int count, void (*callback callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; while ((count--) && (*addresses!=NULL)) { diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index ee887eb836..a12fb92e04 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -81,6 +81,7 @@ #include "dx8texman.h" #include "bound.h" #include "dx8webbrowser.h" +#include "DbgHelpGuard.h" const int DEFAULT_RESOLUTION_WIDTH = 640; @@ -312,7 +313,12 @@ bool DX8Wrapper::Init(void * hwnd, bool lite) ** Create the D3D interface object */ WWDEBUG_SAY(("Create Direct3D8\n")); - D3DInterface = Direct3DCreate8Ptr(D3D_SDK_VERSION); // TODO: handle failure cases... + { + // TheSuperHackers @bugfix xezon 13/06/2025 Temporarily unload dbghelp.dll and prevent it from loading. + DbgHelpGuard dbgHelpGuard; + + D3DInterface = Direct3DCreate8Ptr(D3D_SDK_VERSION); // TODO: handle failure cases... + } if (D3DInterface == NULL) { return(false); } @@ -560,15 +566,21 @@ bool DX8Wrapper::Create_Device(void) Vertex_Processing_Behavior|=D3DCREATE_FPU_PRESERVE; #endif - HRESULT hr=D3DInterface->CreateDevice - ( - CurRenderDevice, - WW3D_DEVTYPE, - _Hwnd, - Vertex_Processing_Behavior, - &_PresentParameters, - &D3DDevice - ); + HRESULT hr; + { + // TheSuperHackers @bugfix xezon 13/06/2025 Temporarily unload dbghelp.dll and prevent it from loading. + DbgHelpGuard dbgHelpGuard; + + hr=D3DInterface->CreateDevice + ( + CurRenderDevice, + WW3D_DEVTYPE, + _Hwnd, + Vertex_Processing_Behavior, + &_PresentParameters, + &D3DDevice + ); + } if (FAILED(hr)) { diff --git a/Generals/Code/Main/CMakeLists.txt b/Generals/Code/Main/CMakeLists.txt index b4cf515569..0dcc45a271 100644 --- a/Generals/Code/Main/CMakeLists.txt +++ b/Generals/Code/Main/CMakeLists.txt @@ -12,7 +12,6 @@ target_link_libraries(g_generals PRIVATE comctl32 d3d8 d3dx8 - dbghelplib dinput8 dxguid g_gameengine diff --git a/Generals/Code/Tools/GUIEdit/CMakeLists.txt b/Generals/Code/Tools/GUIEdit/CMakeLists.txt index 5a7b0eb877..c347a19ad7 100644 --- a/Generals/Code/Tools/GUIEdit/CMakeLists.txt +++ b/Generals/Code/Tools/GUIEdit/CMakeLists.txt @@ -49,7 +49,6 @@ target_link_libraries(g_guiedit PRIVATE benchmark comctl32 d3d8lib - dbghelplib g_gameengine g_gameenginedevice g_wwvegas diff --git a/Generals/Code/Tools/ParticleEditor/CMakeLists.txt b/Generals/Code/Tools/ParticleEditor/CMakeLists.txt index bf7005ea58..0ea7325438 100644 --- a/Generals/Code/Tools/ParticleEditor/CMakeLists.txt +++ b/Generals/Code/Tools/ParticleEditor/CMakeLists.txt @@ -42,7 +42,6 @@ target_link_libraries(g_particleeditor PRIVATE corei_libraries_source_wwvegas corei_libraries_source_wwvegas_wwlib d3d8lib - dbghelplib gi_gameengine_include gi_always gi_libraries_source_wwvegas diff --git a/Generals/Code/Tools/W3DView/CMakeLists.txt b/Generals/Code/Tools/W3DView/CMakeLists.txt index 80ab604011..7a3f14db37 100644 --- a/Generals/Code/Tools/W3DView/CMakeLists.txt +++ b/Generals/Code/Tools/W3DView/CMakeLists.txt @@ -8,7 +8,6 @@ target_link_libraries(g_w3dview PRIVATE d3d8 d3d8lib d3dx8 - dbghelplib imm32 milesstub Version diff --git a/Generals/Code/Tools/WorldBuilder/CMakeLists.txt b/Generals/Code/Tools/WorldBuilder/CMakeLists.txt index 325820e235..17c9f5a314 100644 --- a/Generals/Code/Tools/WorldBuilder/CMakeLists.txt +++ b/Generals/Code/Tools/WorldBuilder/CMakeLists.txt @@ -206,7 +206,6 @@ target_compile_definitions(g_worldbuilder PRIVATE _AFXDLL) target_link_libraries(g_worldbuilder PRIVATE d3d8lib - dbghelplib core_browserdispatch g_gameengine g_gameenginedevice diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/StackDump.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/StackDump.cpp index a0ed36025f..1364a34953 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/StackDump.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/StackDump.cpp @@ -31,6 +31,7 @@ #include "Common/StackDump.h" #include "Common/Debug.h" +#include "DbgHelpLoader.h" //***************************************************************************** // Prototypes @@ -45,14 +46,6 @@ void WriteStackLine(void*address, void (*callback)(const char*)); // Mis-named globals :-) //***************************************************************************** static CONTEXT gsContext; -static Bool gsInit=FALSE; - -BOOL (__stdcall *gsSymGetLineFromAddr)( - IN HANDLE hProcess, - IN DWORD dwAddr, - OUT PDWORD pdwDisplacement, - OUT PIMAGEHLP_LINE Line - ); //***************************************************************************** @@ -72,7 +65,8 @@ void StackDump(void (*callback)(const char*)) callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; DWORD myeip,myesp,myebp; @@ -101,7 +95,8 @@ void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; MakeStackTrace(eip,esp,ebp, 0, callback); } @@ -111,27 +106,20 @@ void StackDumpFromContext(DWORD eip,DWORD esp,DWORD ebp, void (*callback)(const //***************************************************************************** BOOL InitSymbolInfo() { - if (gsInit == TRUE) + if (DbgHelpLoader::isLoaded()) return TRUE; - gsInit = TRUE; + if (!DbgHelpLoader::load()) + return FALSE; atexit(UninitSymbolInfo); - // See if we have the line from address function - // We use GetProcAddress to stop link failures at dll loadup - HINSTANCE hInstDebugHlp = GetModuleHandle("dbghelp.dll"); - - gsSymGetLineFromAddr = (BOOL (__stdcall *)( IN HANDLE,IN DWORD,OUT PDWORD,OUT PIMAGEHLP_LINE)) - GetProcAddress(hInstDebugHlp , "SymGetLineFromAddr"); - char pathname[_MAX_PATH+1]; char drive[10]; char directory[_MAX_PATH+1]; HANDLE process; - - ::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); + DbgHelpLoader::symSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); process = GetCurrentProcess(); @@ -145,18 +133,18 @@ BOOL InitSymbolInfo() // append the current directory to build a search path for SymInit ::lstrcat(pathname, ";.;"); - if(::SymInitialize(process, pathname, FALSE)) + if(DbgHelpLoader::symInitialize(process, pathname, FALSE)) { // regenerate the name of the app ::GetModuleFileName(NULL, pathname, _MAX_PATH); - if(::SymLoadModule(process, NULL, pathname, NULL, 0, 0)) + if(DbgHelpLoader::symLoadModule(process, NULL, pathname, NULL, 0, 0)) { //Load any other relevant modules (ie dlls) here return TRUE; } - ::SymCleanup(process); } + DbgHelpLoader::unload(); return(FALSE); } @@ -165,14 +153,7 @@ BOOL InitSymbolInfo() //***************************************************************************** void UninitSymbolInfo(void) { - if (gsInit == FALSE) - { - return; - } - - gsInit = FALSE; - - ::SymCleanup(GetCurrentProcess()); + DbgHelpLoader::unload(); } @@ -217,14 +198,14 @@ stack_frame.AddrFrame.Offset = myebp; unsigned int skip = skipFrames; while (b_ret&&skip) { - b_ret = StackWalk( IMAGE_FILE_MACHINE_I386, + b_ret = DbgHelpLoader::stackWalk( IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL); skip--; } @@ -233,14 +214,14 @@ stack_frame.AddrFrame.Offset = myebp; while(b_ret&&skip) { - b_ret = StackWalk( IMAGE_FILE_MACHINE_I386, + b_ret = DbgHelpLoader::stackWalk( IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL); @@ -256,7 +237,9 @@ stack_frame.AddrFrame.Offset = myebp; //***************************************************************************** void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* linenumber, unsigned int* address) { - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; + if (name) { strcpy(name, ""); @@ -285,8 +268,8 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l psymbol->SizeOfStruct = sizeof(symbol_buffer); psymbol->MaxNameLength = 512; - if (SymGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol)) - { + if (DbgHelpLoader::symGetSymFromAddr(process, (DWORD) pointer, &displacement, psymbol)) + { if (name) { strcpy(name, psymbol->Name); @@ -294,32 +277,27 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l } // Get line now - if (gsSymGetLineFromAddr) - { - // Unsupported for win95/98 at least with my current dbghelp.dll - IMAGEHLP_LINE line; - memset(&line,0,sizeof(line)); - line.SizeOfStruct = sizeof(line); + IMAGEHLP_LINE line; + memset(&line,0,sizeof(line)); + line.SizeOfStruct = sizeof(line); - - if (gsSymGetLineFromAddr(process, (DWORD) pointer, &displacement, &line)) + if (DbgHelpLoader::symGetLineFromAddr(process, (DWORD) pointer, &displacement, &line)) + { + if (filename) + { + strcpy(filename, line.FileName); + } + if (linenumber) { - if (filename) - { - strcpy(filename, line.FileName); - } - if (linenumber) - { - *linenumber = (unsigned int)line.LineNumber; - } - if (address) - { - *address = (unsigned int)line.Address; - } - } + *linenumber = (unsigned int)line.LineNumber; + } + if (address) + { + *address = (unsigned int)line.Address; + } } - } + } } @@ -328,7 +306,8 @@ void GetFunctionDetails(void *pointer, char*name, char*filename, unsigned int* l //***************************************************************************** void FillStackAddresses(void**addresses, unsigned int count, unsigned int skip) { - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; STACKFRAME stack_frame; @@ -378,28 +357,28 @@ stack_frame.AddrFrame.Offset = myebp; // Skip some? while (stillgoing&&skip) { - stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386, + stillgoing = DbgHelpLoader::stackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL) != 0; skip--; } while(stillgoing&&count) { - stillgoing = StackWalk(IMAGE_FILE_MACHINE_I386, + stillgoing = DbgHelpLoader::stackWalk(IMAGE_FILE_MACHINE_I386, process, thread, &stack_frame, NULL, //&gsContext, NULL, - SymFunctionTableAccess, - SymGetModuleBase, + DbgHelpLoader::symFunctionTableAccess, + DbgHelpLoader::symGetModuleBase, NULL) != 0; if (stillgoing) { @@ -438,7 +417,8 @@ void StackDumpFromAddresses(void**addresses, unsigned int count, void (*callback callback = StackDumpDefaultHandler; } - InitSymbolInfo(); + if (!InitSymbolInfo()) + return; while ((count--) && (*addresses!=NULL)) { diff --git a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp index 2a9b3cf063..4455fcbf68 100644 --- a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp +++ b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp @@ -85,6 +85,7 @@ #include "dx8texman.h" #include "bound.h" #include "dx8webbrowser.h" +#include "DbgHelpGuard.h" #include "shdlib.h" @@ -331,7 +332,12 @@ bool DX8Wrapper::Init(void * hwnd, bool lite) ** Create the D3D interface object */ WWDEBUG_SAY(("Create Direct3D8\n")); - D3DInterface = Direct3DCreate8Ptr(D3D_SDK_VERSION); // TODO: handle failure cases... + { + // TheSuperHackers @bugfix xezon 13/06/2025 Temporarily unload dbghelp.dll and prevent it from loading. + DbgHelpGuard dbgHelpGuard; + + D3DInterface = Direct3DCreate8Ptr(D3D_SDK_VERSION); // TODO: handle failure cases... + } if (D3DInterface == NULL) { return(false); } @@ -592,15 +598,21 @@ bool DX8Wrapper::Create_Device(void) Vertex_Processing_Behavior|=D3DCREATE_FPU_PRESERVE; #endif - HRESULT hr=D3DInterface->CreateDevice - ( - CurRenderDevice, - WW3D_DEVTYPE, - _Hwnd, - Vertex_Processing_Behavior, - &_PresentParameters, - &D3DDevice - ); + HRESULT hr; + { + // TheSuperHackers @bugfix xezon 13/06/2025 Temporarily unload dbghelp.dll and prevent it from loading. + DbgHelpGuard dbgHelpGuard; + + hr=D3DInterface->CreateDevice + ( + CurRenderDevice, + WW3D_DEVTYPE, + _Hwnd, + Vertex_Processing_Behavior, + &_PresentParameters, + &D3DDevice + ); + } if (FAILED(hr)) { diff --git a/GeneralsMD/Code/Main/CMakeLists.txt b/GeneralsMD/Code/Main/CMakeLists.txt index 2ef80b3e87..f6eabb5ffd 100644 --- a/GeneralsMD/Code/Main/CMakeLists.txt +++ b/GeneralsMD/Code/Main/CMakeLists.txt @@ -14,7 +14,6 @@ target_link_libraries(z_generals PRIVATE core_profile d3d8 d3dx8 - dbghelplib dinput8 dxguid imm32 diff --git a/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt b/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt index e401d8d335..dc0f0b8d63 100644 --- a/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/GUIEdit/CMakeLists.txt @@ -51,7 +51,6 @@ target_link_libraries(z_guiedit PRIVATE core_debug core_profile d3d8lib - dbghelplib imm32 stlport vfw32 diff --git a/GeneralsMD/Code/Tools/ParticleEditor/CMakeLists.txt b/GeneralsMD/Code/Tools/ParticleEditor/CMakeLists.txt index 0b410f3470..4d09c98ec2 100644 --- a/GeneralsMD/Code/Tools/ParticleEditor/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/ParticleEditor/CMakeLists.txt @@ -42,7 +42,6 @@ target_link_libraries(z_particleeditor PRIVATE corei_libraries_source_wwvegas corei_libraries_source_wwvegas_wwlib d3d8lib - dbghelplib imm32 core_config stlport diff --git a/GeneralsMD/Code/Tools/W3DView/CMakeLists.txt b/GeneralsMD/Code/Tools/W3DView/CMakeLists.txt index 7225e63bd5..9f321bc93d 100644 --- a/GeneralsMD/Code/Tools/W3DView/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/W3DView/CMakeLists.txt @@ -8,7 +8,6 @@ target_link_libraries(z_w3dview PRIVATE d3d8 d3d8lib d3dx8 - dbghelplib imm32 milesstub Version diff --git a/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt b/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt index c24f97e585..ff7c99e375 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/WorldBuilder/CMakeLists.txt @@ -213,7 +213,6 @@ target_link_libraries(z_worldbuilder PRIVATE core_debug core_profile d3d8lib - dbghelplib imm32 vfw32 winmm diff --git a/GeneralsMD/Code/Tools/wdump/CMakeLists.txt b/GeneralsMD/Code/Tools/wdump/CMakeLists.txt index a363e40260..65d190703e 100644 --- a/GeneralsMD/Code/Tools/wdump/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/wdump/CMakeLists.txt @@ -21,7 +21,6 @@ target_link_libraries(z_wdump PRIVATE core_config core_utility core_wwstub # avoid linking GameEngine - dbghelplib imm32 vfw32 winmm diff --git a/cmake/dbghelp.cmake b/cmake/dbghelp.cmake deleted file mode 100644 index 7940400e3e..0000000000 --- a/cmake/dbghelp.cmake +++ /dev/null @@ -1,7 +0,0 @@ -FetchContent_Declare( - dbghelp - GIT_REPOSITORY https://github.com/TheSuperHackers/dbghelp-import-lib.git - GIT_TAG afeb423d4597167c8fa94215f4574f3ae310f920 -) - -FetchContent_MakeAvailable(dbghelp) \ No newline at end of file From f55b298dd02bb474956565584f1517794b99a50f Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Sat, 14 Jun 2025 23:42:59 +0200 Subject: [PATCH 2/3] Minor improvement in DbgHelpGuard --- Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp | 5 +++-- Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp index 9a0acc8214..16c705c8b5 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp @@ -42,7 +42,7 @@ void DbgHelpGuard::deactivate() else if (DbgHelpLoader::isLoaded()) { DbgHelpLoader::unload(); - m_wasLoaded = true; + m_requiresLoad = true; m_dbgHelpRenamer.rename("dbghelp.dll", "dbghelp.dll.bak"); } else @@ -56,8 +56,9 @@ void DbgHelpGuard::reactivate() m_dbgHelpRenamer.revert(); DbgHelpLoader::unblockLoad(); - if (m_wasLoaded) + if (m_requiresLoad) { DbgHelpLoader::load(); + m_requiresLoad = false; } } diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h index 243f4ed1cf..ddb534c520 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h @@ -43,5 +43,5 @@ class DbgHelpGuard private: ScopedFileRenamer m_dbgHelpRenamer; - bool m_wasLoaded; + bool m_requiresLoad; }; From c235db4c1f2cbfb1183d6431ef3f3939ac206668 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Wed, 18 Jun 2025 18:35:48 +0200 Subject: [PATCH 3/3] Remove ScopeFileRenamer and use dbghelp front loading strategy instead of file renaming --- .../Source/WWVegas/WWLib/CMakeLists.txt | 2 - .../Source/WWVegas/WWLib/DbgHelpGuard.cpp | 27 +++-- .../Source/WWVegas/WWLib/DbgHelpGuard.h | 13 +-- .../Source/WWVegas/WWLib/DbgHelpLoader.cpp | 14 --- .../Source/WWVegas/WWLib/DbgHelpLoader.h | 7 -- .../WWVegas/WWLib/ScopedFileRenamer.cpp | 98 ------------------- .../Source/WWVegas/WWLib/ScopedFileRenamer.h | 47 --------- 7 files changed, 16 insertions(+), 192 deletions(-) delete mode 100644 Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp delete mode 100644 Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h diff --git a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt index ad507d6b68..708dcd6edf 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt +++ b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt @@ -103,8 +103,6 @@ set(WWLIB_SRC #regexpr.cpp #regexpr.h #search.h - ScopedFileRenamer.cpp - ScopedFileRenamer.h sharebuf.h Signaler.h simplevec.h diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp index 16c705c8b5..f53b89adcc 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.cpp @@ -22,43 +22,38 @@ DbgHelpGuard::DbgHelpGuard() + : m_hasLoaded(false) { - deactivate(); + activate(); } DbgHelpGuard::~DbgHelpGuard() { - reactivate(); + deactivate(); } -void DbgHelpGuard::deactivate() +void DbgHelpGuard::activate() { - DbgHelpLoader::blockLoad(); - if (DbgHelpLoader::isLoadedFromSystem()) { // This is ok. Do nothing. } else if (DbgHelpLoader::isLoaded()) { - DbgHelpLoader::unload(); - m_requiresLoad = true; - m_dbgHelpRenamer.rename("dbghelp.dll", "dbghelp.dll.bak"); + // This is maybe not ok. But do nothing until this becomes a user facing problem. } else { - m_dbgHelpRenamer.rename("dbghelp.dll", "dbghelp.dll.bak"); + // Front load the DLL now to prevent other code from loading the potentially wrong DLL. + m_hasLoaded = DbgHelpLoader::load(); } } -void DbgHelpGuard::reactivate() +void DbgHelpGuard::deactivate() { - m_dbgHelpRenamer.revert(); - DbgHelpLoader::unblockLoad(); - - if (m_requiresLoad) + if (m_hasLoaded) { - DbgHelpLoader::load(); - m_requiresLoad = false; + DbgHelpLoader::unload(); + m_hasLoaded = false; } } diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h index ddb534c520..c613cb3b4c 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpGuard.h @@ -20,15 +20,13 @@ #include "always.h" -#include "ScopedFileRenamer.h" - -// This class temporarily unloads dbghelp.dll and prevents it from loading during its lifetime. +// This class temporarily loads and unloads dbghelp.dll from the desired location to prevent +// other code from potentially loading it from an undesired location. // This helps avoid crashing on boot using recent AMD/ATI drivers, which attempt to load and use // dbghelp.dll from the game install directory but are unable to do so correctly because // the dbghelp.dll that ships with the game is very old and the AMD/ATI code does not handle -// that correctly. This workaround is not required if the dbghelp.dll was loaded from the system -// directory. +// that correctly. class DbgHelpGuard { @@ -37,11 +35,10 @@ class DbgHelpGuard DbgHelpGuard(); ~DbgHelpGuard(); + void activate(); void deactivate(); - void reactivate(); private: - ScopedFileRenamer m_dbgHelpRenamer; - bool m_requiresLoad; + bool m_hasLoaded; }; diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp index b99ae06be7..4449ff15a6 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.cpp @@ -19,7 +19,6 @@ #include "DbgHelpLoader.h" -int DbgHelpLoader::BlockLoadCounter = 0; DbgHelpLoader* DbgHelpLoader::Inst = NULL; DbgHelpLoader::DbgHelpLoader() @@ -53,21 +52,8 @@ bool DbgHelpLoader::isLoadedFromSystem() return Inst != NULL && Inst->m_loadedFromSystem; } -void DbgHelpLoader::blockLoad() -{ - ++BlockLoadCounter; -} - -bool DbgHelpLoader::unblockLoad() -{ - return --BlockLoadCounter == 0; -} - bool DbgHelpLoader::load() { - if (BlockLoadCounter > 0) - return false; - if (Inst == NULL) { // Cannot use new/delete here when this is loaded during game memory initialization. diff --git a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h index 8b6079e3c8..3a7d9cafa9 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h +++ b/Core/Libraries/Source/WWVegas/WWLib/DbgHelpLoader.h @@ -33,7 +33,6 @@ class DbgHelpLoader { private: - static int BlockLoadCounter; static DbgHelpLoader* Inst; // Is singleton class DbgHelpLoader(); @@ -47,12 +46,6 @@ class DbgHelpLoader // Returns whether dbghelp.dll is loaded from the system directory static bool isLoadedFromSystem(); - // Blocks loading a dbghelp.dll - static void blockLoad(); - - // Unblocks loading a dbghelp.dll. Returns true if unblocked. - static bool unblockLoad(); - static bool load(); static bool reload(); static void unload(); diff --git a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp deleted file mode 100644 index d13c089426..0000000000 --- a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -** Command & Conquer Generals Zero Hour(tm) -** Copyright 2025 TheSuperHackers -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "ScopedFileRenamer.h" - - -ScopedFileRenamer::ScopedFileRenamer() - : m_file(NULL) -{ -} - -ScopedFileRenamer::ScopedFileRenamer(const char* oldName, const char* newName) - : m_file(NULL) -{ - this->rename(oldName, newName); -} - -ScopedFileRenamer::~ScopedFileRenamer() -{ - revert(); -} - -bool ScopedFileRenamer::rename(const char* oldName, const char* newName) -{ - revert(); - - m_oldName = oldName; - m_newName = newName; - - if (0 == ::rename(m_oldName.c_str(), m_newName.c_str())) - { - // Creates an empty *.tmp dummy file to remember that this program has renamed the file. - // If the program would crash before the file was renamed back to the previous name, then - // the rename condition will be able to recover the next time this code runs successfully. - std::string tmpFilename = createTmpName(); - m_file = ::fopen(tmpFilename.c_str(), "wb"); - - return true; - } - - return false; -} - -bool ScopedFileRenamer::revert() -{ - if (m_oldName.empty()) - return false; - - bool success = false; - std::string tmpName; - - if (m_file == NULL) - { - tmpName = createTmpName(); - m_file = ::fopen(tmpName.c_str(), "rb"); - } - - if (m_file != NULL) - { - ::fclose(m_file); - m_file = NULL; - - if (0 == ::rename(m_newName.c_str(), m_oldName.c_str())) - success = true; - - if (tmpName.empty()) - tmpName = createTmpName(); - - ::remove(tmpName.c_str()); - } - - m_oldName.clear(); - m_newName.clear(); - - return success; -} - -std::string ScopedFileRenamer::createTmpName() const -{ - std::string tmpFilename = m_oldName; - tmpFilename.append(".tmp"); - return tmpFilename; -} diff --git a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h b/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h deleted file mode 100644 index 594192eceb..0000000000 --- a/Core/Libraries/Source/WWVegas/WWLib/ScopedFileRenamer.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -** Command & Conquer Generals Zero Hour(tm) -** Copyright 2025 TheSuperHackers -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#pragma once - -#include "always.h" - -#include -#include - - -class ScopedFileRenamer -{ -public: - - ScopedFileRenamer(); - ScopedFileRenamer(const char* oldName, const char* newName); - ~ScopedFileRenamer(); - - bool rename(const char* oldName, const char* newName); - bool revert(); - -private: - - std::string createTmpName() const; - -private: - - std::string m_oldName; - std::string m_newName; - FILE* m_file; -};