添加虚拟文件系统(VFS)类以支持压缩文件的读取和管理,更新CMake配置以包含libzip子模块
This commit is contained in:
161
vfs.cpp
Normal file
161
vfs.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#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::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, "\\", "/");
|
||||
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;
|
||||
}
|
||||
|
||||
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 n = zip_fseek(file, lDistanceToMove, dwMoveMethod);
|
||||
if (n == -1) {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return INVALID_SET_FILE_POINTER;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
std::string VFS::GetBasePath() {
|
||||
return base_path;
|
||||
}
|
||||
Reference in New Issue
Block a user