Files
jeweha_patch/vfs.cpp
2025-03-13 09:53:28 +08:00

338 lines
10 KiB
C++

#include "vfs.hpp"
#include "wchar_util.h"
#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];
GetModuleFileNameW(NULL, exePath, MAX_PATH);
std::wstring path = exePath;
std::string pathStr;
if (!wchar_util::wstr_to_str(pathStr, path, CP_UTF8)) {
char buf[MAX_PATH];
GetModuleFileNameA(NULL, buf, MAX_PATH);
pathStr = buf;
}
base_path = fileop::dirname(pathStr);
base_path = str_util::str_replace(base_path, "/", "\\");
}
VFS::~VFS() {
for (auto file : handles) {
zip_fclose((zip_file_t*)file.first);
}
for (auto archive : archives) {
zip_close(archive);
}
}
bool VFS::AddArchive(std::string path) {
zip_t* archive = zip_open(path.c_str(), 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;
}
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)) {
path = fileop::relpath(path, base_path);
}
return files.find(path) != files.end();
}
bool VFS::ContainsFile(std::wstring path) {
std::string str;
if (!wchar_util::wstr_to_str(str, path, CP_UTF8)) {
return false;
}
return ContainsFile(str);
}
bool VFS::ContainsHandle(HANDLE hFile) {
return handles.find(hFile) != handles.end();
}
HANDLE VFS::CreateFileW(std::wstring path) {
std::string str;
if (!wchar_util::wstr_to_str(str, path, CP_UTF8)) {
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
str = fileop::relpath(str, base_path);
str = str_util::str_replace(str, "/", "\\");
auto c = files.find(str);
if (c == files.end()) {
SetLastError(ERROR_FILE_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
str = (*c).first;
str = str_util::str_replace(str, "\\", "/");
zip_t* archive = nullptr;
zip_uint64_t index = 0;
for (auto a : archives) {
if (zip_name_locate(a, str.c_str(), 0) != -1) {
archive = a;
break;
}
}
if (!archive) {
SetLastError(ERROR_FILE_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
index = zip_name_locate(archive, str.c_str(), 0);
zip_file_t* file = zip_fopen_index(archive, index, 0);
handles[(HANDLE)file] = str;
return (HANDLE)file;
}
bool VFS::ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead) {
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 n = zip_fread(file, lpBuffer, nNumberOfBytesToRead);
if (n == -1) {
SetLastError(mapZipError(file));
return false;
}
if (lpNumberOfBytesRead) {
*lpNumberOfBytesRead = n;
}
return true;
}
void VFS::CloseHandle(HANDLE hFile) {
if (!ContainsHandle(hFile)) {
SetLastError(ERROR_INVALID_HANDLE);
return;
}
zip_fclose((zip_file_t*)hFile);
handles.erase(hFile);
}
DWORD VFS::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) {
auto f = handles.find(hFile);
if (f == handles.end()) {
SetLastError(ERROR_INVALID_HANDLE);
return INVALID_FILE_SIZE;
}
auto data = *f;
auto name = data.second;
auto size = files[name].size;
if (lpFileSizeHigh) {
*lpFileSizeHigh = size >> 32;
}
return size;
}
BOOL VFS::GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) {
auto f = handles.find(hFile);
if (f == handles.end()) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
auto data = *f;
auto name = data.second;
auto size = files[name].size;
lpFileSize->QuadPart = size;
return TRUE;
}
DWORD VFS::SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) {
if (!ContainsHandle(hFile)) {
SetLastError(ERROR_INVALID_HANDLE);
return INVALID_SET_FILE_POINTER;
}
zip_file_t* file = (zip_file_t*)hFile;
if (!file) {
SetLastError(ERROR_INVALID_HANDLE);
return INVALID_SET_FILE_POINTER;
}
zip_int64_t offset = lDistanceToMove;
if (lpDistanceToMoveHigh) {
offset |= ((zip_int64_t)*lpDistanceToMoveHigh) << 32;
}
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;
}