Add more thorough path manager
This commit is contained in:
parent
c48eb018f5
commit
62ff5306fe
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue