Path manager refactor

This commit is contained in:
Jarrod Doyle 2024-09-19 21:16:07 +01:00
parent eaf440bd03
commit 440d0b91b4
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
8 changed files with 293 additions and 279 deletions

View File

@ -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)
{
// Register OM resources
{
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 (!DirContainsThiefExe(installPath))
if (Directory.Exists(objExtractPath))
{ {
Directory.Delete(objExtractPath, true); throw new ArgumentException($"No Thief installation found at {installPath}", nameof(installPath));
}
ZipFile.OpenRead(objPath).ExtractToDirectory(objExtractPath);
var objectPathMap = GetObjectPaths(_extractionPath);
var objectTexturePathMap = GetObjectTexturePaths(_extractionPath);
_omResources = new CampaignResources
{
missionPathMap = omsMap,
texturePathMap = texturePathMap,
objectPathMap = objectPathMap,
objectTexturePathMap = objectTexturePathMap,
};
} }
// 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>(); _fmResources = new Dictionary<string, CampaignResources>();
foreach (var (campaign, missionPathMap) in fmsMap) foreach (var dir in Directory.GetDirectories(_fmsDir))
{ {
var root = Path.Join(fmsDir, campaign); var name = Path.GetFileName(dir);
var texturePathMap = GetTexturePaths(root); var fmResource = new CampaignResources();
var objectPathMap = GetObjectPaths(root); fmResource.name = name;
var objectTexturePathMap = GetObjectTexturePaths(root); _fmResources.Add(name, fmResource);
var resource = new CampaignResources
{
missionPathMap = missionPathMap,
texturePathMap = texturePathMap,
objectPathMap = objectPathMap,
objectTexturePathMap = objectTexturePathMap,
};
_fmResources.Add(campaign, resource);
_fmsDir = fmsDir;
}
} }
_initialised = true; _initialised = true;
return true;
}
return false;
} }
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;
return true; }
else if (name == "cam_ext.cfg")
{
configPaths[(int)ConfigFile.CamExt] = path;
}
else if (name == "cam_mod.ini")
{
configPaths[(int)ConfigFile.CamMod] = path;
} }
} }
installCfgPath = ""; 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; return false;
} }
private static bool TryGetPath(string dir, string searchPattern, out string path) foreach (var path in Directory.GetFiles(includePath, "*.cfg", searchOptions))
{ {
var searchOptions = new EnumerationOptions var name = Path.GetFileName(path).ToLower();
if (name == $"{gameName}.cfg")
{ {
MatchCasing = MatchCasing.CaseInsensitive, configPaths[(int)ConfigFile.Game] = path;
RecurseSubdirectories = true }
}; else if (name == installCfgName.ToLower())
{
configPaths[(int)ConfigFile.Install] = path;
}
else if (name == userCfgName.ToLower())
{
configPaths[(int)ConfigFile.User] = path;
}
}
var paths = Directory.GetFiles(dir, searchPattern, searchOptions); // Check we found everything
if (paths.Length > 0) var i = 0;
foreach (var path in configPaths)
{ {
path = paths[0]; if (path == null || path == "")
{
return false;
}
i++;
}
return true; return true;
} }
path = ""; 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 false; return false;
} }
} }

View File

@ -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);
}
} }

View File

@ -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;

View File

@ -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);
} }

View File

@ -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

View File

@ -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)
{ {

View File

@ -54,11 +54,9 @@ 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);
} }

View File

@ -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();