mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2026-04-27 12:51:42 +00:00
UMM merge archive support.
This commit is contained in:
parent
28f1a57c56
commit
f28c51d3d1
2 changed files with 112 additions and 60 deletions
|
|
@ -35,15 +35,23 @@ struct FindHandle : KernelObject
|
||||||
if (index != std::string_view::npos)
|
if (index != std::string_view::npos)
|
||||||
pathNoPrefix.remove_prefix(index + 2);
|
pathNoPrefix.remove_prefix(index + 2);
|
||||||
|
|
||||||
for (size_t i = 0; ; i++)
|
// Force add a work folder to let the game see the files in mods,
|
||||||
{
|
// if by some rare chance the user has no DLC or update files.
|
||||||
auto* includeDirs = ModLoader::GetIncludeDirectories(i);
|
if (pathNoPrefix.empty())
|
||||||
if (includeDirs == nullptr)
|
searchResult.emplace(u8"work", std::make_pair(0, true));
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: Should cache what we find here to save on future lookups.
|
// Look for only work folder in mod folders, AR files cause issues.
|
||||||
for (auto& includeDir : *includeDirs)
|
if (pathNoPrefix.starts_with("work"))
|
||||||
addDirectory(includeDir / pathNoPrefix);
|
{
|
||||||
|
for (size_t i = 0; ; i++)
|
||||||
|
{
|
||||||
|
auto* includeDirs = ModLoader::GetIncludeDirectories(i);
|
||||||
|
if (includeDirs == nullptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (auto& includeDir : *includeDirs)
|
||||||
|
addDirectory(includeDir / pathNoPrefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addDirectory(std::u8string_view((const char8_t*)FileSystem::TransformPath(path)));
|
addDirectory(std::u8string_view((const char8_t*)FileSystem::TransformPath(path)));
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ struct Mod
|
||||||
{
|
{
|
||||||
ModType type{};
|
ModType type{};
|
||||||
std::vector<std::filesystem::path> includeDirs;
|
std::vector<std::filesystem::path> includeDirs;
|
||||||
|
bool merge = false;
|
||||||
|
ankerl::unordered_dense::set<std::filesystem::path> readOnly;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<Mod> g_mods;
|
static std::vector<Mod> g_mods;
|
||||||
|
|
@ -26,30 +28,32 @@ std::filesystem::path ModLoader::RedirectPath(std::string_view path)
|
||||||
if (g_mods.empty())
|
if (g_mods.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
thread_local xxHashMap<std::filesystem::path> pathCache;
|
thread_local xxHashMap<std::filesystem::path> s_pathCache;
|
||||||
|
|
||||||
size_t sepIndex = path.find(":\\");
|
size_t sepIndex = path.find(":\\");
|
||||||
if (sepIndex != std::string_view::npos)
|
if (sepIndex != std::string_view::npos)
|
||||||
path.remove_prefix(sepIndex + 2);
|
path.remove_prefix(sepIndex + 2);
|
||||||
|
|
||||||
XXH64_hash_t hash = XXH3_64bits(path.data(), path.size());
|
XXH64_hash_t hash = XXH3_64bits(path.data(), path.size());
|
||||||
auto findResult = pathCache.find(hash);
|
auto findResult = s_pathCache.find(hash);
|
||||||
if (findResult != pathCache.end())
|
if (findResult != s_pathCache.end())
|
||||||
return findResult->second;
|
return findResult->second;
|
||||||
|
|
||||||
for (auto& mod : g_mods)
|
for (auto& mod : g_mods)
|
||||||
{
|
{
|
||||||
// TODO: Need to ignore UMM merge archives here.
|
// TODO: Check for read only.
|
||||||
|
if (mod.type == ModType::UMM && mod.merge)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (auto& includeDir : mod.includeDirs)
|
for (auto& includeDir : mod.includeDirs)
|
||||||
{
|
{
|
||||||
std::filesystem::path modPath = includeDir / path;
|
std::filesystem::path modPath = includeDir / path;
|
||||||
if (std::filesystem::exists(modPath))
|
if (std::filesystem::exists(modPath))
|
||||||
return pathCache.emplace(hash, modPath).first->second;
|
return s_pathCache.emplace(hash, modPath).first->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathCache.emplace(hash, std::filesystem::path{}).first->second;
|
return s_pathCache.emplace(hash, std::filesystem::path{}).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::filesystem::path>* ModLoader::GetIncludeDirectories(size_t modIndex)
|
std::vector<std::filesystem::path>* ModLoader::GetIncludeDirectories(size_t modIndex)
|
||||||
|
|
@ -96,6 +100,7 @@ void ModLoader::Init()
|
||||||
{
|
{
|
||||||
mod.type = ModType::UMM;
|
mod.type = ModType::UMM;
|
||||||
mod.includeDirs.emplace_back(std::move(modDirectoryPath));
|
mod.includeDirs.emplace_back(std::move(modDirectoryPath));
|
||||||
|
mod.merge = modIni.getBool("Details", "Merge", modIni.getBool("Filesystem", "Merge", false));
|
||||||
}
|
}
|
||||||
else // HMM
|
else // HMM
|
||||||
{
|
{
|
||||||
|
|
@ -125,13 +130,10 @@ PPC_FUNC(sub_82E0D3E8)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* arlFilePathU8 = reinterpret_cast<const char*>(base + PPC_LOAD_U32(ctx.r4.u32));
|
thread_local ankerl::unordered_dense::set<std::string> s_arlFileNames;
|
||||||
std::filesystem::path appendArlFilePath;
|
thread_local std::vector<uint8_t> s_arlFileData;
|
||||||
|
s_arlFileNames.clear();
|
||||||
thread_local ankerl::unordered_dense::set<std::string> fileNames;
|
s_arlFileData.clear();
|
||||||
thread_local std::vector<uint8_t> arlFileData;
|
|
||||||
fileNames.clear();
|
|
||||||
arlFileData.clear();
|
|
||||||
|
|
||||||
auto parseArlFileData = [&](const uint8_t* arlFileData, size_t arlFileSize)
|
auto parseArlFileData = [&](const uint8_t* arlFileData, size_t arlFileSize)
|
||||||
{
|
{
|
||||||
|
|
@ -150,7 +152,7 @@ PPC_FUNC(sub_82E0D3E8)
|
||||||
uint8_t fileNameSize = *arlFileNames;
|
uint8_t fileNameSize = *arlFileNames;
|
||||||
++arlFileNames;
|
++arlFileNames;
|
||||||
|
|
||||||
fileNames.emplace(reinterpret_cast<const char*>(arlFileNames), fileNameSize);
|
s_arlFileNames.emplace(reinterpret_cast<const char*>(arlFileNames), fileNameSize);
|
||||||
|
|
||||||
arlFileNames += fileNameSize;
|
arlFileNames += fileNameSize;
|
||||||
}
|
}
|
||||||
|
|
@ -166,33 +168,54 @@ PPC_FUNC(sub_82E0D3E8)
|
||||||
stream.seekg(0, std::ios::end);
|
stream.seekg(0, std::ios::end);
|
||||||
size_t arlFileSize = stream.tellg();
|
size_t arlFileSize = stream.tellg();
|
||||||
stream.seekg(0, std::ios::beg);
|
stream.seekg(0, std::ios::beg);
|
||||||
arlFileData.resize(arlFileSize);
|
s_arlFileData.resize(arlFileSize);
|
||||||
stream.read(reinterpret_cast<char*>(arlFileData.data()), arlFileSize);
|
stream.read(reinterpret_cast<char*>(s_arlFileData.data()), arlFileSize);
|
||||||
stream.close();
|
stream.close();
|
||||||
|
|
||||||
parseArlFileData(arlFileData.data(), arlFileSize);
|
parseArlFileData(s_arlFileData.data(), arlFileSize);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::u8string_view arlFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r4.u32)));
|
||||||
|
std::filesystem::path arlFilePath;
|
||||||
|
std::filesystem::path appendArlFilePath;
|
||||||
|
|
||||||
for (auto& mod : g_mods)
|
for (auto& mod : g_mods)
|
||||||
{
|
{
|
||||||
// TODO: Handle UMM merge archives!
|
|
||||||
for (auto& includeDir : mod.includeDirs)
|
for (auto& includeDir : mod.includeDirs)
|
||||||
{
|
{
|
||||||
if (appendArlFilePath.empty())
|
if (mod.type == ModType::UMM)
|
||||||
{
|
{
|
||||||
std::filesystem::path arlFilePath(std::u8string_view((const char8_t*)arlFilePathU8));
|
if (mod.merge)
|
||||||
appendArlFilePath = arlFilePath.parent_path();
|
{
|
||||||
appendArlFilePath /= "+";
|
// TODO: Merge archives without ARLs
|
||||||
appendArlFilePath += arlFilePath.filename();
|
if (arlFilePath.empty())
|
||||||
appendArlFilePath += ".arl";
|
{
|
||||||
}
|
arlFilePath = arlFilePathU8;
|
||||||
|
arlFilePath += ".arl";
|
||||||
|
}
|
||||||
|
|
||||||
parseArlFile(includeDir / appendArlFilePath);
|
parseArlFile(includeDir / arlFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mod.type == ModType::HMM)
|
||||||
|
{
|
||||||
|
if (appendArlFilePath.empty())
|
||||||
|
{
|
||||||
|
if (arlFilePath.empty())
|
||||||
|
arlFilePath = arlFilePathU8;
|
||||||
|
|
||||||
|
appendArlFilePath = arlFilePath.parent_path();
|
||||||
|
appendArlFilePath /= "+";
|
||||||
|
appendArlFilePath += arlFilePath.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
parseArlFile(includeDir / appendArlFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileNames.empty())
|
if (s_arlFileNames.empty())
|
||||||
{
|
{
|
||||||
__imp__sub_82E0D3E8(ctx, base);
|
__imp__sub_82E0D3E8(ctx, base);
|
||||||
return;
|
return;
|
||||||
|
|
@ -201,7 +224,7 @@ PPC_FUNC(sub_82E0D3E8)
|
||||||
size_t arlHeaderSize = parseArlFileData(base + ctx.r5.u32, ctx.r6.u32);
|
size_t arlHeaderSize = parseArlFileData(base + ctx.r5.u32, ctx.r6.u32);
|
||||||
size_t arlFileSize = arlHeaderSize;
|
size_t arlFileSize = arlHeaderSize;
|
||||||
|
|
||||||
for (auto& fileName : fileNames)
|
for (auto& fileName : s_arlFileNames)
|
||||||
{
|
{
|
||||||
arlFileSize += 1;
|
arlFileSize += 1;
|
||||||
arlFileSize += fileName.size();
|
arlFileSize += fileName.size();
|
||||||
|
|
@ -211,7 +234,7 @@ PPC_FUNC(sub_82E0D3E8)
|
||||||
memcpy(newArlFileData, base + ctx.r5.u32, arlHeaderSize);
|
memcpy(newArlFileData, base + ctx.r5.u32, arlHeaderSize);
|
||||||
|
|
||||||
uint8_t* arlFileNames = newArlFileData + arlHeaderSize;
|
uint8_t* arlFileNames = newArlFileData + arlHeaderSize;
|
||||||
for (auto& fileName : fileNames)
|
for (auto& fileName : s_arlFileNames)
|
||||||
{
|
{
|
||||||
*arlFileNames = uint8_t(fileName.size());
|
*arlFileNames = uint8_t(fileName.size());
|
||||||
++arlFileNames;
|
++arlFileNames;
|
||||||
|
|
@ -254,7 +277,7 @@ PPC_FUNC(sub_82E0B500)
|
||||||
auto r5 = ctx.r5; // Name
|
auto r5 = ctx.r5; // Name
|
||||||
auto r6 = ctx.r6; // Data
|
auto r6 = ctx.r6; // Data
|
||||||
auto r7 = ctx.r7; // Size
|
auto r7 = ctx.r7; // Size
|
||||||
auto r8 = ctx.r8; // Unknown
|
auto r8 = ctx.r8; // Callback data
|
||||||
|
|
||||||
std::u8string_view arFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r5.u32)));
|
std::u8string_view arFilePathU8(reinterpret_cast<const char8_t*>(base + PPC_LOAD_U32(ctx.r5.u32)));
|
||||||
size_t index = arFilePathU8.find(u8".ar.00");
|
size_t index = arFilePathU8.find(u8".ar.00");
|
||||||
|
|
@ -265,17 +288,19 @@ PPC_FUNC(sub_82E0B500)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = arFilePathU8.find(u8".ar");
|
index = arFilePathU8.find(u8".ar");
|
||||||
if (index != (arFilePathU8.size() - 3))
|
|
||||||
|
if (index != (arFilePathU8.size() - 3) ||
|
||||||
|
arFilePathU8.starts_with(u8"tg-") ||
|
||||||
|
arFilePathU8.starts_with(u8"gia-") ||
|
||||||
|
arFilePathU8.starts_with(u8"gi-texture-"))
|
||||||
{
|
{
|
||||||
__imp__sub_82E0B500(ctx, base);
|
__imp__sub_82E0B500(ctx, base);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path appendArFilePath;
|
thread_local std::filesystem::path s_tempFilePath;
|
||||||
|
s_tempFilePath.clear();
|
||||||
thread_local std::filesystem::path tempFilePath;
|
|
||||||
tempFilePath.clear();
|
|
||||||
|
|
||||||
auto loadArchive = [&](const std::filesystem::path& arFilePath)
|
auto loadArchive = [&](const std::filesystem::path& arFilePath)
|
||||||
{
|
{
|
||||||
|
|
@ -314,10 +339,10 @@ PPC_FUNC(sub_82E0B500)
|
||||||
|
|
||||||
auto loadArchives = [&](const std::filesystem::path& arFilePath, bool allowNoArl)
|
auto loadArchives = [&](const std::filesystem::path& arFilePath, bool allowNoArl)
|
||||||
{
|
{
|
||||||
tempFilePath = arFilePath;
|
s_tempFilePath = arFilePath;
|
||||||
tempFilePath += "l";
|
s_tempFilePath += "l";
|
||||||
|
|
||||||
std::ifstream stream(tempFilePath, std::ios::binary);
|
std::ifstream stream(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.
|
||||||
|
|
@ -334,9 +359,9 @@ PPC_FUNC(sub_82E0B500)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < splitCount; i++)
|
for (uint32_t i = 0; i < splitCount; i++)
|
||||||
{
|
{
|
||||||
tempFilePath = arFilePath;
|
s_tempFilePath = arFilePath;
|
||||||
tempFilePath += fmt::format(".{:02}", i);
|
s_tempFilePath += fmt::format(".{:02}", i);
|
||||||
loadArchive(tempFilePath);
|
loadArchive(s_tempFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,29 +371,48 @@ PPC_FUNC(sub_82E0B500)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; ; i++)
|
for (uint32_t i = 0; ; i++)
|
||||||
{
|
{
|
||||||
tempFilePath = arFilePath;
|
s_tempFilePath = arFilePath;
|
||||||
tempFilePath += fmt::format(".{:02}", i);
|
s_tempFilePath += fmt::format(".{:02}", i);
|
||||||
if (!loadArchive(tempFilePath))
|
if (!loadArchive(s_tempFilePath))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::filesystem::path arFilePath;
|
||||||
|
std::filesystem::path appendArFilePath;
|
||||||
|
|
||||||
for (auto& mod : g_mods)
|
for (auto& mod : g_mods)
|
||||||
{
|
{
|
||||||
// TODO: Need to check for UMM merge archives here.
|
|
||||||
for (auto& includeDir : mod.includeDirs)
|
for (auto& includeDir : mod.includeDirs)
|
||||||
{
|
{
|
||||||
if (appendArFilePath.empty())
|
if (mod.type == ModType::UMM)
|
||||||
{
|
{
|
||||||
std::filesystem::path arFilePath(arFilePathU8);
|
if (mod.merge)
|
||||||
appendArFilePath = arFilePath.parent_path();
|
{
|
||||||
appendArFilePath /= "+";
|
// TODO: Check for read only.
|
||||||
appendArFilePath += arFilePath.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadArchives(includeDir / appendArFilePath, false);
|
if (arFilePath.empty())
|
||||||
|
arFilePath = arFilePathU8;
|
||||||
|
|
||||||
|
loadArchives(includeDir / arFilePath, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mod.type == ModType::HMM)
|
||||||
|
{
|
||||||
|
if (appendArFilePath.empty())
|
||||||
|
{
|
||||||
|
if (arFilePath.empty())
|
||||||
|
arFilePath = arFilePathU8;
|
||||||
|
|
||||||
|
appendArFilePath = arFilePath.parent_path();
|
||||||
|
appendArFilePath /= "+";
|
||||||
|
appendArFilePath += arFilePath.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadArchives(includeDir / appendArFilePath, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue