Add more thorough path manager

This commit is contained in:
Jarrod Doyle 2024-08-24 17:09:27 +01:00
parent c48eb018f5
commit 62ff5306fe
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
1 changed files with 360 additions and 0 deletions

View File

@ -0,0 +1,360 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace KeepersCompound.LGS;
// TODO: Support FMs resource paths!
public class ResourcePathManager
{
private record CampaignResources
{
public Dictionary<string, string> missionPathMap;
public Dictionary<string, string> texturePathMap;
public Dictionary<string, string> objectPathMap;
}
private bool _initialised = false;
private readonly string _extractionPath;
private CampaignResources _omResources;
private Dictionary<string, CampaignResources> _fmResources;
public ResourcePathManager(string extractionPath)
{
_extractionPath = extractionPath;
}
public bool 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);
var objExtractPath = Path.Join(_extractionPath, "obj");
if (Directory.Exists(objExtractPath))
{
Directory.Delete(objExtractPath, true);
}
ZipFile.OpenRead(objPath).ExtractToDirectory(objExtractPath);
var objectPathMap = GetObjectPaths(_extractionPath);
_omResources = new CampaignResources
{
missionPathMap = omsMap,
texturePathMap = texturePathMap,
objectPathMap = objectPathMap,
};
}
{
_fmResources = new Dictionary<string, CampaignResources>();
foreach (var (campaign, missionPathMap) in fmsMap)
{
var texturePathMap = GetTexturePaths(Path.Join(fmsDir, campaign));
var objectPathMap = GetObjectPaths(Path.Join(fmsDir, campaign));
var resource = new CampaignResources
{
missionPathMap = missionPathMap,
texturePathMap = texturePathMap,
objectPathMap = objectPathMap,
};
_fmResources.Add(campaign, resource);
}
}
_initialised = true;
return true;
}
return false;
}
public string GetMissionPath(string missionName)
{
if (!_initialised) return null;
if (_omResources.missionPathMap.TryGetValue(missionName, out var path))
{
return path;
}
return null;
}
public string GetMissionPath(string campaignName, string missionName)
{
if (!_initialised) return null;
if (_fmResources.TryGetValue(campaignName, out var campaign) &&
campaign.missionPathMap.TryGetValue(missionName, out var path))
{
return path;
}
return null;
}
public string GetTexturePath(string textureName)
{
if (!_initialised) return null;
textureName = textureName.ToLower();
if (_omResources.texturePathMap.TryGetValue(textureName, out var path))
{
return path;
}
return null;
}
public string GetTexturePath(string campaignName, string textureName)
{
if (!_initialised) return null;
textureName = textureName.ToLower();
if (_fmResources.TryGetValue(campaignName, out var campaign) &&
campaign.texturePathMap.TryGetValue(textureName, out var path))
{
return path;
}
return null;
}
public string GetObjectPath(string objectName)
{
if (!_initialised) return null;
if (_omResources.objectPathMap.TryGetValue(objectName, out var path))
{
return path;
}
return null;
}
public string GetObjectPath(string campaignName, string objectName)
{
if (!_initialised) return null;
if (_fmResources.TryGetValue(campaignName, out var campaign) &&
campaign.objectPathMap.TryGetValue(objectName, out var path))
{
return path;
}
return null;
}
// TODO: Handle object textures?
private static Dictionary<string, string> GetTexturePaths(string root)
{
string[] validExtensions = { ".dds", ".png", ".tga", ".pcx", ".gif", ".bmp", ".cel", };
var famOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var textureOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true,
};
var pathMap = new Dictionary<string, string>();
foreach (var dir in Directory.EnumerateDirectories(root, "fam", famOptions))
{
foreach (var path in Directory.EnumerateFiles(dir, "*", textureOptions))
{
var ext = Path.GetExtension(path);
if (validExtensions.Contains(ext.ToLower()))
{
var key = Path.GetRelativePath(root, path)[..^ext.Length].ToLower();
pathMap.TryAdd(key, path);
}
}
}
return pathMap;
}
private static Dictionary<string, string> GetObjectPaths(string root)
{
var dirOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var binOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true,
};
var pathMap = new Dictionary<string, string>();
foreach (var dir in Directory.EnumerateDirectories(root, "obj", dirOptions))
{
foreach (var path in Directory.EnumerateFiles(dir, "*.bin", binOptions))
{
var key = Path.GetRelativePath(dir, path).ToLower();
pathMap.TryAdd(key, path);
}
}
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: Do we only need to replace on systems with a different path separator?
var path = line.Split(" ")[1].Replace("\\", "/");
omsPath = Path.GetFullPath(root + path);
break;
}
}
if (omsPath == "")
{
return false;
}
var searchOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
};
foreach (var path in Directory.GetFiles(root, "*.mis", searchOptions))
{
var baseName = Path.GetFileName(path).ToLower();
map.Add(baseName, path);
}
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 = line.Split(" ")[1].Replace("\\", "/");
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))
{
if (extensions.Contains(Path.GetExtension(path).ToLower()))
{
var baseName = Path.GetFileName(path).ToLower();
campaignMap.Add(baseName, path);
}
}
if (campaignMap.Count != 0)
{
fmsMap.Add(Path.GetFileName(dir), campaignMap);
}
}
return true;
}
private static bool DirContainsThiefExe(string dir)
{
var searchOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
};
foreach (var path in Directory.GetFiles(dir, "*.exe", searchOptions))
{
var baseName = Path.GetFileName(path).ToLower();
if (baseName.Contains("thief"))
{
return true;
}
}
return false;
}
private static bool TryGetInstallCfgPath(string dir, out string installCfgPath)
{
var searchOptions = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true
};
foreach (var path in Directory.GetFiles(dir, "*.cfg", searchOptions))
{
var baseName = Path.GetFileName(path).ToLower();
if (baseName == "install.cfg" || baseName == "darkinst.cfg")
{
installCfgPath = path;
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;
}
}