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;
// 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
{
Mission,
@ -16,21 +32,23 @@ public enum ResourceType
public class ResourcePathManager
{
private record CampaignResources
public record CampaignResources
{
public Dictionary<string, string> missionPathMap;
public Dictionary<string, string> texturePathMap;
public Dictionary<string, string> objectPathMap;
public Dictionary<string, string> objectTexturePathMap;
public bool initialised = false;
public string name;
private readonly Dictionary<string, string> _missionPathMap = [];
private readonly Dictionary<string, string> _texturePathMap = [];
private readonly Dictionary<string, string> _objectPathMap = [];
private readonly Dictionary<string, string> _objectTexturePathMap = [];
public List<string> GetResourceNames(ResourceType type)
{
List<string> keys = type switch
{
ResourceType.Mission => [.. missionPathMap.Keys],
ResourceType.Object => [.. objectPathMap.Keys],
ResourceType.ObjectTexture => [.. objectTexturePathMap.Keys],
ResourceType.Texture => [.. texturePathMap.Keys],
ResourceType.Mission => [.. _missionPathMap.Keys],
ResourceType.Object => [.. _objectPathMap.Keys],
ResourceType.ObjectTexture => [.. _objectTexturePathMap.Keys],
ResourceType.Texture => [.. _texturePathMap.Keys],
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
keys.Sort();
@ -41,13 +59,58 @@ public class ResourcePathManager
{
var map = type switch
{
ResourceType.Mission => missionPathMap,
ResourceType.Object => objectPathMap,
ResourceType.ObjectTexture => objectTexturePathMap,
ResourceType.Texture => texturePathMap,
ResourceType.Mission => _missionPathMap,
ResourceType.Object => _objectPathMap,
ResourceType.ObjectTexture => _objectTexturePathMap,
ResourceType.Texture => _texturePathMap,
_ => 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('\\', '/');
}
public bool Init(string installPath)
public void Init(string installPath)
{
// TODO: This can be done less awkwardly with the resource paths
if (DirContainsThiefExe(installPath) &&
TryGetInstallCfgPath(installPath, out var installCfgPath) &&
TryBuildOmsPathMap(installPath, installCfgPath, out var omsMap) &&
TryBuildFmsPathMap(installPath, out var fmsMap, out var fmsDir) &&
TryGetPath(installPath, "fam.crf", out var famPath) &&
TryGetPath(installPath, "obj.crf", out var objPath))
{
// 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);
// TODO:
// - Determine if folder is a thief install
// - Load all the (relevant) config files
// - Get base paths from configs
// - Build list of FM campaigns
// - Initialise OM campaign resource paths
// - Lazy load FM campaign resource paths (that inherit OM resources)
var objExtractPath = Path.Join(_extractionPath, "obj");
if (Directory.Exists(objExtractPath))
if (!DirContainsThiefExe(installPath))
{
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,
};
throw new ArgumentException($"No Thief installation found at {installPath}", nameof(installPath));
}
// 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 (campaign, missionPathMap) in fmsMap)
foreach (var dir in Directory.GetDirectories(_fmsDir))
{
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;
}
var name = Path.GetFileName(dir);
var fmResource = new CampaignResources();
fmResource.name = name;
_fmResources.Add(name, fmResource);
}
_initialised = true;
return true;
}
return false;
}
public List<string> GetCampaignNames()
@ -142,60 +218,25 @@ public class ResourcePathManager
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 == "")
{
return _omResources.GetResourceNames(type);
return _omResources;
}
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));
}
// 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)
{
string[] validExtensions = { ".dds", ".png", ".tga", ".pcx", ".gif", ".bmp", ".cel", };
@ -277,100 +318,11 @@ public class ResourcePathManager
return pathMap;
}
private static bool TryBuildOmsPathMap(string root, string cfgPath, out Dictionary<string, string> map)
{
map = new Dictionary<string, string>();
var omsPath = "";
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;
}
/// <summary>
/// Determine if the given directory contains a Thief executable at the top level.
/// </summary>
/// <param name="dir">The directory to search</param>
/// <returns><c>true</c> if a Thief executable was found, <c>false</c> otherwise.</returns>
private static bool DirContainsThiefExe(string dir)
{
var searchOptions = new EnumerationOptions
@ -390,44 +342,108 @@ public class ResourcePathManager
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
{
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();
if (baseName == "install.cfg" || baseName == "darkinst.cfg")
var name = Path.GetFileName(path).ToLower();
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;
}
}
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;
}
}

View File

@ -9,15 +9,20 @@ public partial class Context : Node
public ResourcePathManager PathManager { 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()
{
var extractPath = ProjectSettings.GlobalizePath($"user://extracted/tmp");
PathManager = new ResourcePathManager(extractPath);
ModelLoader = new ModelLoader();
CampaignName = "";
CampaignResources = PathManager.GetCampaign("");
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;
resourceSelector.ResourceSelected += (string campaign, string mission) =>
{
var context = Context.Instance;
context.CampaignName = campaign;
_missionName = mission;
var (newCampaign, missionPath) = context.PathManager.GetResourcePath(ResourceType.Mission, campaign, mission);
if (newCampaign != campaign)
{
GD.Print($"Didn't find campaign mission: ({campaign}, {mission})");
}
FileName = missionPath;
var context = Context.Instance;
context.SetCampaign(campaign);
FileName = context.CampaignResources.GetResourcePath(ResourceType.Mission, mission);
Build = true;
};
}
@ -120,7 +116,7 @@ public partial class Mission : Node3D
{
ClearMap();
_textureLoader = new TextureLoader(Context.Instance.CampaignName);
_textureLoader = new TextureLoader();
Timing.TimeStage("DbFile Parse", () => _file = new(FileName));
Timing.TimeStage("Register Textures", () => UseChunk<TxList>("TXLIST", RegisterTextures));
Timing.TimeStage("Build WR", () => UseChunk<WorldRep>("WREXT", BuildWrMeshes));
@ -285,19 +281,18 @@ public partial class Mission : Node3D
path = prop.value;
var pathManager = Context.Instance.PathManager;
var campaign = Context.Instance.CampaignName;
var campaignResources = Context.Instance.CampaignResources;
if (path.StartsWith("fam", StringComparison.OrdinalIgnoreCase))
{
var resType = ResourceType.Texture;
path = pathManager.GetResourcePath(resType, campaign, prop.value).Item2;
path = campaignResources.GetResourcePath(resType, prop.value);
}
else
{
var resType = ResourceType.ObjectTexture;
var convertedValue = ResourcePathManager.ConvertSeparator(prop.value);
var resName = Path.GetFileNameWithoutExtension(convertedValue);
path = pathManager.GetResourcePath(resType, campaign, resName).Item2;
path = campaignResources.GetResourcePath(resType, resName);
}
return path != null;

View File

@ -18,7 +18,7 @@ public partial class Model : Node3D
node.QueueFree();
}
Context.Instance.CampaignName = campaignName;
Context.Instance.SetCampaign(campaignName);
var model = Context.Instance.ModelLoader.Load(modelPath);
AddChild(model);
}

View File

@ -5,13 +5,15 @@ using KeepersCompound.LGS;
namespace KeepersCompound.TMV;
// TODO: Work out a way to share base game models again in the cache
public class ModelLoader
{
private readonly Dictionary<(string, string), MeshInstance3D> _cache = new();
public MeshInstance3D Load(string modelName, bool forceLoad = false)
{
var campaignName = Context.Instance.CampaignName;
var campaignResources = Context.Instance.CampaignResources;
var campaignName = campaignResources.name;
campaignName ??= "";
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
var model = Timing.TimeStage("Load Models", () => { return LoadModel(ref campaignName, modelName); });
var model = Timing.TimeStage("Load Models", () => { return LoadModel(modelName); });
_cache[(campaignName, modelName)] = model;
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 (newCampaignName, modelPath) = pathManager.GetResourcePath(ResourceType.Object, campaignName, modelName);
campaignName = newCampaignName;
var campaignResources = Context.Instance.CampaignResources;
var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName);
if (modelPath == null)
{
return null;
}
var modelFile = new ModelFile(modelPath);
if (modelFile == null)
{
@ -55,7 +57,7 @@ public class ModelLoader
{
var convertedName = ResourcePathManager.ConvertSeparator(material.Name);
var resName = Path.GetFileNameWithoutExtension(convertedName);
var (_, path) = pathManager.GetResourcePath(ResourceType.ObjectTexture, campaignName, resName);
var path = campaignResources.GetResourcePath(ResourceType.ObjectTexture, resName);
if (path == null)
{
// Might fail in exported projects

View File

@ -7,15 +7,13 @@ namespace KeepersCompound.TMV;
public partial class TextureLoader
{
private readonly string _fmName;
private readonly List<Texture2D> _textureCache = new();
private readonly Dictionary<int, int> _idMap = new();
private readonly Dictionary<int, string> _idPathMap = new();
private readonly Dictionary<string, int> _pathMap = new();
public TextureLoader(string fmName)
public TextureLoader()
{
_fmName = fmName;
LoadDefaultTextures();
}
@ -35,8 +33,8 @@ public partial class TextureLoader
private bool Load(int id, string path)
{
var loaded = false;
var pathManager = Context.Instance.PathManager;
var (_, texPath) = pathManager.GetResourcePath(ResourceType.Texture, _fmName, path);
var campaignResources = Context.Instance.CampaignResources;
var texPath = campaignResources.GetResourcePath(ResourceType.Texture, path);
if (texPath != null)
{

View File

@ -54,11 +54,9 @@ public partial class ResourceSelector : Control
private void SetInstallPath(string path)
{
_FolderPath.Text = path;
if (Context.Instance.PathManager.Init(path))
{
Context.Instance.PathManager.Init(path);
BuildCampaignList();
}
}
private void BuildCampaignList()
{
@ -79,10 +77,11 @@ public partial class ResourceSelector : Control
_Resources.Clear();
_LoadButton.Disabled = true;
var pathManager = Context.Instance.PathManager;
var campaignName = idx == 0 ? null : _Campaigns.GetItemText((int)idx);
var resourceNammes = pathManager.GetResourceNames(ResType, campaignName);
foreach (var resource in resourceNammes)
var pathManager = Context.Instance.PathManager;
var campaignResources = pathManager.GetCampaign(campaignName);
var resourceNames = campaignResources.GetResourceNames(ResType);
foreach (var resource in resourceNames)
{
_Resources.AddItem(resource);
}

View File

@ -37,7 +37,7 @@ public partial class TextureBrowser : Node
{
// !HACK TEMP
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");
_searchBar = GetNode<LineEdit>("%SearchBar");
@ -72,8 +72,8 @@ public partial class TextureBrowser : Node
{
_folderTree.Clear();
var context = Context.Instance;
var textureNames = context.PathManager.GetResourceNames(LGS.ResourceType.Texture, context.CampaignName);
var campaignResources = Context.Instance.CampaignResources;
var textureNames = campaignResources.GetResourceNames(LGS.ResourceType.Texture);
var treeItems = new Dictionary<string, TreeItem>();
treeItems.Add("", _folderTree.CreateItem());
@ -102,13 +102,12 @@ public partial class TextureBrowser : Node
child.QueueFree();
}
var pathManager = Context.Instance.PathManager;
var campaign = Context.Instance.CampaignName;
var campaignResources = Context.Instance.CampaignResources;
var resType = LGS.ResourceType.Texture;
var textureNames = pathManager.GetResourceNames(resType, campaign);
var textureNames = campaignResources.GetResourceNames(resType);
foreach (var name in textureNames)
{
var (_, path) = pathManager.GetResourcePath(resType, campaign, name);
var path = campaignResources.GetResourcePath(resType, name);
var texture = TextureLoader.LoadTexture(path);
var textureRect = new TextureRect();