diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 724dad1..80259a4 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -29,8 +29,20 @@ jobs: - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.3.1 - - name: Build CMake Files - run: cmake -S . -B build -A Win32 + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg.git + .\vcpkg\bootstrap-vcpkg.bat + + - name: Set VCPKG_ROOT + run: | + echo "VCPKG_ROOT=$(Get-Location)\vcpkg" >> $env:GITHUB_ENV + shell: powershell + + - name: Build CMake Files with vcpkg toolchain + run: cmake --preset=vcpkg + env: + VCPKG_ROOT: ${{ env.VCPKG_ROOT }} - name: Build binaries run: cmake --build build --config Release @@ -54,7 +66,7 @@ jobs: mode: update tag_name: v${{ steps.set_version.outputs.version }} release_name: gMod v${{ steps.set_version.outputs.version }} - assets: .\bin\Release\gMod.dll + assets: .\bin\Release\gMod.dll,.\bin\Release\TpfConvert.exe,.\bin\Release\d3dx9_43.dll github_token: ${{ env.GITHUB_TOKEN }} replace_assets: true body_mrkdwn: ${{ env.Changelog }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c51f101..6f4512f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,4 +1,4 @@ -name: gMod CI Pipeline +name: gMod CI Pipeline on: pull_request: @@ -8,9 +8,8 @@ on: branches: - dev workflow_dispatch: - -jobs: +jobs: build: strategy: @@ -32,8 +31,20 @@ jobs: - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.3.1 - - name: Build CMake Files - run: cmake -S . -B build -A Win32 + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg.git + .\vcpkg\bootstrap-vcpkg.bat + + - name: Set VCPKG_ROOT + run: | + echo "VCPKG_ROOT=$(Get-Location)\vcpkg" >> $env:GITHUB_ENV + shell: powershell + + - name: Build CMake Files with vcpkg toolchain + run: cmake --preset=vcpkg + env: + VCPKG_ROOT: ${{ env.VCPKG_ROOT }} - name: Build binaries run: cmake --build build --config Release diff --git a/.gitignore b/.gitignore index 89e8cdb..5857832 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ bin/ */Release */bin */obj +vcpkg +vcpkg_installed # Bloated Windows Databases *.sdf diff --git a/CMakeLists.txt b/CMakeLists.txt index c202cc9..930dc41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ if(${CLEAN} MATCHES "ON") endif() set(VERSION_MAJOR 1) -set(VERSION_MINOR 6) -set(VERSION_PATCH 3) -set(VERSION_TWEAK 1) +set(VERSION_MINOR 7) +set(VERSION_PATCH 0) +set(VERSION_TWEAK 0) set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in" "${VERSION_RC}" @ONLY) @@ -75,3 +75,5 @@ target_compile_definitions(gMod PRIVATE DIRECT_INJECTION LOG_MESSAGE ) + +add_subdirectory(TpfConvert) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..4fd8e25 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "vcpkg", + "generator": "Visual Studio 17 2022", + "architecture": "Win32", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + } + ] +} diff --git a/README.md b/README.md index 568bb8c..3674d6c 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,17 @@ If you would like to use Reshade in combination with gMod, we recommend running Requirements: - Visual Studio 2022 - CMake 3.16+, integrated into the Developer Powershell for VS 2022 +- vcpkg, integrated into the Developer Powershell for VS 2022 Compile: -- cmake -B build +- cmake --preset=vcpkg - cmake --open build - compile + +**TpfConvert** +Small utility to convert old .tpf files with invalid images into .zip files with working images. +Usage: +- put TpfConvert.exe and d3dx9.dll anywhere +- create a folder "plugins" in that folder and put your .tpf/.zip files with invalid images there +- run TpfConvert.exe, My_Texmod.tpf is fixed and copied into My_Texmod_.zip +- copy My_Texmod_.zip into your GW Launcher plugins folder, delete My_Texmod.tpf from it, if it still exists \ No newline at end of file diff --git a/TpfConvert/CMakeLists.txt b/TpfConvert/CMakeLists.txt new file mode 100644 index 0000000..1891f65 --- /dev/null +++ b/TpfConvert/CMakeLists.txt @@ -0,0 +1,39 @@ +add_executable(TpfConvert) + +# Find all .ixx files in the TpfConvert folder +file(GLOB TPF_SOURCES "src/*.ixx") + +# Add the .ixx files to the TpfConvert target +target_sources(TpfConvert PRIVATE ${TPF_SOURCES}) + +set_target_properties(TpfConvert PROPERTIES + CXX_STANDARD 23 + CXX_STANDARD_REQUIRED ON +) + +target_compile_definitions(TpfConvert PRIVATE + "NOMINMAX" + "_WIN32_WINNT=_WIN32_WINNT_WIN7" + "WIN32_LEAN_AND_MEAN" + "VC_EXTRALEAN" +) + +if(DEFINED ENV{VCPKG_ROOT}) + message(STATUS "VCPKG_ROOT: $ENV{VCPKG_ROOT}") +else() + message(STATUS "VCPKG_ROOT is not set") +endif() + +if(DEFINED CMAKE_TOOLCHAIN_FILE) + message(STATUS "CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") +else() + message(STATUS "CMAKE_TOOLCHAIN_FILE is not set") +endif() + +find_package(dxsdk-d3dx CONFIG REQUIRED) + +target_link_libraries(TpfConvert PRIVATE + libzippp + Microsoft::D3DX9 + d3d9 +) diff --git a/TpfConvert/src/ModfileLoader.ixx b/TpfConvert/src/ModfileLoader.ixx new file mode 100644 index 0000000..31c2c75 --- /dev/null +++ b/TpfConvert/src/ModfileLoader.ixx @@ -0,0 +1,179 @@ +export module ModfileLoader; + +import std; +import ; +import ModfileLoader.TpfReader; + +export using HashType = uint64_t; + +export struct TexEntry { + std::vector data{}; + HashType crc_hash = 0; // hash value + std::string ext{}; +}; + +namespace { + HashType GetCrcFromFilename(const std::string& filename) { + const static std::regex re(R"(0x[0-9a-f]{4,16})", std::regex::optimize | std::regex::icase); + std::smatch match; + if (!std::regex_search(filename, match, re)) { + return 0; + } + + uint64_t crc64_hash = 0; + const auto number_str = match.str(); + try { + crc64_hash = std::stoull(number_str, nullptr, 16); + } + catch (const std::invalid_argument&) { + std::print(stderr, "Failed to parse {} as a hash\n", filename); + return 0; + } + catch (const std::out_of_range&) { + std::print(stderr, "Out of range while parsing {} as a hash\n", filename); + return 0; + } + return crc64_hash; + } +} + +export class ModfileLoader { + std::filesystem::path file_name; + const std::string TPF_PASSWORD{ + 0x73, 0x2A, 0x63, 0x7D, 0x5F, 0x0A, static_cast(0xA6), static_cast(0xBD), + 0x7D, 0x65, 0x7E, 0x67, 0x61, 0x2A, 0x7F, 0x7F, + 0x74, 0x61, 0x67, 0x5B, 0x60, 0x70, 0x45, 0x74, + 0x5C, 0x22, 0x74, 0x5D, 0x6E, 0x6A, 0x73, 0x41, + 0x77, 0x6E, 0x46, 0x47, 0x77, 0x49, 0x0C, 0x4B, + 0x46, 0x6F + }; + +public: + ModfileLoader(const std::filesystem::path& fileName); + + std::vector GetContents() const; + +private: + + std::vector GetTpfContents() const; + + std::vector GetFileContents() const; + + static void LoadEntries(libzippp::ZipArchive& archive, std::vector& entries); +}; + +ModfileLoader::ModfileLoader(const std::filesystem::path& fileName) +{ + file_name = std::filesystem::absolute(fileName); +} + +std::vector ModfileLoader::GetContents() const +{ + try { + return file_name.wstring().ends_with(L".tpf") ? GetTpfContents() : GetFileContents(); + } + catch (const std::exception&) { + std::print(stderr, "Failed to open mod file: {}\n", file_name.string().c_str()); + } + return {}; +} + +std::vector ModfileLoader::GetTpfContents() const +{ + std::vector entries; + auto tpf_reader = TpfReader(file_name); + const auto buffer = tpf_reader.ReadToEnd(); + const auto zip_archive = libzippp::ZipArchive::fromBuffer(buffer.data(), buffer.size(), false, TPF_PASSWORD); + if (!zip_archive) { + std::print(stderr, "Failed to open tpf file: {} - {} uint8_ts!\n", file_name.string(), buffer.size()); + return {}; + } + zip_archive->setErrorHandlerCallback( + [](const std::string& message, const std::string& strerror, int zip_error_code, int system_error_code) -> void { + std::print(stderr, "GetTpfContents: {} {} {} {}\n", message, strerror, zip_error_code, system_error_code); + }); + zip_archive->open(); + LoadEntries(*zip_archive, entries); + zip_archive->close(); + libzippp::ZipArchive::free(zip_archive); + + return entries; +} + +std::vector ModfileLoader::GetFileContents() const +{ + std::vector entries; + + libzippp::ZipArchive zip_archive(file_name.string()); + zip_archive.open(); + LoadEntries(zip_archive, entries); + zip_archive.close(); + + return entries; +} + +void ParseSimpleArchive(const libzippp::ZipArchive& archive, std::vector& entries) +{ + for (const auto& entry : archive.getEntries()) { + if (!entry.isFile()) + continue; + const auto crc_hash = GetCrcFromFilename(entry.getName()); + if (!crc_hash) + continue; + const auto data_ptr = static_cast(entry.readAsBinary()); + const auto size = entry.getSize(); + std::vector vec(data_ptr, data_ptr + size); + std::filesystem::path tex_name(entry.getName()); + entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string()); + delete[] data_ptr; + } +} + +void ParseTexmodArchive(std::vector& lines, libzippp::ZipArchive& archive, std::vector& entries) +{ + for (const auto& line : lines) { + // 0xC57D73F7|GW.EXE_0xC57D73F7.tga\r\n + // match[1] | match[2] + const static auto address_file_regex = std::regex(R"(^[\\/.]*([^|]+)\|([^\r\n]+))", std::regex::optimize); + std::smatch match; + if (!std::regex_search(line, match, address_file_regex)) + continue; + const auto address_string = match[1].str(); + const auto file_path = match[2].str(); + + const auto crc_hash = GetCrcFromFilename(address_string); + if (!crc_hash) + continue; + + const auto entry = archive.getEntry(file_path); + if (entry.isNull() || !entry.isFile()) + continue; + + const auto data_ptr = static_cast(entry.readAsBinary()); + const auto size = static_cast(entry.getSize()); + std::vector vec(data_ptr, data_ptr + size); + const auto tex_name = std::filesystem::path(entry.getName()); + entries.emplace_back(std::move(vec), crc_hash, tex_name.extension().string()); + delete[] data_ptr; + } +} + +void ModfileLoader::LoadEntries(libzippp::ZipArchive& archive, std::vector& entries) +{ + const auto def_file = archive.getEntry("texmod.def"); + if (def_file.isNull() || !def_file.isFile()) { + ParseSimpleArchive(archive, entries); + } + else { + const auto def = def_file.readAsText(); + std::istringstream iss(def); + std::vector lines; + std::string line; + + while (std::getline(iss, line)) { + lines.push_back(line); + } + + ParseTexmodArchive(lines, archive, entries); + } +} diff --git a/TpfConvert/src/ModfileLoader_TpfReader.ixx b/TpfConvert/src/ModfileLoader_TpfReader.ixx new file mode 100644 index 0000000..5c14a76 --- /dev/null +++ b/TpfConvert/src/ModfileLoader_TpfReader.ixx @@ -0,0 +1,58 @@ +export module ModfileLoader.TpfReader; + +import std; + +export class TpfReader { +public: + TpfReader(const std::filesystem::path& path) + { + file_stream = std::ifstream(path, std::ios::binary); + if (!file_stream.seekg(0, std::ios::end).good() || !file_stream.seekg(0, std::ios::beg).good()) { + throw std::invalid_argument("Provided stream needs to have SEEK set to True"); + } + } + + ~TpfReader() + { + file_stream.close(); + } + + std::vector ReadToEnd() + { + file_stream.seekg(0, std::ios::end); + line_length = file_stream.tellg(); + file_stream.seekg(0, std::ios::beg); // Go to the beginning of the stream + + std::vector data(line_length); + file_stream.read(data.data(), line_length); + for (auto i = 0; i < data.size(); i++) { + data[i] = XOR(data[i], i); + } + + for (int i = data.size() - 1; i > 0 && data[i] != 0; i--) { + data[i] = 0; + } + + // in the other zip libraries, these had to be cut off, with libzip we need to zero them out + // cutting them off makes the archive invalid + //data.resize(last_zero); + + return data; + } + +private: + std::ifstream file_stream; + long line_length = 0; + + [[nodiscard]] char XOR(const char b, const long position) const + { + if (position > line_length - 4) { + return b ^ TPF_XOREven; + } + + return position % 2 == 0 ? b ^ TPF_XOREven : b ^ TPF_XOROdd; + } + + static constexpr char TPF_XOROdd = 0x3F; + static constexpr char TPF_XOREven = static_cast(0xA4); +}; diff --git a/TpfConvert/src/TpfConvert.ixx b/TpfConvert/src/TpfConvert.ixx new file mode 100644 index 0000000..561f1a5 --- /dev/null +++ b/TpfConvert/src/TpfConvert.ixx @@ -0,0 +1,144 @@ +#include +#include +#include +#include + +import std; +import ModfileLoader; +import ; + +struct TexEntry; + +namespace { + IDirect3D9* pD3D = nullptr; + IDirect3DDevice9* pDevice = nullptr; + + bool InitializeDirect3D() + { + pD3D = Direct3DCreate9(D3D_SDK_VERSION); + if (pD3D == nullptr) { + std::print(stderr, "Failed to create Direct3D9 object\n"); + return false; + } + + D3DPRESENT_PARAMETERS d3dpp = {}; + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.hDeviceWindow = GetDesktopWindow(); + + if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetDesktopWindow(), + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))) { + std::print(stderr, "Failed to create Direct3D9 device\n"); + pD3D->Release(); + return false; + } + + return true; + } + + void CleanupDirect3D() + { + if (pDevice) pDevice->Release(); + if (pD3D) pD3D->Release(); + } + + std::pair> SaveAsDDS(const TexEntry& entry) + { + IDirect3DTexture9* pTexture = nullptr; + ID3DXBuffer* pBuffer = nullptr; + + HRESULT hr = D3DXCreateTextureFromFileInMemoryEx(pDevice, + entry.data.data(), entry.data.size(), + D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, + D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_NONE, + D3DX_FILTER_NONE, 0, NULL, NULL, &pTexture); + + if (FAILED(hr)) { + std::print(stderr, "Failed to create texture from memory\n"); + return {}; + } + + hr = D3DXSaveTextureToFileInMemory(&pBuffer, D3DXIFF_DDS, pTexture, NULL); + if (FAILED(hr)) { + std::print(stderr, "Failed to save texture to DDS memory buffer\n"); + pTexture->Release(); + return {}; + } + + std::string dds_filename = std::format("0x{:x}.dds", entry.crc_hash); + std::vector dds_data(pBuffer->GetBufferSize()); + std::memcpy(dds_data.data(), pBuffer->GetBufferPointer(), pBuffer->GetBufferSize()); + + pTexture->Release(); + pBuffer->Release(); + + return {dds_filename, dds_data}; + } + + std::filesystem::path GetExecutablePath() + { + char buffer[MAX_PATH]; + GetModuleFileNameA(NULL, buffer, MAX_PATH); + return std::filesystem::path(buffer).parent_path(); + } +} + +int main(int argc, char* argv[]) +{ + const std::filesystem::path path = argc > 1 ? argv[1] : GetExecutablePath() / "plugins"; + + if (!InitializeDirect3D()) { + return -1; + } + + if (!std::filesystem::exists(path)) { + return 1; + } + + for (const auto& modfile : std::filesystem::directory_iterator(path)) { + if (modfile.is_regular_file() && (modfile.path().extension() == ".tpf" || modfile.path().extension() == ".zip")) { + const auto mod_path = modfile.path(); + if (mod_path.extension() == ".zip" && mod_path.stem().string().ends_with("_")) { + std::print("Skipping previous TpfConvert output: {}\n", mod_path.filename().string()); + continue; + } + else { + std::print("Processing: {}\n", mod_path.filename().string()); + } + + const auto zip_filename = mod_path.parent_path() / (mod_path.stem().string() + "_.zip"); + if (std::filesystem::exists(zip_filename)) { + std::print("{} was already processed\n", mod_path.filename().string()); + continue; + } + libzippp::ZipArchive zip_archive(zip_filename.string()); + + ModfileLoader loader(mod_path); + std::vector>> data_entries; + const auto entries = loader.GetContents(); + + zip_archive.open(libzippp::ZipArchive::Write); + + for (const auto& entry : entries) { + auto [dds_filename, dds_data] = SaveAsDDS(entry); + if (!dds_filename.empty()) { + data_entries.emplace_back(dds_filename, std::move(dds_data)); + } + } + + for (const auto& [filename, data] : data_entries) { + const auto success = zip_archive.addData(filename, data.data(), data.size()); + if (!success) { + std::print(stderr, "Failed to add data to ZIP: {}\n", filename); + } + } + + zip_archive.close(); + std::print("Saved to ZIP: {}\n", zip_filename.string()); + } + } + + CleanupDirect3D(); + + return 0; +} diff --git a/header/Defines.h b/header/Defines.h index d7ec3dc..53d9ad3 100644 --- a/header/Defines.h +++ b/header/Defines.h @@ -1,7 +1,27 @@ #pragma once -//#define HashType DWORD64 -using HashType = DWORD32; +//using HashType = DWORD32; +using HashType = DWORD64; + +struct HashTuple { + HashType crc32; + HashType crc64; + + bool operator==(const HashTuple& other) const noexcept + { + return (other.crc32 == crc32) || (other.crc64 == crc64); + } + + explicit operator bool() const noexcept + { + return crc32 != 0 || crc64 != 0; + } + + explicit operator HashType() const noexcept + { + return crc32 ? crc32 : crc64; + } +}; struct TexEntry { std::vector data{}; @@ -49,4 +69,3 @@ inline void Warning(const char* format, ...) va_end(args); #endif } - diff --git a/header/uMod_IDirect3DCubeTexture9.h b/header/uMod_IDirect3DCubeTexture9.h index 33fbe3d..c42f705 100644 --- a/header/uMod_IDirect3DCubeTexture9.h +++ b/header/uMod_IDirect3DCubeTexture9.h @@ -2,23 +2,20 @@ #include -interface uMod_IDirect3DCubeTexture9 : IDirect3DCubeTexture9 { +interface uMod_IDirect3DCubeTexture9 : IDirect3DCubeTexture9 { uMod_IDirect3DCubeTexture9(IDirect3DCubeTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9) - { - m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game - m_D3Ddev = pIDirect3DDevice9; //device pointer - CrossRef_D3Dtex = nullptr; //cross reference - // fake texture: store the pointer to the original uMod_IDirect3DCubeTexture9 object, needed if a fake texture is unselected - // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, - // thus the fake texture can also be deleted - } + : m_D3Dtex(*ppTex), + m_D3Ddev(pIDirect3DDevice9) + {} + + virtual ~uMod_IDirect3DCubeTexture9() = default; // callback interface IDirect3DCubeTexture9* m_D3Dtex = nullptr; uMod_IDirect3DCubeTexture9* CrossRef_D3Dtex = nullptr; IDirect3DDevice9* m_D3Ddev = nullptr; TextureFileStruct* Reference = nullptr; - HashType Hash = 0u; + HashTuple Hash = {}; bool FAKE = false; // original interface @@ -47,5 +44,5 @@ interface uMod_IDirect3DCubeTexture9 : IDirect3DCubeTexture9 { STDMETHOD(UnlockRect)(D3DCUBEMAP_FACES FaceType, UINT Level) override; - [[nodiscard]] HashType GetHash() const; + [[nodiscard]] HashTuple GetHash() const; }; diff --git a/header/uMod_IDirect3DTexture9.h b/header/uMod_IDirect3DTexture9.h index 6d4d2b0..544805e 100644 --- a/header/uMod_IDirect3DTexture9.h +++ b/header/uMod_IDirect3DTexture9.h @@ -1,27 +1,23 @@ #pragma once #include -struct TextureFileStruct; -interface uMod_IDirect3DTexture9 : public IDirect3DTexture9 { - uMod_IDirect3DTexture9(IDirect3DTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9) - { - m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game - m_D3Ddev = pIDirect3DDevice9; //device pointer - // fake texture: store the pointer to the original uMod_IDirect3DTexture9 object, needed if a fake texture is unselected - // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, - // thus the fake texture can also be deleted - } - virtual ~uMod_IDirect3DTexture9() - { +#include "Defines.h" - } +struct TextureFileStruct; +interface uMod_IDirect3DTexture9 : IDirect3DTexture9 { + uMod_IDirect3DTexture9(IDirect3DTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9) + : m_D3Dtex(*ppTex), + m_D3Ddev(pIDirect3DDevice9) + {} + + virtual ~uMod_IDirect3DTexture9() = default; // callback interface IDirect3DTexture9* m_D3Dtex = nullptr; uMod_IDirect3DTexture9* CrossRef_D3Dtex = nullptr; IDirect3DDevice9* m_D3Ddev = nullptr; TextureFileStruct* Reference = nullptr; - HashType Hash = 0u; + HashTuple Hash = {}; bool FAKE = false; // original interface @@ -48,5 +44,5 @@ interface uMod_IDirect3DTexture9 : public IDirect3DTexture9 { STDMETHOD(UnlockRect)(UINT Level) override; STDMETHOD(AddDirtyRect)(CONST RECT* pDirtyRect) override; - [[nodiscard]] HashType GetHash() const; + [[nodiscard]] HashTuple GetHash() const; }; diff --git a/header/uMod_IDirect3DVolumeTexture9.h b/header/uMod_IDirect3DVolumeTexture9.h index fcc0029..3e6b4e6 100644 --- a/header/uMod_IDirect3DVolumeTexture9.h +++ b/header/uMod_IDirect3DVolumeTexture9.h @@ -4,21 +4,18 @@ interface uMod_IDirect3DVolumeTexture9 : IDirect3DVolumeTexture9 { uMod_IDirect3DVolumeTexture9(IDirect3DVolumeTexture9** ppTex, IDirect3DDevice9* pIDirect3DDevice9) - { - m_D3Dtex = *ppTex; //Texture which will be displayed and will be passed to the game - m_D3Ddev = pIDirect3DDevice9; //device pointer - CrossRef_D3Dtex = nullptr; //cross reference - // fake texture: store the pointer to the original uMod_IDirect3DVolumeTexture9 object, needed if a fake texture is unselected - // original texture: stores the pointer to the fake texture object, is needed if original texture is deleted, - // thus the fake texture can also be deleted - } + : m_D3Dtex(*ppTex), + m_D3Ddev(pIDirect3DDevice9) + {} + + virtual ~uMod_IDirect3DVolumeTexture9() = default; // callback interface IDirect3DVolumeTexture9* m_D3Dtex = nullptr; uMod_IDirect3DVolumeTexture9* CrossRef_D3Dtex = nullptr; IDirect3DDevice9* m_D3Ddev = nullptr; TextureFileStruct* Reference = nullptr; - HashType Hash = 0u; + HashTuple Hash = {}; bool FAKE = false; // original interface @@ -46,5 +43,5 @@ interface uMod_IDirect3DVolumeTexture9 : IDirect3DVolumeTexture9 { STDMETHOD(UnlockBox)(UINT Level) override; - [[nodiscard]] HashType GetHash() const; + [[nodiscard]] HashTuple GetHash() const; }; diff --git a/modules/ModfileLoader.ixx b/modules/ModfileLoader.ixx index 33cd18c..c953479 100644 --- a/modules/ModfileLoader.ixx +++ b/modules/ModfileLoader.ixx @@ -5,18 +5,15 @@ module; export module ModfileLoader; -import ; -import ; -import ; +import std; import ; -import ; -import ; -import ; import ModfileLoader.TpfReader; import TextureFunction; namespace { - uint32_t GetCrcFromFilename(const std::string& filename) { + bool use_64_bit_crc = false; + + HashType GetCrcFromFilename(const std::string& filename) { const static std::regex re(R"(0x[0-9a-f]{4,16})", std::regex::optimize | std::regex::icase); std::smatch match; if (!std::regex_search(filename, match, re)) { @@ -24,19 +21,26 @@ namespace { } uint64_t crc64_hash = 0; + const auto number_str = match.str(); try { - crc64_hash = std::stoull(match.str(), nullptr, 16); + crc64_hash = std::stoull(number_str, nullptr, 16); } catch (const std::invalid_argument&) { - Warning("Failed to parse %s as a hash", filename.c_str()); + Warning("Failed to parse %s as a hash\n", filename.c_str()); return 0; } catch (const std::out_of_range&) { - Message("Out of range while parsing %s as a hash", filename.c_str()); + Warning("Out of range while parsing %s as a hash\n", filename.c_str()); return 0; } - // Truncate the higher 32-bits - return (uint32_t)(crc64_hash & 0xFFFFFFFF); + use_64_bit_crc = use_64_bit_crc || crc64_hash > 0xFFFFFFFF; + return crc64_hash; + } +} + +namespace HashCheck { + export bool Use64BitCrc() { + return use_64_bit_crc; } } @@ -54,15 +58,15 @@ export class ModfileLoader { public: ModfileLoader(const std::filesystem::path& fileName); - std::vector GetContents(); + std::vector GetContents() const; private: - std::vector GetTpfContents(); + std::vector GetTpfContents() const; - std::vector GetFileContents(); + std::vector GetFileContents() const; - void LoadEntries(libzippp::ZipArchive& archive, std::vector& entries); + static void LoadEntries(libzippp::ZipArchive& archive, std::vector& entries); }; ModfileLoader::ModfileLoader(const std::filesystem::path& fileName) @@ -70,7 +74,7 @@ ModfileLoader::ModfileLoader(const std::filesystem::path& fileName) file_name = std::filesystem::absolute(fileName); } -std::vector ModfileLoader::GetContents() +std::vector ModfileLoader::GetContents() const { try { return file_name.wstring().ends_with(L".tpf") ? GetTpfContents() : GetFileContents(); @@ -81,7 +85,7 @@ std::vector ModfileLoader::GetContents() return {}; } -std::vector ModfileLoader::GetTpfContents() +std::vector ModfileLoader::GetTpfContents() const { std::vector entries; auto tpf_reader = TpfReader(file_name); @@ -103,7 +107,7 @@ std::vector ModfileLoader::GetTpfContents() return entries; } -std::vector ModfileLoader::GetFileContents() +std::vector ModfileLoader::GetFileContents() const { std::vector entries; diff --git a/modules/ModfileLoader_TpfReader.ixx b/modules/ModfileLoader_TpfReader.ixx index 5a39c69..5c14a76 100644 --- a/modules/ModfileLoader_TpfReader.ixx +++ b/modules/ModfileLoader_TpfReader.ixx @@ -1,9 +1,6 @@ export module ModfileLoader.TpfReader; -import ; -import ; -import ; -import ; +import std; export class TpfReader { public: @@ -45,7 +42,7 @@ public: private: std::ifstream file_stream; - long line_length; + long line_length = 0; [[nodiscard]] char XOR(const char b, const long position) const { diff --git a/modules/TextureClient.ixx b/modules/TextureClient.ixx index 8d7f7e7..9d69832 100644 --- a/modules/TextureClient.ixx +++ b/modules/TextureClient.ixx @@ -34,7 +34,6 @@ public: std::vector OriginalCubeTextures; // stores the pointer to the uMod_IDirect3DCubeTexture9 objects created by the game - private: IDirect3DDevice9* D3D9Device; // Cached info about whether this id a dx9ex device or not; used for proxy functions @@ -340,9 +339,10 @@ int TextureClient::LookUpToMod(uModTexturePtr auto pTexture) if (pTexture->CrossRef_D3Dtex != nullptr) return ret; // bug, this texture is already switched - const auto found = modded_textures.find(pTexture->Hash); + auto found = modded_textures.find(pTexture->Hash.crc32); if (found == modded_textures.end()) - return ret; + if (found = modded_textures.find(pTexture->Hash.crc64), !pTexture->Hash.crc64 || found == modded_textures.end()) + return ret; const auto textureFileStruct = found->second; diff --git a/modules/TextureFunction.ixx b/modules/TextureFunction.ixx index 4a995c3..4195a6b 100644 --- a/modules/TextureFunction.ixx +++ b/modules/TextureFunction.ixx @@ -45,15 +45,114 @@ int SwitchTextures(T pTexture1, T pTexture2) return RETURN_TEXTURE_NOT_SWITCHED; } +constexpr auto crctab64 = std::to_array({ + 0x0000000000000000ULL, 0x7ad870c830358979ULL, 0xf5b0e190606b12f2ULL, + 0x8f689158505e9b8bULL, 0xc038e5739841b68fULL, 0xbae095bba8743ff6ULL, + 0x358804e3f82aa47dULL, 0x4f50742bc81f2d04ULL, 0xab28ecb46814fe75ULL, + 0xd1f09c7c5821770cULL, 0x5e980d24087fec87ULL, 0x24407dec384a65feULL, + 0x6b1009c7f05548faULL, 0x11c8790fc060c183ULL, 0x9ea0e857903e5a08ULL, + 0xe478989fa00bd371ULL, 0x7d08ff3b88be6f81ULL, 0x07d08ff3b88be6f8ULL, + 0x88b81eabe8d57d73ULL, 0xf2606e63d8e0f40aULL, 0xbd301a4810ffd90eULL, + 0xc7e86a8020ca5077ULL, 0x4880fbd87094cbfcULL, 0x32588b1040a14285ULL, + 0xd620138fe0aa91f4ULL, 0xacf86347d09f188dULL, 0x2390f21f80c18306ULL, + 0x594882d7b0f40a7fULL, 0x1618f6fc78eb277bULL, 0x6cc0863448deae02ULL, + 0xe3a8176c18803589ULL, 0x997067a428b5bcf0ULL, 0xfa11fe77117cdf02ULL, + 0x80c98ebf2149567bULL, 0x0fa11fe77117cdf0ULL, 0x75796f2f41224489ULL, + 0x3a291b04893d698dULL, 0x40f16bccb908e0f4ULL, 0xcf99fa94e9567b7fULL, + 0xb5418a5cd963f206ULL, 0x513912c379682177ULL, 0x2be1620b495da80eULL, + 0xa489f35319033385ULL, 0xde51839b2936bafcULL, 0x9101f7b0e12997f8ULL, + 0xebd98778d11c1e81ULL, 0x64b116208142850aULL, 0x1e6966e8b1770c73ULL, + 0x8719014c99c2b083ULL, 0xfdc17184a9f739faULL, 0x72a9e0dcf9a9a271ULL, + 0x08719014c99c2b08ULL, 0x4721e43f0183060cULL, 0x3df994f731b68f75ULL, + 0xb29105af61e814feULL, 0xc849756751dd9d87ULL, 0x2c31edf8f1d64ef6ULL, + 0x56e99d30c1e3c78fULL, 0xd9810c6891bd5c04ULL, 0xa3597ca0a188d57dULL, + 0xec09088b6997f879ULL, 0x96d1784359a27100ULL, 0x19b9e91b09fcea8bULL, + 0x636199d339c963f2ULL, 0xdf7adabd7a6e2d6fULL, 0xa5a2aa754a5ba416ULL, + 0x2aca3b2d1a053f9dULL, 0x50124be52a30b6e4ULL, 0x1f423fcee22f9be0ULL, + 0x659a4f06d21a1299ULL, 0xeaf2de5e82448912ULL, 0x902aae96b271006bULL, + 0x74523609127ad31aULL, 0x0e8a46c1224f5a63ULL, 0x81e2d7997211c1e8ULL, + 0xfb3aa75142244891ULL, 0xb46ad37a8a3b6595ULL, 0xceb2a3b2ba0eececULL, + 0x41da32eaea507767ULL, 0x3b024222da65fe1eULL, 0xa2722586f2d042eeULL, + 0xd8aa554ec2e5cb97ULL, 0x57c2c41692bb501cULL, 0x2d1ab4dea28ed965ULL, + 0x624ac0f56a91f461ULL, 0x1892b03d5aa47d18ULL, 0x97fa21650afae693ULL, + 0xed2251ad3acf6feaULL, 0x095ac9329ac4bc9bULL, 0x7382b9faaaf135e2ULL, + 0xfcea28a2faafae69ULL, 0x8632586aca9a2710ULL, 0xc9622c4102850a14ULL, + 0xb3ba5c8932b0836dULL, 0x3cd2cdd162ee18e6ULL, 0x460abd1952db919fULL, + 0x256b24ca6b12f26dULL, 0x5fb354025b277b14ULL, 0xd0dbc55a0b79e09fULL, + 0xaa03b5923b4c69e6ULL, 0xe553c1b9f35344e2ULL, 0x9f8bb171c366cd9bULL, + 0x10e3202993385610ULL, 0x6a3b50e1a30ddf69ULL, 0x8e43c87e03060c18ULL, + 0xf49bb8b633338561ULL, 0x7bf329ee636d1eeaULL, 0x012b592653589793ULL, + 0x4e7b2d0d9b47ba97ULL, 0x34a35dc5ab7233eeULL, 0xbbcbcc9dfb2ca865ULL, + 0xc113bc55cb19211cULL, 0x5863dbf1e3ac9decULL, 0x22bbab39d3991495ULL, + 0xadd33a6183c78f1eULL, 0xd70b4aa9b3f20667ULL, 0x985b3e827bed2b63ULL, + 0xe2834e4a4bd8a21aULL, 0x6debdf121b863991ULL, 0x1733afda2bb3b0e8ULL, + 0xf34b37458bb86399ULL, 0x8993478dbb8deae0ULL, 0x06fbd6d5ebd3716bULL, + 0x7c23a61ddbe6f812ULL, 0x3373d23613f9d516ULL, 0x49aba2fe23cc5c6fULL, + 0xc6c333a67392c7e4ULL, 0xbc1b436e43a74e9dULL, 0x95ac9329ac4bc9b5ULL, + 0xef74e3e19c7e40ccULL, 0x601c72b9cc20db47ULL, 0x1ac40271fc15523eULL, + 0x5594765a340a7f3aULL, 0x2f4c0692043ff643ULL, 0xa02497ca54616dc8ULL, + 0xdafce7026454e4b1ULL, 0x3e847f9dc45f37c0ULL, 0x445c0f55f46abeb9ULL, + 0xcb349e0da4342532ULL, 0xb1eceec59401ac4bULL, 0xfebc9aee5c1e814fULL, + 0x8464ea266c2b0836ULL, 0x0b0c7b7e3c7593bdULL, 0x71d40bb60c401ac4ULL, + 0xe8a46c1224f5a634ULL, 0x927c1cda14c02f4dULL, 0x1d148d82449eb4c6ULL, + 0x67ccfd4a74ab3dbfULL, 0x289c8961bcb410bbULL, 0x5244f9a98c8199c2ULL, + 0xdd2c68f1dcdf0249ULL, 0xa7f41839ecea8b30ULL, 0x438c80a64ce15841ULL, + 0x3954f06e7cd4d138ULL, 0xb63c61362c8a4ab3ULL, 0xcce411fe1cbfc3caULL, + 0x83b465d5d4a0eeceULL, 0xf96c151de49567b7ULL, 0x76048445b4cbfc3cULL, + 0x0cdcf48d84fe7545ULL, 0x6fbd6d5ebd3716b7ULL, 0x15651d968d029fceULL, + 0x9a0d8ccedd5c0445ULL, 0xe0d5fc06ed698d3cULL, 0xaf85882d2576a038ULL, + 0xd55df8e515432941ULL, 0x5a3569bd451db2caULL, 0x20ed197575283bb3ULL, + 0xc49581ead523e8c2ULL, 0xbe4df122e51661bbULL, 0x3125607ab548fa30ULL, + 0x4bfd10b2857d7349ULL, 0x04ad64994d625e4dULL, 0x7e7514517d57d734ULL, + 0xf11d85092d094cbfULL, 0x8bc5f5c11d3cc5c6ULL, 0x12b5926535897936ULL, + 0x686de2ad05bcf04fULL, 0xe70573f555e26bc4ULL, 0x9ddd033d65d7e2bdULL, + 0xd28d7716adc8cfb9ULL, 0xa85507de9dfd46c0ULL, 0x273d9686cda3dd4bULL, + 0x5de5e64efd965432ULL, 0xb99d7ed15d9d8743ULL, 0xc3450e196da80e3aULL, + 0x4c2d9f413df695b1ULL, 0x36f5ef890dc31cc8ULL, 0x79a59ba2c5dc31ccULL, + 0x037deb6af5e9b8b5ULL, 0x8c157a32a5b7233eULL, 0xf6cd0afa9582aa47ULL, + 0x4ad64994d625e4daULL, 0x300e395ce6106da3ULL, 0xbf66a804b64ef628ULL, + 0xc5bed8cc867b7f51ULL, 0x8aeeace74e645255ULL, 0xf036dc2f7e51db2cULL, + 0x7f5e4d772e0f40a7ULL, 0x05863dbf1e3ac9deULL, 0xe1fea520be311aafULL, + 0x9b26d5e88e0493d6ULL, 0x144e44b0de5a085dULL, 0x6e963478ee6f8124ULL, + 0x21c640532670ac20ULL, 0x5b1e309b16452559ULL, 0xd476a1c3461bbed2ULL, + 0xaeaed10b762e37abULL, 0x37deb6af5e9b8b5bULL, 0x4d06c6676eae0222ULL, + 0xc26e573f3ef099a9ULL, 0xb8b627f70ec510d0ULL, 0xf7e653dcc6da3dd4ULL, + 0x8d3e2314f6efb4adULL, 0x0256b24ca6b12f26ULL, 0x788ec2849684a65fULL, + 0x9cf65a1b368f752eULL, 0xe62e2ad306bafc57ULL, 0x6946bb8b56e467dcULL, + 0x139ecb4366d1eea5ULL, 0x5ccebf68aecec3a1ULL, 0x2616cfa09efb4ad8ULL, + 0xa97e5ef8cea5d153ULL, 0xd3a62e30fe90582aULL, 0xb0c7b7e3c7593bd8ULL, + 0xca1fc72bf76cb2a1ULL, 0x45775673a732292aULL, 0x3faf26bb9707a053ULL, + 0x70ff52905f188d57ULL, 0x0a2722586f2d042eULL, 0x854fb3003f739fa5ULL, + 0xff97c3c80f4616dcULL, 0x1bef5b57af4dc5adULL, 0x61372b9f9f784cd4ULL, + 0xee5fbac7cf26d75fULL, 0x9487ca0fff135e26ULL, 0xdbd7be24370c7322ULL, + 0xa10fceec0739fa5bULL, 0x2e675fb4576761d0ULL, 0x54bf2f7c6752e8a9ULL, + 0xcdcf48d84fe75459ULL, 0xb71738107fd2dd20ULL, 0x387fa9482f8c46abULL, + 0x42a7d9801fb9cfd2ULL, 0x0df7adabd7a6e2d6ULL, 0x772fdd63e7936bafULL, + 0xf8474c3bb7cdf024ULL, 0x829f3cf387f8795dULL, 0x66e7a46c27f3aa2cULL, + 0x1c3fd4a417c62355ULL, 0x935745fc4798b8deULL, 0xe98f353477ad31a7ULL, + 0xa6df411fbfb21ca3ULL, 0xdc0731d78f8795daULL, 0x536fa08fdfd90e51ULL, + 0x29b7d047efec8728ULL +}); + export namespace TextureFunction { - unsigned int GetCRC32(char* pcDatabuf, unsigned int ulDatalen) + uint64_t get_crc64(const char* data, unsigned int length) { + uint64_t crc = 0xFFFFFFFFFFFFFFFFULL; + + while (length--) { + crc = crctab64[(crc ^ *data++) & 0xFF] ^ (crc >> 8); + } + + return crc; + } + + uint32_t get_crc32(const char* data_ptr, const int length) { constexpr static auto crc32_poly = 0xEDB88320u; constexpr static auto ul_crc_in = 0xffffffff; unsigned int crc = ul_crc_in; - for (unsigned int idx = 0u; idx < ulDatalen; idx++) { - unsigned int data = *pcDatabuf++; + for (unsigned int idx = 0u; idx < length; idx++) { + unsigned int data = *data_ptr++; for (unsigned int bit = 0u; bit < 8u; bit++, data >>= 1) { crc = crc >> 1 ^ ((crc ^ data) & 1 ? crc32_poly : 0); } @@ -283,12 +382,7 @@ export namespace TextureFunction { else { hr = DirectX::LoadFromWICMemory(entry.data.data(), entry.data.size(), DirectX::WIC_FLAGS_NONE, nullptr, image); if (image.GetMetadata().format == DXGI_FORMAT_B8G8R8X8_UNORM) { - // todo: this is undefined behaviour, but we must force them to be interpreted as BGRA instead of BGRX - const_cast(image.GetMetadata().format) = DXGI_FORMAT_B8G8R8A8_UNORM; - const auto images = image.GetImages(); - for (int i = 0; i < image.GetImageCount(); ++i) { - const_cast(images[i].format) = DXGI_FORMAT_B8G8R8A8_UNORM; - } + image.OverrideFormat(DXGI_FORMAT_B8G8R8A8_UNORM); } } entry.data.clear(); diff --git a/source/uMod_IDirect3DCubeTexture9.cpp b/source/uMod_IDirect3DCubeTexture9.cpp index bfb50c7..236b3eb 100644 --- a/source/uMod_IDirect3DCubeTexture9.cpp +++ b/source/uMod_IDirect3DCubeTexture9.cpp @@ -1,7 +1,8 @@ #include "Main.h" -import TextureFunction; +import ModfileLoader; import TextureClient; +import TextureFunction; //this function yields for the non switched texture object HRESULT APIENTRY uMod_IDirect3DCubeTexture9::QueryInterface(REFIID riid, void** ppvObj) @@ -227,10 +228,10 @@ HRESULT APIENTRY uMod_IDirect3DCubeTexture9::UnlockRect(D3DCUBEMAP_FACES FaceTyp return m_D3Dtex->UnlockRect(FaceType, Level); } -HashType uMod_IDirect3DCubeTexture9::GetHash() const +HashTuple uMod_IDirect3DCubeTexture9::GetHash() const { if (FAKE) { - return 0; + return {}; } IDirect3DCubeTexture9* pTexture = m_D3Dtex; if (CrossRef_D3Dtex != nullptr) { @@ -244,7 +245,7 @@ HashType uMod_IDirect3DCubeTexture9::GetHash() const if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture { Warning("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetLevelDesc \n"); - return 0; + return {}; } Message("uMod_IDirect3DCubeTexture9::GetHash() (%d %d) %d\n", desc.Width, desc.Height, desc.Format); @@ -254,17 +255,18 @@ HashType uMod_IDirect3DCubeTexture9::GetHash() const Warning("uMod_IDirect3DCubeTexture9::GetHash() Failed: LockRect 1\n"); if (pTexture->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &pResolvedSurface) != D3D_OK) { Warning("uMod_IDirect3DCubeTexture9::GetHash() Failed: GetSurfaceLevel\n"); - return 0; + return {}; } if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) { pResolvedSurface->Release(); Warning("uMod_IDirect3DCubeTexture9::GetHash() Failed: LockRect 2\n"); - return 0; + return {}; } } const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8; - const auto hash = TextureFunction::GetCRC32(static_cast(d3dlr.pBits), size); //calculate the crc32 of the texture + const auto crc32 = TextureFunction::get_crc32(static_cast(d3dlr.pBits), size); + const auto crc64 = HashCheck::Use64BitCrc() ? TextureFunction::get_crc64(static_cast(d3dlr.pBits), size) : 0; // Only release surfaces after we're finished with d3dlr if (pResolvedSurface != nullptr) { @@ -275,6 +277,7 @@ HashType uMod_IDirect3DCubeTexture9::GetHash() const pTexture->UnlockRect(D3DCUBEMAP_FACE_POSITIVE_X, 0); //unlock the raw data } - Message("uMod_IDirect3DCubeTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size); - return hash; + Message("uMod_IDirect3DCubeTexture9::GetHash() %#lX (%d %d) %d = %d\n", crc32, desc.Width, desc.Height, desc.Format, size); + Message("uMod_IDirect3DCubeTexture9::GetHash() %#llX (%d %d) %d = %d\n", crc64, desc.Width, desc.Height, desc.Format, size); + return {crc32, crc64}; } diff --git a/source/uMod_IDirect3DDevice9.cpp b/source/uMod_IDirect3DDevice9.cpp index 9af66aa..5c261b2 100644 --- a/source/uMod_IDirect3DDevice9.cpp +++ b/source/uMod_IDirect3DDevice9.cpp @@ -275,10 +275,11 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu uMod_IDirect3DCubeTexture9* pSourceCube = nullptr; IDirect3DBaseTexture9* cpy; if (pSourceTexture != nullptr) { + const auto hash = pSource->GetHash(); switch (pSourceTexture->QueryInterface(IID_IDirect3D9, (void**)&cpy)) { case 0x01000000L: { pSource = static_cast(pSourceTexture); - if (const auto hash = pSource->GetHash()) { + if (hash) { if (hash != pSource->Hash) // this hash has changed !! { pSource->Hash = hash; @@ -303,7 +304,7 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu } case 0x01000001L: { pSourceVolume = static_cast(pSourceTexture); - if (const auto hash = pSource->GetHash()) { + if (hash) { if (hash != pSourceVolume->Hash) // this hash has changed !! { pSourceVolume->Hash = hash; @@ -328,7 +329,7 @@ HRESULT uMod_IDirect3DDevice9::UpdateTexture(IDirect3DBaseTexture9* pSourceTextu } case 0x01000002L: { pSourceCube = static_cast(pSourceTexture); - if (const auto hash = pSourceCube->GetHash()) { + if (hash) { if (hash != pSourceCube->Hash) // this hash has changed !! { pSourceCube->Hash = hash; diff --git a/source/uMod_IDirect3DTexture9.cpp b/source/uMod_IDirect3DTexture9.cpp index 8a9f7b3..5bfa5af 100644 --- a/source/uMod_IDirect3DTexture9.cpp +++ b/source/uMod_IDirect3DTexture9.cpp @@ -1,7 +1,8 @@ #include "Main.h" -import TextureFunction; +import ModfileLoader; import TextureClient; +import TextureFunction; //this function yields for the non switched texture object HRESULT APIENTRY uMod_IDirect3DTexture9::QueryInterface(REFIID riid, void** ppvObj) @@ -203,7 +204,7 @@ HRESULT APIENTRY uMod_IDirect3DTexture9::GetSurfaceLevel(UINT Level, IDirect3DSu } //this function yields for the non switched texture object -HRESULT APIENTRY uMod_IDirect3DTexture9::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect, DWORD Flags) +HRESULT APIENTRY uMod_IDirect3DTexture9::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { if (CrossRef_D3Dtex != nullptr) { return CrossRef_D3Dtex->m_D3Dtex->LockRect(Level, pLockedRect, pRect, Flags); @@ -230,7 +231,7 @@ HRESULT APIENTRY uMod_IDirect3DTexture9::AddDirtyRect(CONST RECT* pDirtyRect) } -HashType uMod_IDirect3DTexture9::GetHash() const +HashTuple uMod_IDirect3DTexture9::GetHash() const { ASSERT(!FAKE); IDirect3DTexture9* pTexture = m_D3Dtex; @@ -247,7 +248,7 @@ HashType uMod_IDirect3DTexture9::GetHash() const if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture { Warning("uMod_IDirect3DTexture9::GetHash() Failed: GetLevelDesc \n"); - return 0; + return {}; } Message("uMod_IDirect3DTexture9::GetHash() (%d %d) %d\n", desc.Width, desc.Height, desc.Format); @@ -260,7 +261,7 @@ HashType uMod_IDirect3DTexture9::GetHash() const IDirect3DSurface9* pSurfaceLevel_orig = nullptr; if (pTexture->GetSurfaceLevel(0, &pSurfaceLevel_orig) != D3D_OK) { Warning("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel 1 (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { @@ -268,12 +269,12 @@ HashType uMod_IDirect3DTexture9::GetHash() const if (D3D_OK != m_D3Ddev->CreateRenderTarget(desc.Width, desc.Height, desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &pResolvedSurface, nullptr)) { pSurfaceLevel_orig->Release(); Warning("uMod_IDirect3DTexture9::GetHash() Failed: CreateRenderTarget (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } if (D3D_OK != m_D3Ddev->StretchRect(pSurfaceLevel_orig, nullptr, pResolvedSurface, nullptr, D3DTEXF_NONE)) { pSurfaceLevel_orig->Release(); Warning("uMod_IDirect3DTexture9::GetHash() Failed: StretchRect (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } pSurfaceLevel_orig = pResolvedSurface; @@ -285,7 +286,7 @@ HashType uMod_IDirect3DTexture9::GetHash() const pResolvedSurface->Release(); } Warning("uMod_IDirect3DTexture9::GetHash() Failed: CreateOffscreenPlainSurface (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } if (D3D_OK != m_D3Ddev->GetRenderTargetData(pSurfaceLevel_orig, pOffscreenSurface)) { @@ -295,7 +296,7 @@ HashType uMod_IDirect3DTexture9::GetHash() const } pOffscreenSurface->Release(); Warning("uMod_IDirect3DTexture9::GetHash() Failed: GetRenderTargetData (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } pSurfaceLevel_orig->Release(); @@ -305,24 +306,25 @@ HashType uMod_IDirect3DTexture9::GetHash() const } pOffscreenSurface->Release(); Warning("uMod_IDirect3DTexture9::GetHash() Failed: LockRect (D3DPOOL_DEFAULT)\n"); - return 0; + return {}; } } else if (pTexture->LockRect(0, &d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) { Warning("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 1\n"); if (pTexture->GetSurfaceLevel(0, &pResolvedSurface) != D3D_OK) { Warning("uMod_IDirect3DTexture9::GetHash() Failed: GetSurfaceLevel\n"); - return 0; + return {}; } if (pResolvedSurface->LockRect(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) { pResolvedSurface->Release(); Warning("uMod_IDirect3DTexture9::GetHash() Failed: LockRect 2\n"); - return 0; + return {}; } } const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height) / 8; - const auto hash = TextureFunction::GetCRC32(static_cast(d3dlr.pBits), size); //calculate the crc32 of the texture + const auto crc32 = TextureFunction::get_crc32(static_cast(d3dlr.pBits), size); + const auto crc64 = HashCheck::Use64BitCrc() ? TextureFunction::get_crc64(static_cast(d3dlr.pBits), size) : 0; // Only release surfaces after we're finished with d3dlr if (pOffscreenSurface != nullptr) { @@ -339,6 +341,8 @@ HashType uMod_IDirect3DTexture9::GetHash() const else { pTexture->UnlockRect(0); } - Message("uMod_IDirect3DTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size); - return hash; + + Message("uMod_IDirect3DTexture9::GetHash() crc32 %#lX (%d %d) %d = %d\n", crc32, desc.Width, desc.Height, desc.Format, size); + Message("uMod_IDirect3DTexture9::GetHash() crc64 %#llX (%d %d) %d = %d\n", crc64, desc.Width, desc.Height, desc.Format, size); + return {crc32, crc64}; } diff --git a/source/uMod_IDirect3DVolumeTexture9.cpp b/source/uMod_IDirect3DVolumeTexture9.cpp index 51ad395..8cfcf3c 100644 --- a/source/uMod_IDirect3DVolumeTexture9.cpp +++ b/source/uMod_IDirect3DVolumeTexture9.cpp @@ -1,6 +1,8 @@ #include "Main.h" -import TextureFunction; + +import ModfileLoader; import TextureClient; +import TextureFunction; HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::QueryInterface(REFIID riid, void** ppvObj) { @@ -225,10 +227,10 @@ HRESULT APIENTRY uMod_IDirect3DVolumeTexture9::UnlockBox(UINT Level) return m_D3Dtex->UnlockBox(Level); } -HashType uMod_IDirect3DVolumeTexture9::GetHash() const +HashTuple uMod_IDirect3DVolumeTexture9::GetHash() const { if (FAKE) { - return 0; + return {}; } IDirect3DVolumeTexture9* pTexture = m_D3Dtex; if (CrossRef_D3Dtex != nullptr) { @@ -242,7 +244,7 @@ HashType uMod_IDirect3DVolumeTexture9::GetHash() const if (pTexture->GetLevelDesc(0, &desc) != D3D_OK) //get the format and the size of the texture { Warning("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetLevelDesc \n"); - return 0; + return {}; } Message("uMod_IDirect3DVolumeTexture9::GetHash() (%d %d %d) %d\n", desc.Width, desc.Height, desc.Depth, desc.Format); @@ -250,17 +252,18 @@ HashType uMod_IDirect3DVolumeTexture9::GetHash() const Warning("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect 1\n"); if (pTexture->GetVolumeLevel(0, &pResolvedSurface) != D3D_OK) { Warning("uMod_IDirect3DVolumeTexture9::GetHash() Failed: GetSurfaceLevel\n"); - return 0; + return {}; } if (pResolvedSurface->LockBox(&d3dlr, nullptr, D3DLOCK_READONLY) != D3D_OK) { pResolvedSurface->Release(); Warning("uMod_IDirect3DVolumeTexture9::GetHash() Failed: LockRect 2\n"); - return 0; + return {}; } } const int size = (TextureFunction::GetBitsFromFormat(desc.Format) * desc.Width * desc.Height * desc.Depth) / 8; - const auto hash = TextureFunction::GetCRC32(static_cast(d3dlr.pBits), size); //calculate the crc32 of the texture + const auto crc32 = TextureFunction::get_crc32(static_cast(d3dlr.pBits), size); + const auto crc64 = HashCheck::Use64BitCrc() ? TextureFunction::get_crc64(static_cast(d3dlr.pBits), size) : 0; // Only release surfaces after we're finished with d3dlr if (pResolvedSurface != nullptr) { @@ -271,6 +274,7 @@ HashType uMod_IDirect3DVolumeTexture9::GetHash() const pTexture->UnlockBox(0); } - Message("uMod_IDirect3DVolumeTexture9::GetHash() %#lX (%d %d) %d = %d\n", hash, desc.Width, desc.Height, desc.Format, size); - return hash; + Message("uMod_IDirect3DVolumeTexture9::GetHash() crc32 %#lX (%d %d) %d = %d\n", crc32, desc.Width, desc.Height, desc.Format, size); + Message("uMod_IDirect3DVolumeTexture9::GetHash() crc64 %#llX (%d %d) %d = %d\n", crc64, desc.Width, desc.Height, desc.Format, size); + return {crc32, crc64}; } diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000..b9734a0 --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,14 @@ +{ + "default-registry": { + "kind": "git", + "baseline": "7f9f0e44db287e8e67c0e888141bfa200ab45121", + "repository": "https://github.com/microsoft/vcpkg" + }, + "registries": [ + { + "kind": "artifact", + "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", + "name": "microsoft" + } + ] +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..bacf0fa --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "dxsdk-d3dx" + ] +}