From 5a0f03a299e87252e5b943f85f33a92e18984c65 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Sat, 22 Oct 2022 01:55:52 +0100 Subject: Allow rpaks to use starpaks that are in the mod's folder (#279) * this works * read starpak entires from rpak headers * formatting * move logging to after function * Use str.empty instead of str != "" * change allocatedNewPath to std::string * merge conflict resolution part 2 * change to use int + add comment * better comments + init variable * path syntax + another comment * use goto to exit loop + improve commenting * implement requested changes * remove accidental diff * explanatory comment --- NorthstarDLL/modmanager.cpp | 40 +++++++++++++++++++++++++++ NorthstarDLL/modmanager.h | 3 ++ NorthstarDLL/rpakfilesystem.cpp | 61 +++++++++++++++++++++++++++++++++-------- 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/NorthstarDLL/modmanager.cpp b/NorthstarDLL/modmanager.cpp index 05060902..07a197dd 100644 --- a/NorthstarDLL/modmanager.cpp +++ b/NorthstarDLL/modmanager.cpp @@ -469,6 +469,46 @@ void ModManager::LoadMods() modPak.m_sPakName = pakName; + // read header of file and get the starpak paths + // this is done here as opposed to on starpak load because multiple rpaks can load a starpak + // and there is seemingly no good way to tell which rpak is causing the load of a starpak :/ + + std::ifstream rpakStream(file.path(), std::ios::binary); + + // seek to the point in the header where the starpak reference size is + rpakStream.seekg(0x38, std::ios::beg); + int starpaksSize = 0; + rpakStream.read((char*)&starpaksSize, 2); + + // seek to just after the header + rpakStream.seekg(0x58, std::ios::beg); + // read the starpak reference(s) + std::vector buf(starpaksSize); + rpakStream.read(buf.data(), starpaksSize); + + rpakStream.close(); + + // split the starpak reference(s) into strings to hash + std::string str = ""; + for (int i = 0; i < starpaksSize; i++) + { + // if the current char is null, that signals the end of the current starpak path + if (buf[i] != 0x00) + { + str += buf[i]; + } + else + { + // only add the string we are making if it isnt empty + if (!str.empty()) + { + mod.StarpakPaths.push_back(STR_HASH(str)); + spdlog::info("Mod {} registered starpak '{}'", mod.Name, str); + str = ""; + } + } + } + // not using atm because we need to resolve path to rpak // if (m_hasLoadedMods && modPak.m_bAutoLoad) // g_pPakLoadManager->LoadPakAsync(pakName.c_str()); diff --git a/NorthstarDLL/modmanager.h b/NorthstarDLL/modmanager.h index f622c675..9f57c69a 100644 --- a/NorthstarDLL/modmanager.h +++ b/NorthstarDLL/modmanager.h @@ -100,6 +100,9 @@ class Mod std::vector Rpaks; std::unordered_map RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite + std::vector StarpakPaths; // starpaks that this mod contains + // there seems to be no nice way to get the rpak that is causing the load of a starpak? + // hashed with STR_HASH std::unordered_map DependencyConstants; diff --git a/NorthstarDLL/rpakfilesystem.cpp b/NorthstarDLL/rpakfilesystem.cpp index b1363589..5d5ac7ba 100644 --- a/NorthstarDLL/rpakfilesystem.cpp +++ b/NorthstarDLL/rpakfilesystem.cpp @@ -258,30 +258,69 @@ void*, __fastcall, (const char* pPath, void* pCallback)) // clang-format on { fs::path path(pPath); - char* allocatedNewPath = nullptr; + std::string newPath = ""; + fs::path filename = path.filename(); if (path.extension() == ".stbsp") { - fs::path filename = path.filename(); spdlog::info("LoadStreamBsp: {}", filename.string()); // resolve modded stbsp path so we can load mod stbsps auto modFile = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path("maps" / filename))); if (modFile != g_pModManager->m_ModFiles.end()) { - // need to allocate a new string for this - std::string newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string(); - allocatedNewPath = new char[newPath.size() + 1]; - strncpy_s(allocatedNewPath, newPath.size() + 1, newPath.c_str(), newPath.size()); - pPath = allocatedNewPath; + newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string(); + pPath = newPath.c_str(); } } + else if (path.extension() == ".starpak") + { + // code for this is mostly stolen from above + + // unfortunately I can't find a way to get the rpak that is causing this function call, so I have to + // store them on mod init and then compare the current path with the stored paths + + // game adds r2\ to every path, so assume that a starpak path that begins with r2\paks\ is a vanilla one + // modded starpaks will be in the mod's paks folder (but can be in folders within the paks folder) + + // this might look a bit confusing, but its just an iterator over the various directories in a path. + // path.begin() being the first directory, r2 in this case, which is guaranteed anyway, + // so increment the iterator with ++ to get the first actual directory, * just gets the actual value + // then we compare to "paks" to determine if it's a vanilla rpak or not + if (*++path.begin() != "paks") + { + // remove the r2\ from the start used for path lookups + std::string starpakPath = path.string().substr(3); + // hash the starpakPath to compare with stored entries + size_t hashed = STR_HASH(starpakPath); + + // loop through all loaded mods + for (Mod& mod : g_pModManager->m_LoadedMods) + { + // ignore non-loaded mods + if (!mod.m_bEnabled) + continue; - void* ret = ReadFileAsync(pPath, pCallback); - if (allocatedNewPath) - delete[] allocatedNewPath; + // loop through the stored starpak paths + for (size_t hash : mod.StarpakPaths) + { + if (hash == hashed) + { + // construct new path + newPath = (mod.m_ModDirectory / "paks" / starpakPath).string(); + // set path to the new path + pPath = newPath.c_str(); + goto LOG_STARPAK; + } + } + } + } + + LOG_STARPAK: + spdlog::info("LoadStreamPak: {}", filename.string()); + } - return ret; + return ReadFileAsync(pPath, pCallback); } ON_DLL_LOAD("engine.dll", RpakFilesystem, (CModule module)) -- cgit v1.2.3