Path manager refactor
This commit is contained in:
parent
eaf440bd03
commit
440d0b91b4
|
@ -6,6 +6,22 @@ using System.Linq;
|
||||||
|
|
||||||
namespace KeepersCompound.LGS;
|
namespace KeepersCompound.LGS;
|
||||||
|
|
||||||
|
// TODO: Make this nicer to use!
|
||||||
|
// Rather than navigating through the path manager Context should hold the current campaign resources
|
||||||
|
// Campaign resources should be lazy loaded (only get the paths when we first set it as campaign)
|
||||||
|
// Campaign resources should extend off of the base game resources and just overwrite any resource paths it needs to
|
||||||
|
|
||||||
|
enum ConfigFile
|
||||||
|
{
|
||||||
|
Cam,
|
||||||
|
CamExt,
|
||||||
|
CamMod,
|
||||||
|
Game,
|
||||||
|
Install,
|
||||||
|
User,
|
||||||
|
ConfigFileCount,
|
||||||
|
}
|
||||||
|
|
||||||
public enum ResourceType
|
public enum ResourceType
|
||||||
{
|
{
|
||||||
Mission,
|
Mission,
|
||||||
|
@ -16,21 +32,23 @@ public enum ResourceType
|
||||||
|
|
||||||
public class ResourcePathManager
|
public class ResourcePathManager
|
||||||
{
|
{
|
||||||
private record CampaignResources
|
public record CampaignResources
|
||||||
{
|
{
|
||||||
public Dictionary<string, string> missionPathMap;
|
public bool initialised = false;
|
||||||
public Dictionary<string, string> texturePathMap;
|
public string name;
|
||||||
public Dictionary<string, string> objectPathMap;
|
private readonly Dictionary<string, string> _missionPathMap = [];
|
||||||
public Dictionary<string, string> objectTexturePathMap;
|
private readonly Dictionary<string, string> _texturePathMap = [];
|
||||||
|
private readonly Dictionary<string, string> _objectPathMap = [];
|
||||||
|
private readonly Dictionary<string, string> _objectTexturePathMap = [];
|
||||||
|
|
||||||
public List<string> GetResourceNames(ResourceType type)
|
public List<string> GetResourceNames(ResourceType type)
|
||||||
{
|
{
|
||||||
List<string> keys = type switch
|
List<string> keys = type switch
|
||||||
{
|
{
|
||||||
ResourceType.Mission => [.. missionPathMap.Keys],
|
ResourceType.Mission => [.. _missionPathMap.Keys],
|
||||||
ResourceType.Object => [.. objectPathMap.Keys],
|
ResourceType.Object => [.. _objectPathMap.Keys],
|
||||||
ResourceType.ObjectTexture => [.. objectTexturePathMap.Keys],
|
ResourceType.ObjectTexture => [.. _objectTexturePathMap.Keys],
|
||||||
ResourceType.Texture => [.. texturePathMap.Keys],
|
ResourceType.Texture => [.. _texturePathMap.Keys],
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type)),
|
_ => throw new ArgumentOutOfRangeException(nameof(type)),
|
||||||
};
|
};
|
||||||
keys.Sort();
|
keys.Sort();
|
||||||
|
@ -41,13 +59,58 @@ public class ResourcePathManager
|
||||||
{
|
{
|
||||||
var map = type switch
|
var map = type switch
|
||||||
{
|
{
|
||||||
ResourceType.Mission => missionPathMap,
|
ResourceType.Mission => _missionPathMap,
|
||||||
ResourceType.Object => objectPathMap,
|
ResourceType.Object => _objectPathMap,
|
||||||
ResourceType.ObjectTexture => objectTexturePathMap,
|
ResourceType.ObjectTexture => _objectTexturePathMap,
|
||||||
ResourceType.Texture => texturePathMap,
|
ResourceType.Texture => _texturePathMap,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type)),
|
_ => throw new ArgumentOutOfRangeException(nameof(type)),
|
||||||
};
|
};
|
||||||
return map.TryGetValue(name, out var resourcePath) ? resourcePath : null;
|
return map.TryGetValue(name.ToLower(), out var resourcePath) ? resourcePath : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialise(string misPath, string resPath)
|
||||||
|
{
|
||||||
|
foreach (var path in Directory.GetFiles(misPath))
|
||||||
|
{
|
||||||
|
var convertedPath = ConvertSeparator(path);
|
||||||
|
var ext = Path.GetExtension(convertedPath).ToLower();
|
||||||
|
if (ext == ".mis" || ext == ".cow")
|
||||||
|
{
|
||||||
|
var baseName = Path.GetFileName(convertedPath).ToLower();
|
||||||
|
_missionPathMap[baseName] = convertedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (name, path) in GetTexturePaths(resPath))
|
||||||
|
{
|
||||||
|
_texturePathMap[name] = path;
|
||||||
|
}
|
||||||
|
foreach (var (name, path) in GetObjectPaths(resPath))
|
||||||
|
{
|
||||||
|
_objectPathMap[name] = path;
|
||||||
|
}
|
||||||
|
foreach (var (name, path) in GetObjectTexturePaths(resPath))
|
||||||
|
{
|
||||||
|
_objectTexturePathMap[name] = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialise(string misPath, string resPath, CampaignResources parent)
|
||||||
|
{
|
||||||
|
foreach (var (name, path) in parent._texturePathMap)
|
||||||
|
{
|
||||||
|
_texturePathMap[name] = path;
|
||||||
|
}
|
||||||
|
foreach (var (name, path) in parent._objectPathMap)
|
||||||
|
{
|
||||||
|
_objectPathMap[name] = path;
|
||||||
|
}
|
||||||
|
foreach (var (name, path) in parent._objectTexturePathMap)
|
||||||
|
{
|
||||||
|
_objectTexturePathMap[name] = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Initialise(misPath, resPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,70 +130,83 @@ public class ResourcePathManager
|
||||||
return path.Replace('\\', '/');
|
return path.Replace('\\', '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Init(string installPath)
|
public void Init(string installPath)
|
||||||
{
|
{
|
||||||
// TODO: This can be done less awkwardly with the resource paths
|
// TODO:
|
||||||
if (DirContainsThiefExe(installPath) &&
|
// - Determine if folder is a thief install
|
||||||
TryGetInstallCfgPath(installPath, out var installCfgPath) &&
|
// - Load all the (relevant) config files
|
||||||
TryBuildOmsPathMap(installPath, installCfgPath, out var omsMap) &&
|
// - Get base paths from configs
|
||||||
TryBuildFmsPathMap(installPath, out var fmsMap, out var fmsDir) &&
|
// - Build list of FM campaigns
|
||||||
TryGetPath(installPath, "fam.crf", out var famPath) &&
|
// - Initialise OM campaign resource paths
|
||||||
TryGetPath(installPath, "obj.crf", out var objPath))
|
// - Lazy load FM campaign resource paths (that inherit OM resources)
|
||||||
|
|
||||||
|
if (!DirContainsThiefExe(installPath))
|
||||||
{
|
{
|
||||||
// Register OM resources
|
throw new ArgumentException($"No Thief installation found at {installPath}", nameof(installPath));
|
||||||
{
|
|
||||||
var famExtractPath = Path.Join(_extractionPath, "fam");
|
|
||||||
if (Directory.Exists(famExtractPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(famExtractPath, true);
|
|
||||||
}
|
|
||||||
ZipFile.OpenRead(famPath).ExtractToDirectory(famExtractPath);
|
|
||||||
var texturePathMap = GetTexturePaths(_extractionPath);
|
|
||||||
|
|
||||||
var objExtractPath = Path.Join(_extractionPath, "obj");
|
|
||||||
if (Directory.Exists(objExtractPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(objExtractPath, true);
|
|
||||||
}
|
|
||||||
ZipFile.OpenRead(objPath).ExtractToDirectory(objExtractPath);
|
|
||||||
var objectPathMap = GetObjectPaths(_extractionPath);
|
|
||||||
var objectTexturePathMap = GetObjectTexturePaths(_extractionPath);
|
|
||||||
|
|
||||||
_omResources = new CampaignResources
|
|
||||||
{
|
|
||||||
missionPathMap = omsMap,
|
|
||||||
texturePathMap = texturePathMap,
|
|
||||||
objectPathMap = objectPathMap,
|
|
||||||
objectTexturePathMap = objectTexturePathMap,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
_fmResources = new Dictionary<string, CampaignResources>();
|
|
||||||
foreach (var (campaign, missionPathMap) in fmsMap)
|
|
||||||
{
|
|
||||||
var root = Path.Join(fmsDir, campaign);
|
|
||||||
var texturePathMap = GetTexturePaths(root);
|
|
||||||
var objectPathMap = GetObjectPaths(root);
|
|
||||||
var objectTexturePathMap = GetObjectTexturePaths(root);
|
|
||||||
|
|
||||||
var resource = new CampaignResources
|
|
||||||
{
|
|
||||||
missionPathMap = missionPathMap,
|
|
||||||
texturePathMap = texturePathMap,
|
|
||||||
objectPathMap = objectPathMap,
|
|
||||||
objectTexturePathMap = objectTexturePathMap,
|
|
||||||
};
|
|
||||||
_fmResources.Add(campaign, resource);
|
|
||||||
_fmsDir = fmsDir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialised = true;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// TODO: Should these paths be stored?
|
||||||
|
if (!TryGetConfigPaths(installPath, out var configPaths))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to find all installation config paths.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the paths of the base Fam and Obj resources so we can extract them.
|
||||||
|
var installCfgLines = File.ReadAllLines(configPaths[(int)ConfigFile.Install]);
|
||||||
|
FindConfigVar(installCfgLines, "resname_base", out var resPaths);
|
||||||
|
var baseFamPath = "";
|
||||||
|
var baseObjPath = "";
|
||||||
|
foreach (var resPath in resPaths.Split('+'))
|
||||||
|
{
|
||||||
|
var dir = Path.Join(installPath, ConvertSeparator(resPath));
|
||||||
|
foreach (var path in Directory.GetFiles(dir))
|
||||||
|
{
|
||||||
|
var name = Path.GetFileName(path).ToLower();
|
||||||
|
if (name == "fam.crf" && baseFamPath == "")
|
||||||
|
{
|
||||||
|
baseFamPath = path;
|
||||||
|
}
|
||||||
|
else if (name == "obj.crf" && baseObjPath == "")
|
||||||
|
{
|
||||||
|
baseObjPath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the extraction bro
|
||||||
|
(string, string)[] resources = [("fam", baseFamPath), ("obj", baseObjPath)];
|
||||||
|
foreach (var (extractName, zipPath) in resources)
|
||||||
|
{
|
||||||
|
var extractPath = Path.Join(_extractionPath, extractName);
|
||||||
|
if (Directory.Exists(extractPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(extractPath, true);
|
||||||
|
}
|
||||||
|
ZipFile.OpenRead(zipPath).ExtractToDirectory(extractPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
FindConfigVar(installCfgLines, "load_path", out var omsPath);
|
||||||
|
omsPath = Path.Join(installPath, ConvertSeparator(omsPath));
|
||||||
|
_omResources = new CampaignResources();
|
||||||
|
_omResources.name = "";
|
||||||
|
_omResources.Initialise(omsPath, _extractionPath);
|
||||||
|
|
||||||
|
var camModLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamMod]);
|
||||||
|
FindConfigVar(camModLines, "fm_path", out var fmsPath, "FMs");
|
||||||
|
_fmsDir = Path.Join(installPath, fmsPath);
|
||||||
|
|
||||||
|
// Build up the map of FM campaigns. These are uninitialised, we just want
|
||||||
|
// to have their name
|
||||||
|
_fmResources = new Dictionary<string, CampaignResources>();
|
||||||
|
foreach (var dir in Directory.GetDirectories(_fmsDir))
|
||||||
|
{
|
||||||
|
var name = Path.GetFileName(dir);
|
||||||
|
var fmResource = new CampaignResources();
|
||||||
|
fmResource.name = name;
|
||||||
|
_fmResources.Add(name, fmResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> GetCampaignNames()
|
public List<string> GetCampaignNames()
|
||||||
|
@ -142,60 +218,25 @@ public class ResourcePathManager
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> GetResourceNames(ResourceType type, string campaignName)
|
public CampaignResources GetCampaign(string campaignName)
|
||||||
{
|
{
|
||||||
if (!_initialised)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Resource Path Manager hasn't been initialised.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (campaignName == null || campaignName == "")
|
if (campaignName == null || campaignName == "")
|
||||||
{
|
{
|
||||||
return _omResources.GetResourceNames(type);
|
return _omResources;
|
||||||
}
|
}
|
||||||
else if (_fmResources.TryGetValue(campaignName, out var campaign))
|
else if (_fmResources.TryGetValue(campaignName, out var campaign))
|
||||||
{
|
{
|
||||||
return campaign.GetResourceNames(type);
|
if (!campaign.initialised)
|
||||||
|
{
|
||||||
|
var fmPath = Path.Join(_fmsDir, campaignName);
|
||||||
|
campaign.Initialise(fmPath, fmPath, _omResources);
|
||||||
|
}
|
||||||
|
return campaign;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException("No campaign found with given name", nameof(campaignName));
|
throw new ArgumentException("No campaign found with given name", nameof(campaignName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This expects resourceName to already have it's separator converted
|
|
||||||
public (string, string) GetResourcePath(
|
|
||||||
ResourceType type,
|
|
||||||
string campaignName,
|
|
||||||
string resourceName)
|
|
||||||
{
|
|
||||||
if (!_initialised)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Resource Path Manager hasn't been initialised.");
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceName = resourceName.ToLower();
|
|
||||||
var omResourcePath = _omResources.GetResourcePath(type, resourceName);
|
|
||||||
|
|
||||||
if (campaignName == null || campaignName == "" && omResourcePath != null)
|
|
||||||
{
|
|
||||||
return ("", omResourcePath);
|
|
||||||
}
|
|
||||||
else if (_fmResources.TryGetValue(campaignName, out var campaign))
|
|
||||||
{
|
|
||||||
var fmResourcePath = campaign.GetResourcePath(type, resourceName);
|
|
||||||
if (fmResourcePath != null)
|
|
||||||
{
|
|
||||||
return (campaignName, fmResourcePath);
|
|
||||||
}
|
|
||||||
else if (omResourcePath != null)
|
|
||||||
{
|
|
||||||
return ("", omResourcePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// throw new ArgumentException($"No resource found with given type and name: {type}, {resourceName}", nameof(resourceName));
|
|
||||||
return (null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<string, string> GetObjectTexturePaths(string root)
|
private static Dictionary<string, string> GetObjectTexturePaths(string root)
|
||||||
{
|
{
|
||||||
string[] validExtensions = { ".dds", ".png", ".tga", ".pcx", ".gif", ".bmp", ".cel", };
|
string[] validExtensions = { ".dds", ".png", ".tga", ".pcx", ".gif", ".bmp", ".cel", };
|
||||||
|
@ -277,100 +318,11 @@ public class ResourcePathManager
|
||||||
return pathMap;
|
return pathMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryBuildOmsPathMap(string root, string cfgPath, out Dictionary<string, string> map)
|
/// <summary>
|
||||||
{
|
/// Determine if the given directory contains a Thief executable at the top level.
|
||||||
map = new Dictionary<string, string>();
|
/// </summary>
|
||||||
|
/// <param name="dir">The directory to search</param>
|
||||||
var omsPath = "";
|
/// <returns><c>true</c> if a Thief executable was found, <c>false</c> otherwise.</returns>
|
||||||
foreach (var line in File.ReadLines(cfgPath))
|
|
||||||
{
|
|
||||||
if (line.StartsWith("load_path"))
|
|
||||||
{
|
|
||||||
// TODO: This can have multiple paths I think
|
|
||||||
var path = ConvertSeparator(line.Split(" ")[1]);
|
|
||||||
omsPath = Path.GetFullPath(root + path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (omsPath == "")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchOptions = new EnumerationOptions
|
|
||||||
{
|
|
||||||
MatchCasing = MatchCasing.CaseInsensitive,
|
|
||||||
};
|
|
||||||
foreach (var path in Directory.GetFiles(omsPath, "*.mis", searchOptions))
|
|
||||||
{
|
|
||||||
var convertedPath = ConvertSeparator(path);
|
|
||||||
var baseName = Path.GetFileName(convertedPath).ToLower();
|
|
||||||
map.Add(baseName, convertedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryBuildFmsPathMap(
|
|
||||||
string root,
|
|
||||||
out Dictionary<string, Dictionary<string, string>> fmsMap,
|
|
||||||
out string fmsPath)
|
|
||||||
{
|
|
||||||
fmsMap = new Dictionary<string, Dictionary<string, string>>();
|
|
||||||
fmsPath = root + "/FMs";
|
|
||||||
|
|
||||||
var searchOptions = new EnumerationOptions
|
|
||||||
{
|
|
||||||
MatchCasing = MatchCasing.CaseInsensitive,
|
|
||||||
RecurseSubdirectories = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cam Mod tells us where any FMs are installed (alongside other things)
|
|
||||||
// If it doesn't exist then something is wrong with the install directory
|
|
||||||
if (!TryGetPath(root, "cam_mod.ini", out var camModPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're looking for an uncommented line defining an fm_path, but its
|
|
||||||
// find if we don't find one because there's a default path.
|
|
||||||
foreach (var line in File.ReadLines(camModPath))
|
|
||||||
{
|
|
||||||
if (line.StartsWith("fm_path"))
|
|
||||||
{
|
|
||||||
// TODO: I think this can technically contain multiple paths
|
|
||||||
var path = ConvertSeparator(line.Split(" ")[1]);
|
|
||||||
fmsPath = Path.GetFullPath(root + path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can iterate through all the FM directories and map their mission paths
|
|
||||||
// if they have any.
|
|
||||||
string[] extensions = { ".mis", ".cow" };
|
|
||||||
searchOptions.RecurseSubdirectories = false;
|
|
||||||
foreach (var dir in Directory.GetDirectories(fmsPath))
|
|
||||||
{
|
|
||||||
var campaignMap = new Dictionary<string, string>();
|
|
||||||
foreach (var path in Directory.GetFiles(dir))
|
|
||||||
{
|
|
||||||
var convertedPath = ConvertSeparator(path);
|
|
||||||
if (extensions.Contains(Path.GetExtension(convertedPath).ToLower()))
|
|
||||||
{
|
|
||||||
var baseName = Path.GetFileName(convertedPath).ToLower();
|
|
||||||
campaignMap.Add(baseName, ConvertSeparator(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (campaignMap.Count != 0)
|
|
||||||
{
|
|
||||||
fmsMap.Add(Path.GetFileName(dir), campaignMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool DirContainsThiefExe(string dir)
|
private static bool DirContainsThiefExe(string dir)
|
||||||
{
|
{
|
||||||
var searchOptions = new EnumerationOptions
|
var searchOptions = new EnumerationOptions
|
||||||
|
@ -390,44 +342,108 @@ public class ResourcePathManager
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetInstallCfgPath(string dir, out string installCfgPath)
|
/// <summary>
|
||||||
|
/// Get an array of of all the Dark config file paths.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="installPath">Root directory of the Thief installation.</param>
|
||||||
|
/// <param name="configPaths">Output array of config file paths</param>
|
||||||
|
/// <returns><c>true</c> if all config files were found, <c>false</c> otherwise.</returns>
|
||||||
|
private static bool TryGetConfigPaths(string installPath, out string[] configPaths)
|
||||||
{
|
{
|
||||||
|
configPaths = new string[(int)ConfigFile.ConfigFileCount];
|
||||||
|
|
||||||
var searchOptions = new EnumerationOptions
|
var searchOptions = new EnumerationOptions
|
||||||
{
|
{
|
||||||
MatchCasing = MatchCasing.CaseInsensitive,
|
MatchCasing = MatchCasing.CaseInsensitive,
|
||||||
RecurseSubdirectories = true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var path in Directory.GetFiles(dir, "*.cfg", searchOptions))
|
// `cam.cfg`, `cam_ext.cfg`, and `cam_mod.ini` are always in the root of the install.
|
||||||
|
// The first two configs will tell us if any other configs are in non-default locations.
|
||||||
|
// We can't just do a recursive search for everything else because they can potentially
|
||||||
|
// be *outside* of the Thief installation.
|
||||||
|
foreach (var path in Directory.GetFiles(installPath, "cam*", searchOptions))
|
||||||
{
|
{
|
||||||
var baseName = Path.GetFileName(path).ToLower();
|
var name = Path.GetFileName(path).ToLower();
|
||||||
if (baseName == "install.cfg" || baseName == "darkinst.cfg")
|
if (name == "cam.cfg")
|
||||||
{
|
{
|
||||||
installCfgPath = path;
|
configPaths[(int)ConfigFile.Cam] = path;
|
||||||
|
}
|
||||||
|
else if (name == "cam_ext.cfg")
|
||||||
|
{
|
||||||
|
configPaths[(int)ConfigFile.CamExt] = path;
|
||||||
|
}
|
||||||
|
else if (name == "cam_mod.ini")
|
||||||
|
{
|
||||||
|
configPaths[(int)ConfigFile.CamMod] = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var camExtLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamExt]);
|
||||||
|
var camLines = File.ReadAllLines(configPaths[(int)ConfigFile.Cam]);
|
||||||
|
|
||||||
|
bool FindCamVar(string varName, out string value, string defaultValue = "")
|
||||||
|
{
|
||||||
|
return FindConfigVar(camExtLines, varName, out value, defaultValue) ||
|
||||||
|
FindConfigVar(camLines, varName, out value, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
FindCamVar("include_path", out var includePath, "./");
|
||||||
|
FindCamVar("game", out var gameName);
|
||||||
|
FindCamVar($"{gameName}_include_install_cfg", out var installCfgName);
|
||||||
|
FindCamVar("include_user_cfg", out var userCfgName);
|
||||||
|
|
||||||
|
// TODO: How to handle case-insensitive absolute paths?
|
||||||
|
// Fixup the include path to "work" cross-platform
|
||||||
|
includePath = ConvertSeparator(includePath);
|
||||||
|
includePath = Path.Join(installPath, includePath);
|
||||||
|
if (!Directory.Exists(includePath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var path in Directory.GetFiles(includePath, "*.cfg", searchOptions))
|
||||||
|
{
|
||||||
|
var name = Path.GetFileName(path).ToLower();
|
||||||
|
if (name == $"{gameName}.cfg")
|
||||||
|
{
|
||||||
|
configPaths[(int)ConfigFile.Game] = path;
|
||||||
|
}
|
||||||
|
else if (name == installCfgName.ToLower())
|
||||||
|
{
|
||||||
|
configPaths[(int)ConfigFile.Install] = path;
|
||||||
|
}
|
||||||
|
else if (name == userCfgName.ToLower())
|
||||||
|
{
|
||||||
|
configPaths[(int)ConfigFile.User] = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we found everything
|
||||||
|
var i = 0;
|
||||||
|
foreach (var path in configPaths)
|
||||||
|
{
|
||||||
|
if (path == null || path == "")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool FindConfigVar(string[] lines, string varName, out string value, string defaultValue = "")
|
||||||
|
{
|
||||||
|
value = defaultValue;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (line.StartsWith(varName))
|
||||||
|
{
|
||||||
|
value = line[(line.IndexOf(' ') + 1)..];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installCfgPath = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetPath(string dir, string searchPattern, out string path)
|
|
||||||
{
|
|
||||||
var searchOptions = new EnumerationOptions
|
|
||||||
{
|
|
||||||
MatchCasing = MatchCasing.CaseInsensitive,
|
|
||||||
RecurseSubdirectories = true
|
|
||||||
};
|
|
||||||
|
|
||||||
var paths = Directory.GetFiles(dir, searchPattern, searchOptions);
|
|
||||||
if (paths.Length > 0)
|
|
||||||
{
|
|
||||||
path = paths[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = "";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,15 +9,20 @@ public partial class Context : Node
|
||||||
|
|
||||||
public ResourcePathManager PathManager { get; private set; }
|
public ResourcePathManager PathManager { get; private set; }
|
||||||
public ModelLoader ModelLoader { get; private set; }
|
public ModelLoader ModelLoader { get; private set; }
|
||||||
public string CampaignName { get; set; }
|
public ResourcePathManager.CampaignResources CampaignResources { get; private set; }
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
var extractPath = ProjectSettings.GlobalizePath($"user://extracted/tmp");
|
var extractPath = ProjectSettings.GlobalizePath($"user://extracted/tmp");
|
||||||
PathManager = new ResourcePathManager(extractPath);
|
PathManager = new ResourcePathManager(extractPath);
|
||||||
ModelLoader = new ModelLoader();
|
ModelLoader = new ModelLoader();
|
||||||
CampaignName = "";
|
CampaignResources = PathManager.GetCampaign("");
|
||||||
|
|
||||||
Instance = this;
|
Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetCampaign(string campaignName)
|
||||||
|
{
|
||||||
|
CampaignResources = PathManager.GetCampaign(campaignName);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -58,15 +58,11 @@ public partial class Mission : Node3D
|
||||||
var resourceSelector = GetNode<Control>("%ResourceSelector") as ResourceSelector;
|
var resourceSelector = GetNode<Control>("%ResourceSelector") as ResourceSelector;
|
||||||
resourceSelector.ResourceSelected += (string campaign, string mission) =>
|
resourceSelector.ResourceSelected += (string campaign, string mission) =>
|
||||||
{
|
{
|
||||||
var context = Context.Instance;
|
|
||||||
context.CampaignName = campaign;
|
|
||||||
_missionName = mission;
|
_missionName = mission;
|
||||||
var (newCampaign, missionPath) = context.PathManager.GetResourcePath(ResourceType.Mission, campaign, mission);
|
|
||||||
if (newCampaign != campaign)
|
var context = Context.Instance;
|
||||||
{
|
context.SetCampaign(campaign);
|
||||||
GD.Print($"Didn't find campaign mission: ({campaign}, {mission})");
|
FileName = context.CampaignResources.GetResourcePath(ResourceType.Mission, mission);
|
||||||
}
|
|
||||||
FileName = missionPath;
|
|
||||||
Build = true;
|
Build = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -120,7 +116,7 @@ public partial class Mission : Node3D
|
||||||
{
|
{
|
||||||
ClearMap();
|
ClearMap();
|
||||||
|
|
||||||
_textureLoader = new TextureLoader(Context.Instance.CampaignName);
|
_textureLoader = new TextureLoader();
|
||||||
Timing.TimeStage("DbFile Parse", () => _file = new(FileName));
|
Timing.TimeStage("DbFile Parse", () => _file = new(FileName));
|
||||||
Timing.TimeStage("Register Textures", () => UseChunk<TxList>("TXLIST", RegisterTextures));
|
Timing.TimeStage("Register Textures", () => UseChunk<TxList>("TXLIST", RegisterTextures));
|
||||||
Timing.TimeStage("Build WR", () => UseChunk<WorldRep>("WREXT", BuildWrMeshes));
|
Timing.TimeStage("Build WR", () => UseChunk<WorldRep>("WREXT", BuildWrMeshes));
|
||||||
|
@ -285,19 +281,18 @@ public partial class Mission : Node3D
|
||||||
|
|
||||||
path = prop.value;
|
path = prop.value;
|
||||||
|
|
||||||
var pathManager = Context.Instance.PathManager;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
var campaign = Context.Instance.CampaignName;
|
|
||||||
if (path.StartsWith("fam", StringComparison.OrdinalIgnoreCase))
|
if (path.StartsWith("fam", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var resType = ResourceType.Texture;
|
var resType = ResourceType.Texture;
|
||||||
path = pathManager.GetResourcePath(resType, campaign, prop.value).Item2;
|
path = campaignResources.GetResourcePath(resType, prop.value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var resType = ResourceType.ObjectTexture;
|
var resType = ResourceType.ObjectTexture;
|
||||||
var convertedValue = ResourcePathManager.ConvertSeparator(prop.value);
|
var convertedValue = ResourcePathManager.ConvertSeparator(prop.value);
|
||||||
var resName = Path.GetFileNameWithoutExtension(convertedValue);
|
var resName = Path.GetFileNameWithoutExtension(convertedValue);
|
||||||
path = pathManager.GetResourcePath(resType, campaign, resName).Item2;
|
path = campaignResources.GetResourcePath(resType, resName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path != null;
|
return path != null;
|
||||||
|
|
|
@ -18,7 +18,7 @@ public partial class Model : Node3D
|
||||||
node.QueueFree();
|
node.QueueFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Instance.CampaignName = campaignName;
|
Context.Instance.SetCampaign(campaignName);
|
||||||
var model = Context.Instance.ModelLoader.Load(modelPath);
|
var model = Context.Instance.ModelLoader.Load(modelPath);
|
||||||
AddChild(model);
|
AddChild(model);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,15 @@ using KeepersCompound.LGS;
|
||||||
|
|
||||||
namespace KeepersCompound.TMV;
|
namespace KeepersCompound.TMV;
|
||||||
|
|
||||||
|
// TODO: Work out a way to share base game models again in the cache
|
||||||
public class ModelLoader
|
public class ModelLoader
|
||||||
{
|
{
|
||||||
private readonly Dictionary<(string, string), MeshInstance3D> _cache = new();
|
private readonly Dictionary<(string, string), MeshInstance3D> _cache = new();
|
||||||
|
|
||||||
public MeshInstance3D Load(string modelName, bool forceLoad = false)
|
public MeshInstance3D Load(string modelName, bool forceLoad = false)
|
||||||
{
|
{
|
||||||
var campaignName = Context.Instance.CampaignName;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
|
var campaignName = campaignResources.name;
|
||||||
campaignName ??= "";
|
campaignName ??= "";
|
||||||
|
|
||||||
if (!forceLoad)
|
if (!forceLoad)
|
||||||
|
@ -27,20 +29,20 @@ public class ModelLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't care if this is null actually, we'll still cache that it's null lol
|
// We don't care if this is null actually, we'll still cache that it's null lol
|
||||||
var model = Timing.TimeStage("Load Models", () => { return LoadModel(ref campaignName, modelName); });
|
var model = Timing.TimeStage("Load Models", () => { return LoadModel(modelName); });
|
||||||
_cache[(campaignName, modelName)] = model;
|
_cache[(campaignName, modelName)] = model;
|
||||||
return model?.Duplicate() as MeshInstance3D;
|
return model?.Duplicate() as MeshInstance3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MeshInstance3D LoadModel(ref string campaignName, string modelName)
|
public static MeshInstance3D LoadModel(string modelName)
|
||||||
{
|
{
|
||||||
var pathManager = Context.Instance.PathManager;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
var (newCampaignName, modelPath) = pathManager.GetResourcePath(ResourceType.Object, campaignName, modelName);
|
var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName);
|
||||||
campaignName = newCampaignName;
|
|
||||||
if (modelPath == null)
|
if (modelPath == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var modelFile = new ModelFile(modelPath);
|
var modelFile = new ModelFile(modelPath);
|
||||||
if (modelFile == null)
|
if (modelFile == null)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +57,7 @@ public class ModelLoader
|
||||||
{
|
{
|
||||||
var convertedName = ResourcePathManager.ConvertSeparator(material.Name);
|
var convertedName = ResourcePathManager.ConvertSeparator(material.Name);
|
||||||
var resName = Path.GetFileNameWithoutExtension(convertedName);
|
var resName = Path.GetFileNameWithoutExtension(convertedName);
|
||||||
var (_, path) = pathManager.GetResourcePath(ResourceType.ObjectTexture, campaignName, resName);
|
var path = campaignResources.GetResourcePath(ResourceType.ObjectTexture, resName);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
{
|
{
|
||||||
// Might fail in exported projects
|
// Might fail in exported projects
|
||||||
|
|
|
@ -7,15 +7,13 @@ namespace KeepersCompound.TMV;
|
||||||
|
|
||||||
public partial class TextureLoader
|
public partial class TextureLoader
|
||||||
{
|
{
|
||||||
private readonly string _fmName;
|
|
||||||
private readonly List<Texture2D> _textureCache = new();
|
private readonly List<Texture2D> _textureCache = new();
|
||||||
private readonly Dictionary<int, int> _idMap = new();
|
private readonly Dictionary<int, int> _idMap = new();
|
||||||
private readonly Dictionary<int, string> _idPathMap = new();
|
private readonly Dictionary<int, string> _idPathMap = new();
|
||||||
private readonly Dictionary<string, int> _pathMap = new();
|
private readonly Dictionary<string, int> _pathMap = new();
|
||||||
|
|
||||||
public TextureLoader(string fmName)
|
public TextureLoader()
|
||||||
{
|
{
|
||||||
_fmName = fmName;
|
|
||||||
LoadDefaultTextures();
|
LoadDefaultTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +33,8 @@ public partial class TextureLoader
|
||||||
private bool Load(int id, string path)
|
private bool Load(int id, string path)
|
||||||
{
|
{
|
||||||
var loaded = false;
|
var loaded = false;
|
||||||
var pathManager = Context.Instance.PathManager;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
var (_, texPath) = pathManager.GetResourcePath(ResourceType.Texture, _fmName, path);
|
var texPath = campaignResources.GetResourcePath(ResourceType.Texture, path);
|
||||||
|
|
||||||
if (texPath != null)
|
if (texPath != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,10 +54,8 @@ public partial class ResourceSelector : Control
|
||||||
private void SetInstallPath(string path)
|
private void SetInstallPath(string path)
|
||||||
{
|
{
|
||||||
_FolderPath.Text = path;
|
_FolderPath.Text = path;
|
||||||
if (Context.Instance.PathManager.Init(path))
|
Context.Instance.PathManager.Init(path);
|
||||||
{
|
BuildCampaignList();
|
||||||
BuildCampaignList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildCampaignList()
|
private void BuildCampaignList()
|
||||||
|
@ -79,10 +77,11 @@ public partial class ResourceSelector : Control
|
||||||
_Resources.Clear();
|
_Resources.Clear();
|
||||||
_LoadButton.Disabled = true;
|
_LoadButton.Disabled = true;
|
||||||
|
|
||||||
var pathManager = Context.Instance.PathManager;
|
|
||||||
var campaignName = idx == 0 ? null : _Campaigns.GetItemText((int)idx);
|
var campaignName = idx == 0 ? null : _Campaigns.GetItemText((int)idx);
|
||||||
var resourceNammes = pathManager.GetResourceNames(ResType, campaignName);
|
var pathManager = Context.Instance.PathManager;
|
||||||
foreach (var resource in resourceNammes)
|
var campaignResources = pathManager.GetCampaign(campaignName);
|
||||||
|
var resourceNames = campaignResources.GetResourceNames(ResType);
|
||||||
|
foreach (var resource in resourceNames)
|
||||||
{
|
{
|
||||||
_Resources.AddItem(resource);
|
_Resources.AddItem(resource);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public partial class TextureBrowser : Node
|
||||||
{
|
{
|
||||||
// !HACK TEMP
|
// !HACK TEMP
|
||||||
Context.Instance.PathManager.Init("/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/");
|
Context.Instance.PathManager.Init("/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/");
|
||||||
Context.Instance.CampaignName = "TheBlackParade_1_0";
|
Context.Instance.SetCampaign("TheBlackParade_1_0");
|
||||||
|
|
||||||
_folderTree = GetNode<Tree>("%FolderTree");
|
_folderTree = GetNode<Tree>("%FolderTree");
|
||||||
_searchBar = GetNode<LineEdit>("%SearchBar");
|
_searchBar = GetNode<LineEdit>("%SearchBar");
|
||||||
|
@ -72,8 +72,8 @@ public partial class TextureBrowser : Node
|
||||||
{
|
{
|
||||||
_folderTree.Clear();
|
_folderTree.Clear();
|
||||||
|
|
||||||
var context = Context.Instance;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
var textureNames = context.PathManager.GetResourceNames(LGS.ResourceType.Texture, context.CampaignName);
|
var textureNames = campaignResources.GetResourceNames(LGS.ResourceType.Texture);
|
||||||
|
|
||||||
var treeItems = new Dictionary<string, TreeItem>();
|
var treeItems = new Dictionary<string, TreeItem>();
|
||||||
treeItems.Add("", _folderTree.CreateItem());
|
treeItems.Add("", _folderTree.CreateItem());
|
||||||
|
@ -102,13 +102,12 @@ public partial class TextureBrowser : Node
|
||||||
child.QueueFree();
|
child.QueueFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
var pathManager = Context.Instance.PathManager;
|
var campaignResources = Context.Instance.CampaignResources;
|
||||||
var campaign = Context.Instance.CampaignName;
|
|
||||||
var resType = LGS.ResourceType.Texture;
|
var resType = LGS.ResourceType.Texture;
|
||||||
var textureNames = pathManager.GetResourceNames(resType, campaign);
|
var textureNames = campaignResources.GetResourceNames(resType);
|
||||||
foreach (var name in textureNames)
|
foreach (var name in textureNames)
|
||||||
{
|
{
|
||||||
var (_, path) = pathManager.GetResourcePath(resType, campaign, name);
|
var path = campaignResources.GetResourcePath(resType, name);
|
||||||
var texture = TextureLoader.LoadTexture(path);
|
var texture = TextureLoader.LoadTexture(path);
|
||||||
|
|
||||||
var textureRect = new TextureRect();
|
var textureRect = new TextureRect();
|
||||||
|
|
Loading…
Reference in New Issue