Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PackageManager improvements #61

Merged
merged 7 commits into from
Dec 31, 2023
3 changes: 2 additions & 1 deletion SurrealEngine/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ Engine::Engine(GameLaunchInfo launchinfo) : LaunchInfo(launchinfo)
{
engine = this;

packages = std::make_unique<PackageManager>(LaunchInfo.folder, LaunchInfo.engineVersion, LaunchInfo.gameName);
//packages = std::make_unique<PackageManager>(LaunchInfo.folder, LaunchInfo.engineVersion, LaunchInfo.gameName);
packages = std::make_unique<PackageManager>(LaunchInfo);
}

Engine::~Engine()
Expand Down
93 changes: 75 additions & 18 deletions SurrealEngine/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,40 @@ std::string FilePath::remove_extension(const std::string &filename)
return filename.substr(0, filename.length() - file.length() + pos);
}

std::string FilePath::first_component(const std::string& path)
{
#ifdef WIN32
auto first_slash = path.find_first_of("/\\");
if (first_slash != std::string::npos)
return path.substr(0, first_slash);
else
return path;
#else
auto first_slash = path.find_first_of('/');
if (first_slash != std::string::npos)
return path.substr(0, first_slash);
else
return path;
#endif
}

std::string FilePath::remove_first_component(const std::string& path)
{
#ifdef WIN32
auto first_slash = path.find_first_of("/\\");
if (first_slash != std::string::npos)
return path.substr(first_slash + 1);
else
return std::string();
#else
auto first_slash = path.find_first_of('/');
if (first_slash != std::string::npos)
return path.substr(first_slash + 1);
else
return std::string();
#endif
}

std::string FilePath::last_component(const std::string &path)
{
#ifdef WIN32
Expand Down Expand Up @@ -409,27 +443,50 @@ std::string FilePath::remove_last_component(const std::string &path)

std::string FilePath::combine(const std::string &path1, const std::string &path2)
{
auto path1_conv = convert_path_delimiters(path1);
auto path2_conv = convert_path_delimiters(path2);
#ifdef WIN32
if (path1.empty())
return path2;
else if (path2.empty())
return path1;
else if (path2.front() == '/' || path2.front() == '\\')
return path2;
else if (path1.back() != '/' && path1.back() != '\\')
return path1 + "\\" + path2;
if (path1_conv.empty())
return path2_conv;
else if (path2_conv.empty())
return path1_conv;
else if (path2_conv.front() == '/' || path2_conv.front() == '\\')
return path2_conv;
else if (path1_conv.back() != '/' && path1_conv.back() != '\\')
return path1_conv + "\\" + path2_conv;
else
return path1 + path2;
return path1_conv + path2_conv;
#else
if (path1.empty())
return path2;
else if (path2.empty())
return path1;
else if (path2.front() == '/')
return path2;
else if (path1.back() != '/')
return path1 + "/" + path2;
if (path1_conv.empty())
return path2_conv;
else if (path2_conv.empty())
return path1_conv;
else if (path2_conv.front() == '/')
return path2_conv;
else if (path1_conv.back() != '/')
return path1_conv + "/" + path2_conv;
else
return path1 + path2;
return path1_conv + path2_conv;
#endif
}

std::string FilePath::convert_path_delimiters(const std::string &path)
{
std::string result = path;
#ifdef WIN32
auto pos = result.find("/");
while (pos != std::string::npos)
{
result.replace(result.find("/"), 1, "\\");
pos = result.find("/");
}
#else
auto pos = result.find("\\");
while (pos != std::string::npos)
{
result.replace(result.find("\\"), 1, "/");
pos = result.find("\\");
}
#endif
return result;
}
3 changes: 3 additions & 0 deletions SurrealEngine/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ class FilePath
static bool has_extension(const std::string &filename, const char *extension);
static std::string extension(const std::string &filename);
static std::string remove_extension(const std::string &filename);
static std::string first_component(const std::string& path);
static std::string remove_first_component(const std::string& path);
static std::string last_component(const std::string &path);
static std::string remove_last_component(const std::string &path);
static std::string combine(const std::string &path1, const std::string &path2);
static std::string convert_path_delimiters(const std::string &path);
};
18 changes: 18 additions & 0 deletions SurrealEngine/Package/IniFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ bool IniFile::ReadLine(const std::string& text, size_t& pos, std::string& line)
return true;
}

std::vector<NameString> IniFile::GetKeys(NameString sectionName) const
{
std::vector<NameString> result;

auto itSection = sections.find(sectionName);
if (itSection == sections.end())
return {};

const auto& values = itSection->second;

for (auto& key : values)
{
result.push_back(key.first);
}

return result;
}

std::string IniFile::GetValue(NameString sectionName, NameString keyName) const
{
auto itSection = sections.find(sectionName);
Expand Down
1 change: 1 addition & 0 deletions SurrealEngine/Package/IniFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class IniFile
IniFile() = default;
IniFile(const std::string& filename);

std::vector<NameString> GetKeys(NameString sectionName) const;
std::string GetValue(NameString sectionName, NameString keyName) const;
std::vector<std::string> GetValues(NameString sectionName, NameString keyName) const;

Expand Down
129 changes: 105 additions & 24 deletions SurrealEngine/Package/PackageManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
#include "Native/NScriptedPawn.h"
#include "Native/NPlayerPawnExt.h"

PackageManager::PackageManager(const std::string& basepath, int engineVersion, const std::string& gameName) : basepath(basepath), engineVersion(engineVersion), gameName(gameName)
PackageManager::PackageManager(const GameLaunchInfo& launchInfo) : launchInfo(launchInfo)
{
NActor::RegisterFunctions();
NCanvas::RegisterFunctions();
Expand All @@ -57,7 +57,7 @@ PackageManager::PackageManager(const std::string& basepath, int engineVersion, c
NInternetLink::RegisterFunctions();
NTcpLink::RegisterFunctions();
NUdpLink::RegisterFunctions();
if (gameName == "DeusEx")
if (IsDeusEx())
{
NDebugInfo::RegisterFunctions();
NDeusExDecoration::RegisterFunctions();
Expand All @@ -71,17 +71,9 @@ PackageManager::PackageManager(const std::string& basepath, int engineVersion, c
}

LoadIntFiles();
LoadPackageRemaps();
ScanForMaps();

// TODO: parse game ini for this info
ScanFolder("Maps", "*.unr");
ScanFolder("Maps", "*.dx"); // Deus Ex
if (IsUnreal1())
ScanFolder("Maps/UPak", "*.unr");
ScanFolder("Music", "*.umx");
ScanFolder("Sounds", "*.uax");
ScanFolder("System", "*.u");
ScanFolder("Textures", "*.utx");
ScanPaths();

InitPropertyOffsets(this);

Expand All @@ -95,15 +87,23 @@ Package* PackageManager::GetPackage(const NameString& name)
if (package)
return package.get();

auto it = packageFilenames.find(name);
// TODO: Is this how PackageRemap really works?
// There is no documentation of it anywhere it seems
NameString remapped_name = name;

auto remap_it = packageRemaps.find(name.ToString());
if (remap_it != packageRemaps.end())
{
remapped_name = NameString(remap_it->second);
}

auto it = packageFilenames.find(remapped_name);
if (it != packageFilenames.end())
{
package = std::make_unique<Package>(this, name, it->second);
package = std::make_unique<Package>(this, remapped_name, it->second);
}
else
{
if (name == "UnrealI")
return GetPackage("UnrealShare");
throw std::runtime_error("Could not find package " + name.ToString());
}

Expand All @@ -129,19 +129,58 @@ void PackageManager::UnloadPackage(const NameString& name)

void PackageManager::ScanForMaps()
{
std::string packagedir = FilePath::combine(basepath, "Maps");
std::string packagedir = FilePath::combine(launchInfo.folder, "Maps");
for (std::string filename : Directory::files(FilePath::combine(packagedir, "*.unr")))
{
maps.push_back(filename);
}
}

void PackageManager::ScanFolder(const std::string& name, const std::string& search)
void PackageManager::ScanFolder(const std::string& packagedir, const std::string& search)
{
std::string packagedir = FilePath::combine(basepath, name);
for (std::string filename : Directory::files(FilePath::combine(packagedir, search)))
{
packageFilenames[NameString(FilePath::remove_extension(filename))] = FilePath::combine(packagedir, filename);
// Do not add the package again if it exists
// This is useful for example when you have HD textures installed in a different folder
// And you wish to load them instead of the original ones
auto it = packageFilenames.find(NameString(FilePath::remove_extension(filename)));
if (it == packageFilenames.end())
packageFilenames[NameString(FilePath::remove_extension(filename))] = FilePath::combine(packagedir, filename);
}
}

void PackageManager::ScanPaths()
{
auto paths = GetIniValues("system", "Core.System", "Paths");

for (auto& current_path : paths)
{
// Get the filename
std::string filename = FilePath::last_component(current_path);
current_path = FilePath::remove_last_component(current_path);

// Paths are relative from the System folder the executable (and the ini file) is in
// So we should start from there
auto resulting_root_path = FilePath::combine(launchInfo.folder, "System");

auto first_component = FilePath::first_component(current_path);

while (first_component == ".." || first_component == ".")
{
if (first_component == "..")
{
// "Go one directory up" as many as the amount of ".."s in the current_path
resulting_root_path = FilePath::remove_last_component(resulting_root_path);
}

current_path = FilePath::remove_first_component(current_path);
first_component = FilePath::first_component(current_path);
}

// Combine everything
auto final_path = FilePath::combine(resulting_root_path, current_path);

ScanFolder(final_path, filename);
}
}

Expand Down Expand Up @@ -231,25 +270,67 @@ UClass* PackageManager::FindClass(const NameString& name)
}
}

std::vector<NameString> PackageManager::GetIniKeysFromSection(NameString iniName, const NameString& sectionName)
{
if (iniName == "system" || iniName == "System")
iniName = launchInfo.gameName;
else if (iniName == "user")
iniName = "User";

auto& ini = iniFiles[iniName];
if (!ini)
{
ini = std::make_unique<IniFile>(FilePath::combine(launchInfo.folder, "System/" + iniName.ToString() + ".ini"));
}

return ini->GetKeys(sectionName);
}

std::string PackageManager::GetIniValue(NameString iniName, const NameString& sectionName, const NameString& keyName)
{
if (iniName == "system" || iniName == "System")
iniName = gameName;
iniName = launchInfo.gameName;
else if (iniName == "user")
iniName = "User";

auto& ini = iniFiles[iniName];
if (!ini)
{
ini = std::make_unique<IniFile>(FilePath::combine(basepath, "System/" + iniName.ToString() + ".ini"));
ini = std::make_unique<IniFile>(FilePath::combine(launchInfo.folder, "System/" + iniName.ToString() + ".ini"));
}

return ini->GetValue(sectionName, keyName);
}

std::vector<std::string> PackageManager::GetIniValues(NameString iniName, const NameString& sectionName, const NameString& keyName)
{
if (iniName == "system" || iniName == "System")
iniName = launchInfo.gameName;
else if (iniName == "user")
iniName = "User";

auto& ini = iniFiles[iniName];
if (!ini)
{
ini = std::make_unique<IniFile>(FilePath::combine(launchInfo.folder, "System/" + iniName.ToString() + ".ini"));
}

return ini->GetValues(sectionName, keyName);
}

void PackageManager::LoadPackageRemaps()
{
auto remap_keys = GetIniKeysFromSection("system", "PackageRemap");

for (auto& key : remap_keys)
{
packageRemaps[key.ToString()] = GetIniValue("system", "PackageRemap", key);
}
}

void PackageManager::LoadIntFiles()
{
std::string systemdir = FilePath::combine(basepath, "System");
std::string systemdir = FilePath::combine(launchInfo.folder, "System");
for (std::string filename : Directory::files(FilePath::combine(systemdir, "*.int")))
{
try
Expand Down Expand Up @@ -316,7 +397,7 @@ std::string PackageManager::Localize(NameString packageName, const NameString& s
{
try
{
intFile = std::make_unique<IniFile>(FilePath::combine(basepath, "System/" + packageName.ToString() + ".int"));
intFile = std::make_unique<IniFile>(FilePath::combine(launchInfo.folder, "System/" + packageName.ToString() + ".int"));
}
catch (...)
{
Expand Down
Loading