242 lines
7.1 KiB
C++
242 lines
7.1 KiB
C++
#include "vfs.hpp"
|
|
#include "wchar_util.h"
|
|
#include "str_util.h"
|
|
#include "fileop.h"
|
|
#include "shlwapi.h"
|
|
|
|
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_back(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.size;
|
|
}
|
|
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_back(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.size;
|
|
}
|
|
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(ERROR_INVALID_HANDLE);
|
|
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];
|
|
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];
|
|
lpFileSize->LowPart = size & 0xFFFFFFFF;
|
|
lpFileSize->HighPart = size >> 32;
|
|
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_int64_t n = zip_fseek(file, offset, dwMoveMethod);
|
|
if (n == -1) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return INVALID_SET_FILE_POINTER;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
std::string VFS::GetBasePath() {
|
|
return base_path;
|
|
}
|