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