2022-11-27 11:59:59 +00:00
|
|
|
#include <filesystem>
|
|
|
|
|
2022-11-01 18:21:26 +00:00
|
|
|
#include "AssetManager.h"
|
2022-11-03 03:30:35 +00:00
|
|
|
#include "Game.h"
|
2023-10-21 23:31:55 +00:00
|
|
|
#include "Logger.h"
|
2022-11-01 18:21:26 +00:00
|
|
|
|
2022-11-27 11:59:59 +00:00
|
|
|
AssetManager::AssetManager(const std::filesystem::path& path) {
|
2022-11-01 18:21:26 +00:00
|
|
|
if (!std::filesystem::is_directory(path)) {
|
2022-11-27 11:59:59 +00:00
|
|
|
throw std::runtime_error("Attempted to load asset bundle (" + path.string() + ") however it is not a valid directory.");
|
2022-11-01 18:21:26 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 11:59:59 +00:00
|
|
|
m_Path = path;
|
2022-11-01 18:21:26 +00:00
|
|
|
|
|
|
|
if (std::filesystem::exists(m_Path / "client") && std::filesystem::exists(m_Path / "versions")) {
|
|
|
|
m_AssetBundleType = eAssetBundleType::Packed;
|
|
|
|
|
|
|
|
m_RootPath = m_Path;
|
|
|
|
m_ResPath = (m_Path / "client" / "res");
|
|
|
|
} else if (std::filesystem::exists(m_Path / ".." / "versions") && std::filesystem::exists(m_Path / "res")) {
|
|
|
|
m_AssetBundleType = eAssetBundleType::Packed;
|
|
|
|
|
|
|
|
m_RootPath = (m_Path / "..");
|
|
|
|
m_ResPath = (m_Path / "res");
|
|
|
|
} else if (std::filesystem::exists(m_Path / "pack") && std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
|
|
|
m_AssetBundleType = eAssetBundleType::Packed;
|
|
|
|
|
|
|
|
m_RootPath = (m_Path / ".." / "..");
|
|
|
|
m_ResPath = m_Path;
|
2022-11-21 22:19:37 +00:00
|
|
|
} else if ((std::filesystem::exists(m_Path / "res" / "cdclient.fdb") || std::filesystem::exists(m_Path / "res" / "CDServer.sqlite")) && !std::filesystem::exists(m_Path / "res" / "pack")) {
|
2022-11-01 18:21:26 +00:00
|
|
|
m_AssetBundleType = eAssetBundleType::Unpacked;
|
|
|
|
|
|
|
|
m_ResPath = (m_Path / "res");
|
2022-11-21 22:19:37 +00:00
|
|
|
} else if ((std::filesystem::exists(m_Path / "cdclient.fdb") || std::filesystem::exists(m_Path / "CDServer.sqlite")) && !std::filesystem::exists(m_Path / "pack")) {
|
2022-11-01 18:21:26 +00:00
|
|
|
m_AssetBundleType = eAssetBundleType::Unpacked;
|
|
|
|
|
|
|
|
m_ResPath = m_Path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_AssetBundleType == eAssetBundleType::None) {
|
|
|
|
throw std::runtime_error("Failed to identify client type, cannot read client data.");
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_AssetBundleType) {
|
|
|
|
case eAssetBundleType::Packed: {
|
|
|
|
this->LoadPackIndex();
|
|
|
|
break;
|
|
|
|
}
|
2023-04-12 16:48:20 +00:00
|
|
|
case eAssetBundleType::None:
|
|
|
|
case eAssetBundleType::Unpacked: {
|
|
|
|
break;
|
|
|
|
}
|
2022-11-01 18:21:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetManager::LoadPackIndex() {
|
|
|
|
m_PackIndex = new PackIndex(m_RootPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::filesystem::path AssetManager::GetResPath() {
|
|
|
|
return m_ResPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
eAssetBundleType AssetManager::GetAssetBundleType() {
|
|
|
|
return m_AssetBundleType;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AssetManager::HasFile(const char* name) {
|
|
|
|
auto fixedName = std::string(name);
|
|
|
|
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
|
|
|
|
2022-11-06 00:09:26 +00:00
|
|
|
// Special case for unpacked client have BrickModels in upper case
|
|
|
|
if (this->m_AssetBundleType == eAssetBundleType::Unpacked) GeneralUtils::ReplaceInString(fixedName, "brickmodels", "BrickModels");
|
2022-11-01 18:21:26 +00:00
|
|
|
|
2022-11-05 00:45:04 +00:00
|
|
|
std::replace(fixedName.begin(), fixedName.end(), '\\', '/');
|
|
|
|
if (std::filesystem::exists(m_ResPath / fixedName)) return true;
|
2022-11-01 18:21:26 +00:00
|
|
|
|
2022-11-05 00:45:04 +00:00
|
|
|
if (this->m_AssetBundleType == eAssetBundleType::Unpacked) return false;
|
|
|
|
|
|
|
|
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
|
|
|
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
2022-11-01 18:21:26 +00:00
|
|
|
|
2023-12-28 04:18:20 +00:00
|
|
|
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
2024-12-05 20:03:17 +00:00
|
|
|
crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
|
2022-11-01 18:21:26 +00:00
|
|
|
|
|
|
|
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
|
|
|
if (item.m_Crc == crc) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
|
|
|
|
auto fixedName = std::string(name);
|
|
|
|
std::transform(fixedName.begin(), fixedName.end(), fixedName.begin(), [](uint8_t c) { return std::tolower(c); });
|
2022-11-03 03:30:35 +00:00
|
|
|
std::replace(fixedName.begin(), fixedName.end(), '\\', '/'); // On the off chance someone has the wrong slashes, force forward slashes
|
2022-11-01 18:21:26 +00:00
|
|
|
|
2022-11-06 00:09:26 +00:00
|
|
|
// Special case for unpacked client have BrickModels in upper case
|
|
|
|
if (this->m_AssetBundleType == eAssetBundleType::Unpacked) GeneralUtils::ReplaceInString(fixedName, "brickmodels", "BrickModels");
|
|
|
|
|
|
|
|
if (std::filesystem::exists(m_ResPath / fixedName)) {
|
2022-11-01 18:21:26 +00:00
|
|
|
FILE* file;
|
|
|
|
#ifdef _WIN32
|
2022-11-06 00:09:26 +00:00
|
|
|
fopen_s(&file, (m_ResPath / fixedName).string().c_str(), "rb");
|
2022-11-01 18:21:26 +00:00
|
|
|
#elif __APPLE__
|
|
|
|
// macOS has 64bit file IO by default
|
2022-11-06 00:09:26 +00:00
|
|
|
file = fopen((m_ResPath / fixedName).string().c_str(), "rb");
|
2022-11-01 18:21:26 +00:00
|
|
|
#else
|
2022-11-06 00:09:26 +00:00
|
|
|
file = fopen64((m_ResPath / fixedName).string().c_str(), "rb");
|
2022-11-01 18:21:26 +00:00
|
|
|
#endif
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
*len = ftell(file);
|
2023-12-28 04:18:20 +00:00
|
|
|
*data = static_cast<char*>(malloc(*len));
|
2022-11-01 18:21:26 +00:00
|
|
|
fseek(file, 0, SEEK_SET);
|
2023-04-12 16:48:20 +00:00
|
|
|
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
|
2022-11-01 18:21:26 +00:00
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->m_AssetBundleType == eAssetBundleType::Unpacked) return false;
|
|
|
|
|
2022-11-03 03:30:35 +00:00
|
|
|
// The crc in side of the pack always uses backslashes, so we need to convert them again...
|
|
|
|
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
|
|
|
if (fixedName.rfind("client\\res\\", 0) != 0) {
|
|
|
|
fixedName = "client\\res\\" + fixedName;
|
|
|
|
}
|
2022-11-01 18:21:26 +00:00
|
|
|
int32_t packIndex = -1;
|
2023-12-28 04:18:20 +00:00
|
|
|
uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
|
2024-12-05 20:03:17 +00:00
|
|
|
crc = crc32b(crc, reinterpret_cast<uint8_t*>(const_cast<char*>("\0\0\0\0")), 4);
|
2022-11-01 18:21:26 +00:00
|
|
|
|
|
|
|
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
|
|
|
|
if (item.m_Crc == crc) {
|
|
|
|
packIndex = item.m_PackFileIndex;
|
|
|
|
crc = item.m_Crc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packIndex == -1 || !crc) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto packs = this->m_PackIndex->GetPacks();
|
|
|
|
auto* pack = packs.at(packIndex);
|
|
|
|
|
|
|
|
bool success = pack->ReadFileFromPack(crc, data, len);
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2023-12-23 17:24:16 +00:00
|
|
|
AssetStream AssetManager::GetFile(const char* name) {
|
|
|
|
char* buf; uint32_t len;
|
2022-11-01 18:21:26 +00:00
|
|
|
|
|
|
|
bool success = this->GetFile(name, &buf, &len);
|
|
|
|
|
2023-12-23 17:24:16 +00:00
|
|
|
return AssetStream(buf, len, success);
|
2022-11-01 18:21:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
|
|
|
|
size_t i, j;
|
|
|
|
uint32_t crc, msb;
|
|
|
|
|
|
|
|
crc = base;
|
|
|
|
for (i = 0; i < l; i++) {
|
|
|
|
// xor next byte to upper bits of crc
|
2023-12-28 04:18:20 +00:00
|
|
|
crc ^= (static_cast<unsigned int>(message[i]) << 24);
|
2022-11-01 18:21:26 +00:00
|
|
|
for (j = 0; j < 8; j++) { // Do eight times.
|
|
|
|
msb = crc >> 31;
|
|
|
|
crc <<= 1;
|
|
|
|
crc ^= (0 - msb) & 0x04C11DB7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return crc; // don't complement crc on output
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetManager::~AssetManager() {
|
|
|
|
delete m_PackIndex;
|
|
|
|
}
|