From 6dca7c6029a7308a63ea1da78c5c819c41dcc7e6 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 13 Mar 2025 10:34:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=20jeweha=20patch=20=E7=A7=BB=E6=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dllmain.cpp | 46 ++++++++++++-- vfs.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++++++--- vfs.hpp | 7 ++- 3 files changed, 208 insertions(+), 16 deletions(-) diff --git a/dllmain.cpp b/dllmain.cpp index 052b739..8f15459 100644 --- a/dllmain.cpp +++ b/dllmain.cpp @@ -17,6 +17,10 @@ static BOOL(WINAPI *TrueCloseHandle)(HANDLE hObject) = CloseHandle; static DWORD(WINAPI *TrueGetFileSize)(HANDLE hFile, LPDWORD lpFileSizeHigh) = GetFileSize; static decltype(GetFileSizeEx) *TrueGetFileSizeEx = GetFileSizeEx; static DWORD(WINAPI *TrueSetFilePointer)(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) = SetFilePointer; +static decltype(SetFilePointerEx) *TrueSetFilePointerEx = SetFilePointerEx; +static decltype(GetFileType) *TrueGetFileType = GetFileType; +static decltype(GetFileAttributesW) *TrueGetFileAttributesW = GetFileAttributesW; +static decltype(GetFileAttributesExW) *TrueGetFileAttributesExW = GetFileAttributesExW; static Config config; static std::wstring defaultFont; @@ -143,6 +147,34 @@ DWORD WINAPI HookedSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDi return TrueSetFilePointer(hFile, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod); } +BOOL WINAPI HookedSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) { + if (vfs.ContainsHandle(hFile)) { + return vfs.SetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod); + } + return TrueSetFilePointerEx(hFile, liDistanceToMove, lpNewFilePointer, dwMoveMethod); +} + +DWORD WINAPI HookedGetFileType(HANDLE hFile) { + if (vfs.ContainsHandle(hFile)) { + return FILE_TYPE_DISK; + } + return TrueGetFileType(hFile); +} + +DWORD WINAPI HookedGetFileAttributesW(LPCWSTR lpFileName) { + if (vfs.ContainsFile(lpFileName)) { + return FILE_ATTRIBUTE_READONLY; + } + return TrueGetFileAttributesW(lpFileName); +} + +BOOL WINAPI HookedGetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) { + if (vfs.ContainsFile(lpFileName)) { + return vfs.GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation); + } + return TrueGetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation); +} + extern "C" __declspec(dllexport) void Attach() { config.Load("config.txt"); if (!wchar_util::str_to_wstr(defaultFont, config.configs["defaultFont"], CP_UTF8)) { @@ -151,11 +183,7 @@ extern "C" __declspec(dllexport) void Attach() { if (defaultFont.empty()) { defaultFont = L"微软雅黑"; } - if (!vfs.AddArchive("jewena-chs.dat")) { - MessageBoxW(NULL, L"无法打开 jewena-chs.dat。请检查文件是否存在", L"错误", MB_ICONERROR); - ExitProcess(1); - return; - } + vfs.AddArchiveWithErrorMsg("jewena-chs.dat"); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); h = GetHandle(); @@ -168,6 +196,10 @@ extern "C" __declspec(dllexport) void Attach() { DetourAttach(&TrueGetFileSize, HookedGetFileSize); DetourAttach(&TrueGetFileSizeEx, HookedGetFileSizeEx); DetourAttach(&TrueSetFilePointer, HookedSetFilePointer); + DetourAttach(&TrueSetFilePointerEx, HookedSetFilePointerEx); + DetourAttach(&TrueGetFileType, HookedGetFileType); + DetourAttach(&TrueGetFileAttributesW, HookedGetFileAttributesW); + DetourAttach(&TrueGetFileAttributesExW, HookedGetFileAttributesExW); DetourTransactionCommit(); std::string stringReplaceFile = config.configs["stringReplaceFile"]; if (!stringReplaceFile.empty()) { @@ -194,6 +226,10 @@ extern "C" __declspec(dllexport) void Detach() { DetourDetach(&TrueGetFileSize, HookedGetFileSize); DetourDetach(&TrueGetFileSizeEx, HookedGetFileSizeEx); DetourDetach(&TrueSetFilePointer, HookedSetFilePointer); + DetourDetach(&TrueSetFilePointerEx, HookedSetFilePointerEx); + DetourDetach(&TrueGetFileType, HookedGetFileType); + DetourDetach(&TrueGetFileAttributesW, HookedGetFileAttributesW); + DetourDetach(&TrueGetFileAttributesExW, HookedGetFileAttributesExW); DetourTransactionCommit(); } diff --git a/vfs.cpp b/vfs.cpp index 6c550f1..cfc3367 100644 --- a/vfs.cpp +++ b/vfs.cpp @@ -3,6 +3,34 @@ #include "str_util.h" #include "fileop.h" #include "shlwapi.h" +#include "time_util.h" + +DWORD mapZipError(zip_file_t* file) { + auto error = zip_file_get_error(file); + if (error) { + switch (error->zip_err) { + case ZIP_ER_EOF: + return ERROR_HANDLE_EOF; + case ZIP_ER_INVAL: + return ERROR_INVALID_PARAMETER; + case ZIP_ER_SEEK: + return ERROR_SEEK; + case ZIP_ER_READ: + return ERROR_READ_FAULT; + case ZIP_ER_CRC: + return ERROR_CRC; + case ZIP_ER_ZIPCLOSED: + return ERROR_INVALID_HANDLE; + case ZIP_ER_NOENT: + return ERROR_FILE_NOT_FOUND; + case ZIP_ER_EXISTS: + return ERROR_FILE_EXISTS; + case ZIP_ER_OPEN: + return ERROR_OPEN_FAILED; + } + } + return ERROR_INVALID_HANDLE; +} VFS::VFS() { WCHAR exePath[MAX_PATH]; @@ -30,7 +58,7 @@ VFS::~VFS() { bool VFS::AddArchive(std::string path) { zip_t* archive = zip_open(path.c_str(), ZIP_RDONLY, nullptr); if (!archive) return false; - archives.push_back(archive); + archives.push_front(archive); auto len = zip_get_num_entries(archive, 0); for (zip_int64_t i = 0; i < len; i++) { struct zip_stat st; @@ -42,11 +70,66 @@ bool VFS::AddArchive(std::string path) { } std::string name = st.name; name = str_util::str_replace(name, "/", "\\"); - files[name] = st.size; + files[name] = st; } return true; } +bool VFS::AddArchiveFromResource(HMODULE hModule, int resourceID) { + HRSRC hResInfo = FindResource(hModule, MAKEINTRESOURCE(resourceID), RT_RCDATA); + if (!hResInfo) return false; + HGLOBAL hResData = LoadResource(hModule, hResInfo); + if (!hResData) return false; + LPVOID lpResData = LockResource(hResData); + if (!lpResData) return false; + DWORD dwSize = SizeofResource(hModule, hResInfo); + if (!dwSize) return false; + auto re = zip_source_buffer_create(lpResData, dwSize, 0, nullptr); + if (!re) { + return false; + } + zip_t* archive = zip_open_from_source(re, ZIP_RDONLY, nullptr); + if (!archive) return false; + archives.push_front(archive); + auto len = zip_get_num_entries(archive, 0); + for (zip_int64_t i = 0; i < len; i++) { + struct zip_stat st; + zip_stat_init(&st); + zip_stat_index(archive, i, 0, &st); + // Skip directories/folders (directory entries usually end with a '/') + if (st.name[strlen(st.name) - 1] == '/') { + continue; + } + std::string name = st.name; + name = str_util::str_replace(name, "/", "\\"); + files[name] = st; + } + return true; +} + +void VFS::AddArchiveWithErrorMsg(std::string path) { + if (!AddArchive(path)) { + std::wstring wpath; + if (!wchar_util::str_to_wstr(wpath, path, CP_UTF8)) { + MessageBoxW(NULL, L"无法打开资源文件。请检查资源文件是否完整", L"错误", MB_ICONERROR); + ExitProcess(1); + return; + } + std::wstring wmsg = L"无法打开 " + wpath + L"。请检查文件是否存在"; + MessageBoxW(NULL, wmsg.c_str(), L"错误", MB_ICONERROR); + ExitProcess(1); + return; + } +} + +void VFS::AddArchiveFromResourceWithErrorMsg(HMODULE hModule, int resourceID) { + if (!AddArchiveFromResource(hModule, resourceID)) { + MessageBoxW(NULL, L"无法打开内置的资源文件。", L"错误", MB_ICONERROR); + ExitProcess(1); + return; + } +} + bool VFS::ContainsFile(std::string path) { path = str_util::str_replace(path, "/", "\\"); if (fileop::isabs(path)) { @@ -112,7 +195,7 @@ bool VFS::ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LP } zip_int64_t n = zip_fread(file, lpBuffer, nNumberOfBytesToRead); if (n == -1) { - SetLastError(ERROR_INVALID_HANDLE); + SetLastError(mapZipError(file)); return false; } if (lpNumberOfBytesRead) { @@ -138,7 +221,7 @@ DWORD VFS::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { } auto data = *f; auto name = data.second; - auto size = files[name]; + auto size = files[name].size; if (lpFileSizeHigh) { *lpFileSizeHigh = size >> 32; } @@ -153,9 +236,8 @@ BOOL VFS::GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) { } auto data = *f; auto name = data.second; - auto size = files[name]; - lpFileSize->LowPart = size & 0xFFFFFFFF; - lpFileSize->HighPart = size >> 32; + auto size = files[name].size; + lpFileSize->QuadPart = size; return TRUE; } @@ -173,14 +255,83 @@ DWORD VFS::SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceTo if (lpDistanceToMoveHigh) { offset |= ((zip_int64_t)*lpDistanceToMoveHigh) << 32; } - zip_int64_t n = zip_fseek(file, offset, dwMoveMethod); - if (n == -1) { - SetLastError(ERROR_INVALID_HANDLE); + zip_int8_t code = zip_fseek(file, offset, dwMoveMethod); + if (code == -1) { + SetLastError(mapZipError(file)); return INVALID_SET_FILE_POINTER; } + zip_int64_t n = zip_ftell(file); + if (n == -1) { + SetLastError(mapZipError(file)); + return INVALID_SET_FILE_POINTER; + } + if (lpDistanceToMoveHigh) { + *lpDistanceToMoveHigh = n >> 32; + } return n; } +BOOL VFS::SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) { + if (!ContainsHandle(hFile)) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + zip_file_t* file = (zip_file_t*)hFile; + if (!file) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + zip_int64_t offset = liDistanceToMove.QuadPart; + zip_int8_t code = zip_fseek(file, offset, dwMoveMethod); + if (code == -1) { + SetLastError(mapZipError(file)); + return FALSE; + } + zip_int64_t n = zip_ftell(file); + if (n == -1) { + SetLastError(mapZipError(file)); + return FALSE; + } + if (lpNewFilePointer) { + lpNewFilePointer->QuadPart = n; + } + return TRUE; +} + std::string VFS::GetBasePath() { return base_path; } + +BOOL VFS::GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) { + std::string path; + if (!wchar_util::wstr_to_str(path, lpFileName, CP_UTF8)) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + path = str_util::str_replace(path, "/", "\\"); + if (fileop::isabs(path)) { + path = fileop::relpath(path, base_path); + } + auto c = files.find(path); + if (c == files.end()) { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + auto st = (*c).second; + if (fInfoLevelId == GetFileExInfoStandard) { + if (!lpFileInformation) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + WIN32_FILE_ATTRIBUTE_DATA data; + data.dwFileAttributes = FILE_ATTRIBUTE_READONLY; + time_util::time_t_to_file_time(st.mtime, &data.ftLastWriteTime); + data.ftCreationTime = data.ftLastWriteTime; + data.ftLastAccessTime = data.ftLastWriteTime; + data.nFileSizeHigh = st.size >> 32; + data.nFileSizeLow = st.size & 0xFFFFFFFF; + memcpy(lpFileInformation, &data, sizeof(data)); + return TRUE; + } + return FALSE; +} diff --git a/vfs.hpp b/vfs.hpp index a4491f2..13c4360 100644 --- a/vfs.hpp +++ b/vfs.hpp @@ -31,16 +31,21 @@ class VFS { VFS(); ~VFS(); bool AddArchive(std::string path); + bool AddArchiveFromResource(HMODULE hModule, int resourceID); + void AddArchiveWithErrorMsg(std::string path); + void AddArchiveFromResourceWithErrorMsg(HMODULE hModule, int resourceID); bool ContainsFile(std::string path); bool ContainsFile(std::wstring path); bool ContainsHandle(HANDLE hFile); HANDLE CreateFileW(std::wstring path); bool ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead); void CloseHandle(HANDLE hFile); + BOOL GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation); DWORD GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); - std::unordered_map files; + BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); + std::unordered_map files; std::string GetBasePath(); private: std::string base_path;