Skip loading merge archives if they are read-only.

This commit is contained in:
Skyth 2024-12-29 01:38:55 +03:00
parent a37f2885ae
commit 8b51f81cee

View file

@ -18,7 +18,7 @@ struct Mod
ModType type{}; ModType type{};
std::vector<std::filesystem::path> includeDirs; std::vector<std::filesystem::path> includeDirs;
bool merge = false; bool merge = false;
ankerl::unordered_dense::set<XXH64_hash_t> readOnly; ankerl::unordered_dense::set<std::filesystem::path> readOnly;
}; };
static std::vector<Mod> g_mods; static std::vector<Mod> g_mods;
@ -41,16 +41,16 @@ std::filesystem::path ModLoader::RedirectPath(std::string_view path)
std::string pathStr(path); std::string pathStr(path);
std::replace(pathStr.begin(), pathStr.end(), '\\', '/'); std::replace(pathStr.begin(), pathStr.end(), '\\', '/');
hash = XXH3_64bits(pathStr.data(), pathStr.size()); std::filesystem::path fsPath(std::move(pathStr));
for (auto& mod : g_mods) for (auto& mod : g_mods)
{ {
if (mod.type == ModType::UMM && mod.merge && !mod.readOnly.contains(hash)) if (mod.type == ModType::UMM && mod.merge && !mod.readOnly.contains(fsPath))
continue; continue;
for (auto& includeDir : mod.includeDirs) for (auto& includeDir : mod.includeDirs)
{ {
std::filesystem::path modPath = includeDir / pathStr; std::filesystem::path modPath = includeDir / fsPath;
if (std::filesystem::exists(modPath)) if (std::filesystem::exists(modPath))
return s_pathCache.emplace(hash, modPath).first->second; return s_pathCache.emplace(hash, modPath).first->second;
} }
@ -107,19 +107,19 @@ void ModLoader::Init()
std::string readOnly = modIni.getString("Details", "Read-only", modIni.getString("Filesystem", "Read-only", std::string())); std::string readOnly = modIni.getString("Details", "Read-only", modIni.getString("Filesystem", "Read-only", std::string()));
std::replace(readOnly.begin(), readOnly.end(), '\\', '/'); std::replace(readOnly.begin(), readOnly.end(), '\\', '/');
std::string_view iterator = readOnly; std::string_view readOnlySplit = readOnly;
while (!iterator.empty()) while (!readOnlySplit.empty())
{ {
size_t index = iterator.find(','); size_t index = readOnlySplit.find(',');
if (index == std::string_view::npos) if (index == std::string_view::npos)
{ {
mod.readOnly.emplace(XXH3_64bits(iterator.data(), iterator.size())); mod.readOnly.emplace(readOnlySplit);
break; break;
} }
mod.readOnly.emplace(XXH3_64bits(iterator.data(), index)); mod.readOnly.emplace(readOnlySplit.substr(0, index));
iterator.remove_prefix(index + 1); readOnlySplit.remove_prefix(index + 1);
} }
} }
else // HMM else // HMM
@ -200,9 +200,21 @@ PPC_FUNC(sub_82E0D3E8)
} }
}; };
std::u8string_view arlFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r4.u32)));
std::filesystem::path arlFilePath;
std::filesystem::path arFilePath;
std::filesystem::path appendArlFilePath;
for (auto& mod : g_mods)
{
for (auto& includeDir : mod.includeDirs)
{
auto loadFile = [&]<typename TFunction>(const std::filesystem::path& filePath, const TFunction& function) auto loadFile = [&]<typename TFunction>(const std::filesystem::path& filePath, const TFunction& function)
{ {
std::ifstream stream(filePath, std::ios::binary); if (mod.type == ModType::UMM && mod.readOnly.contains(filePath))
return false;
std::ifstream stream(includeDir / filePath, std::ios::binary);
if (stream.good()) if (stream.good())
{ {
stream.seekg(0, std::ios::end); stream.seekg(0, std::ios::end);
@ -220,15 +232,6 @@ PPC_FUNC(sub_82E0D3E8)
return false; return false;
}; };
std::u8string_view arlFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r4.u32)));
std::filesystem::path arlFilePath;
std::filesystem::path arFilePath;
std::filesystem::path appendArlFilePath;
for (auto& mod : g_mods)
{
for (auto& includeDir : mod.includeDirs)
{
if (mod.type == ModType::UMM) if (mod.type == ModType::UMM)
{ {
if (mod.merge) if (mod.merge)
@ -239,7 +242,7 @@ PPC_FUNC(sub_82E0D3E8)
arlFilePath += ".arl"; arlFilePath += ".arl";
} }
if (!loadFile(includeDir / arlFilePath, parseArlFileData)) if (!loadFile(arlFilePath, parseArlFileData))
{ {
if (arFilePath.empty()) if (arFilePath.empty())
{ {
@ -247,12 +250,11 @@ PPC_FUNC(sub_82E0D3E8)
arFilePath += ".ar"; arFilePath += ".ar";
} }
if (!loadFile(includeDir / arFilePath, parseArFileData)) if (!loadFile(arFilePath, parseArFileData))
{ {
for (uint32_t i = 0; ; i++) for (uint32_t i = 0; ; i++)
{ {
s_tempPath = includeDir; s_tempPath = arFilePath;
s_tempPath /= arFilePath;
s_tempPath += fmt::format(".{:02}", i); s_tempPath += fmt::format(".{:02}", i);
if (!loadFile(s_tempPath, parseArFileData)) if (!loadFile(s_tempPath, parseArFileData))
@ -274,7 +276,7 @@ PPC_FUNC(sub_82E0D3E8)
appendArlFilePath += arlFilePath.filename(); appendArlFilePath += arlFilePath.filename();
} }
loadFile(includeDir / appendArlFilePath, parseArlFileData); loadFile(appendArlFilePath, parseArlFileData);
} }
} }
} }
@ -366,9 +368,19 @@ PPC_FUNC(sub_82E0B500)
thread_local std::filesystem::path s_tempFilePath; thread_local std::filesystem::path s_tempFilePath;
s_tempFilePath.clear(); s_tempFilePath.clear();
std::filesystem::path arFilePath;
std::filesystem::path appendArFilePath;
for (auto& mod : g_mods)
{
for (auto& includeDir : mod.includeDirs)
{
auto loadArchive = [&](const std::filesystem::path& arFilePath) auto loadArchive = [&](const std::filesystem::path& arFilePath)
{ {
std::ifstream stream(arFilePath, std::ios::binary); if (mod.type == ModType::UMM && mod.readOnly.contains(arFilePath))
return false;
std::ifstream stream(includeDir / arFilePath, std::ios::binary);
if (stream.good()) if (stream.good())
{ {
stream.seekg(0, std::ios::end); stream.seekg(0, std::ios::end);
@ -401,12 +413,15 @@ PPC_FUNC(sub_82E0B500)
return false; return false;
}; };
auto loadArchives = [&](const std::filesystem::path& arFilePath, bool allowNoArl) auto loadArchives = [&](const std::filesystem::path& arFilePath)
{ {
s_tempFilePath = arFilePath; s_tempFilePath = arFilePath;
s_tempFilePath += "l"; s_tempFilePath += "l";
std::ifstream stream(s_tempFilePath, std::ios::binary); if (mod.type == ModType::UMM && mod.readOnly.contains(s_tempFilePath))
return;
std::ifstream stream(includeDir / s_tempFilePath, std::ios::binary);
if (stream.good()) if (stream.good())
{ {
// TODO: Should cache this instead of re-opening the file. // TODO: Should cache this instead of re-opening the file.
@ -429,7 +444,7 @@ PPC_FUNC(sub_82E0B500)
} }
} }
} }
else if (allowNoArl) else if (mod.type == ModType::UMM)
{ {
if (!loadArchive(arFilePath)) if (!loadArchive(arFilePath))
{ {
@ -444,13 +459,6 @@ PPC_FUNC(sub_82E0B500)
} }
}; };
std::filesystem::path arFilePath;
std::filesystem::path appendArFilePath;
for (auto& mod : g_mods)
{
for (auto& includeDir : mod.includeDirs)
{
if (mod.type == ModType::UMM) if (mod.type == ModType::UMM)
{ {
if (mod.merge) if (mod.merge)
@ -458,7 +466,7 @@ PPC_FUNC(sub_82E0B500)
if (arFilePath.empty()) if (arFilePath.empty())
arFilePath = arFilePathU8; arFilePath = arFilePathU8;
loadArchives(includeDir / arFilePath, true); loadArchives(arFilePath);
} }
} }
else if (mod.type == ModType::HMM) else if (mod.type == ModType::HMM)
@ -473,7 +481,7 @@ PPC_FUNC(sub_82E0B500)
appendArFilePath += arFilePath.filename(); appendArFilePath += arFilePath.filename();
} }
loadArchives(includeDir / appendArFilePath, false); loadArchives(appendArFilePath);
} }
} }
} }