48 if (s_vpak_key ==
"FALLBACK_VPAK_KEY_0000") {
49 log(LogLevel::ERROR,
"VirtualFileSystem: Using fallback VPAK key - assets may not decrypt correctly!");
52 m_loaded_vpk = std::make_unique<LoadedVPK>();
53 m_loaded_vpk->file_path = vpk_path;
55 m_loaded_vpk->file_stream.open(vpk_path, std::ios::binary);
56 if (!m_loaded_vpk->file_stream) {
57 log(LogLevel::ERROR,
"Failed to open VPK file: %s", vpk_path.c_str());
63 m_loaded_vpk->file_stream.read(
64 reinterpret_cast<char*
>(&m_loaded_vpk->header),
68 if (std::strncmp(m_loaded_vpk->header.magic,
"VPAK", 4) != 0) {
74 if (m_loaded_vpk->header.version != 3) {
75 log(LogLevel::WARNING,
"VPK version mismatch: expected 3, got %u", m_loaded_vpk->header.version);
78 m_loaded_vpk->entries.resize(m_loaded_vpk->header.file_count);
79 m_loaded_vpk->file_stream.seekg(
sizeof(
VPKHeader));
80 m_loaded_vpk->file_stream.read(
81 reinterpret_cast<char*
>(m_loaded_vpk->entries.data()),
85 m_loaded_vpk->file_stream.seekg(m_loaded_vpk->header.names_offset);
86 for (uint32_t i = 0; i < m_loaded_vpk->header.file_count; ++i) {
88 std::getline(m_loaded_vpk->file_stream, name,
'\0');
89 m_loaded_vpk->file_names.push_back(name);
94 std::vector<uint8_t> compressed_buffer(m_loaded_vpk->header.solid_compressed_size);
97 std::lock_guard<std::mutex> lock(stream_mutex);
98 m_loaded_vpk->file_stream.seekg(m_loaded_vpk->header.data_offset);
99 m_loaded_vpk->file_stream.read(
100 reinterpret_cast<char*
>(compressed_buffer.data()),
101 m_loaded_vpk->header.solid_compressed_size
109 size_t key_len = key.length();
111 for (
size_t j = 0; j < compressed_buffer.size(); ++j) {
112 compressed_buffer[j] ^= key[key_idx];
114 if (key_idx == key_len) {
119 m_loaded_vpk->solid_data.resize(m_loaded_vpk->header.solid_uncompressed_size);
120 std::memset(m_loaded_vpk->solid_data.data(), 0, m_loaded_vpk->header.solid_uncompressed_size);
122 size_t result = ZSTD_decompress(
123 m_loaded_vpk->solid_data.data(), m_loaded_vpk->header.solid_uncompressed_size,
124 compressed_buffer.data(), m_loaded_vpk->header.solid_compressed_size
127 if (ZSTD_isError(result)) {
128 log(LogLevel::ERROR,
"Zstd decompression failed: %s", ZSTD_getErrorName(result));
129 m_loaded_vpk.reset();
133 log(
"Loaded VPK with %u files", m_loaded_vpk->header.file_count);
139 if (!m_loaded_vpk)
return nullptr;
141 std::string cleanVirtualPath =
clean_path(virtual_path);
143 m_loaded_vpk->file_names.begin(),
144 m_loaded_vpk->file_names.end(),
148 if (it == m_loaded_vpk->file_names.end()) {
152 size_t index = std::distance(m_loaded_vpk->file_names.begin(), it);
153 return &m_loaded_vpk->entries[index];
157 std::string cleanVirtualPath =
clean_path(virtual_path);
159 if (m_use_packed_assets && m_loaded_vpk) {
165 auto fileData = std::make_unique<FileData>();
166 fileData->size = entry->uncompressed_size;
167 fileData->data.resize(entry->uncompressed_size);
169 std::memcpy(fileData->data.data(),
170 m_loaded_vpk->solid_data.data() + entry->data_offset,
171 entry->uncompressed_size);
175 std::string fullPath;
178 fullPath = cleanVirtualPath;
180 fullPath =
"/" + cleanVirtualPath;
183 if (!fs::exists(fullPath)) {
187 std::ifstream file(fullPath, std::ios::binary);
192 file.seekg(0, std::ios::end);
193 size_t size = file.tellg();
194 file.seekg(0, std::ios::beg);
196 auto fileData = std::make_unique<FileData>();
197 fileData->data.resize(size);
198 fileData->size = size;
200 file.read(
reinterpret_cast<char*
>(fileData->data.data()), size);
206 std::string cleanVirtualPath =
clean_path(virtual_path);
208 if (m_use_packed_assets && m_loaded_vpk) {
214 return std::make_unique<VPKStream>(
215 reinterpret_cast<const char*
>(m_loaded_vpk->solid_data.data() + entry->data_offset),
216 entry->uncompressed_size
219 std::string fullPath;
222 fullPath = cleanVirtualPath;
224 fullPath =
"/" + cleanVirtualPath;
226 if (!fs::exists(fullPath)) {
229 return std::make_unique<std::ifstream>(fullPath, std::ios::binary);
275 std::vector<std::string> result;
276 std::string cleanDir =
clean_path(virtual_dir);
278 if (m_use_packed_assets && m_loaded_vpk) {
279 for (
const auto& name : m_loaded_vpk->file_names) {
280 if (cleanDir.empty() || name.find(cleanDir) == 0) {
281 result.push_back(name);
285 std::string fullDir = m_base_path +
"/Assets/" + cleanDir;
286 if (fs::exists(fullDir) && fs::is_directory(fullDir)) {
287 for (
const auto& entry : fs::recursive_directory_iterator(fullDir)) {
288 if (entry.is_regular_file()) {
289 std::string relativePath = fs::relative(entry.path(), m_base_path +
"/Assets").generic_string();
290 result.push_back(relativePath);
300 std::string result = path;
302 std::replace(result.begin(), result.end(),
'\\',
'/');
304 if (result.find(
"./") == 0) {
305 result = result.substr(2);
308 if (!result.empty() && result[0] ==
'/') {
309 result = result.substr(1);
312 if (result.find(
"Assets/") == 0 && m_use_packed_assets) {
313 result = result.substr(7);
320 std::filesystem::path base(base_path);
321 std::filesystem::path relative(relative_path);
323 if (relative.is_absolute()) {
327 base.remove_filename();
328 std::filesystem::path resolved = base / relative;
329 return clean_path(resolved.lexically_normal().string());
struct defining virtual file "header", used to identify files in the virtual file system.