Additional logging around initialisation

This commit is contained in:
Jarrod Doyle 2025-01-13 19:11:06 +00:00
parent 0136dc1141
commit 9bf80b1b5f
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
4 changed files with 146 additions and 53 deletions

View File

@ -1,5 +1,6 @@
using System.Text; using System.Text;
using KeepersCompound.LGS.Database.Chunks; using KeepersCompound.LGS.Database.Chunks;
using Serilog;
namespace KeepersCompound.LGS.Database; namespace KeepersCompound.LGS.Database;
@ -84,8 +85,13 @@ public class DbFile
public DbFile(string filename) public DbFile(string filename)
{ {
// TODO: Throw rather than return Log.Information("Loading DbFile: {Path}", filename);
if (!File.Exists(filename)) return;
if (!File.Exists(filename))
{
Log.Error("Failed to load DbFile. File does not exist.");
throw new FileNotFoundException();
}
using MemoryStream stream = new(File.ReadAllBytes(filename)); using MemoryStream stream = new(File.ReadAllBytes(filename));
using BinaryReader reader = new(stream, Encoding.UTF8, false); using BinaryReader reader = new(stream, Encoding.UTF8, false);
@ -105,6 +111,8 @@ public class DbFile
public void Save(string filename) public void Save(string filename)
{ {
Log.Information("Saving DbFile: {Path}", filename);
using var stream = File.Open(filename, FileMode.Create); using var stream = File.Open(filename, FileMode.Create);
using var writer = new BinaryWriter(stream, Encoding.UTF8, false); using var writer = new BinaryWriter(stream, Encoding.UTF8, false);

View File

@ -1,4 +1,5 @@
using System.IO.Compression; using System.IO.Compression;
using Serilog;
namespace KeepersCompound.LGS; namespace KeepersCompound.LGS;
@ -77,33 +78,41 @@ public class ResourcePathManager
} }
} }
foreach (var (name, path) in GetTexturePaths(resPath)) var texPaths = GetTexturePaths(resPath);
var objPaths = GetObjectPaths(resPath);
var objTexPaths = GetObjectTexturePaths(resPath);
Log.Information("Found {TexCount} textures, {ObjCount} objects, {ObjTexCount} object textures for campaign: {CampaignName}",
texPaths.Count, objPaths.Count, objTexPaths.Count, name);
foreach (var (resName, path) in texPaths)
{ {
_texturePathMap[name] = path; _texturePathMap[resName] = path;
} }
foreach (var (name, path) in GetObjectPaths(resPath)) foreach (var (resName, path) in objPaths)
{ {
_objectPathMap[name] = path; _objectPathMap[resName] = path;
} }
foreach (var (name, path) in GetObjectTexturePaths(resPath)) foreach (var (resName, path) in objTexPaths)
{ {
_objectTexturePathMap[name] = path; _objectTexturePathMap[resName] = path;
} }
initialised = true;
} }
public void Initialise(string misPath, string resPath, CampaignResources parent) public void Initialise(string misPath, string resPath, CampaignResources parent)
{ {
foreach (var (name, path) in parent._texturePathMap) foreach (var (resName, path) in parent._texturePathMap)
{ {
_texturePathMap[name] = path; _texturePathMap[resName] = path;
} }
foreach (var (name, path) in parent._objectPathMap) foreach (var (resName, path) in parent._objectPathMap)
{ {
_objectPathMap[name] = path; _objectPathMap[resName] = path;
} }
foreach (var (name, path) in parent._objectTexturePathMap) foreach (var (resName, path) in parent._objectTexturePathMap)
{ {
_objectTexturePathMap[name] = path; _objectTexturePathMap[resName] = path;
} }
Initialise(misPath, resPath); Initialise(misPath, resPath);
@ -126,7 +135,7 @@ public class ResourcePathManager
return path.Replace('\\', '/'); return path.Replace('\\', '/');
} }
public void Init(string installPath) public bool TryInit(string installPath)
{ {
// TODO: // TODO:
// - Determine if folder is a thief install // - Determine if folder is a thief install
@ -136,22 +145,25 @@ public class ResourcePathManager
// - Initialise OM campaign resource paths // - Initialise OM campaign resource paths
// - Lazy load FM campaign resource paths (that inherit OM resources) // - Lazy load FM campaign resource paths (that inherit OM resources)
Log.Information("Initialising path manager");
if (!DirContainsThiefExe(installPath)) if (!DirContainsThiefExe(installPath))
{ {
throw new ArgumentException($"No Thief installation found at {installPath}", nameof(installPath)); return false;
} }
// TODO: Should these paths be stored? // TODO: Should these paths be stored?
if (!TryGetConfigPaths(installPath, out var configPaths)) if (!TryGetConfigPaths(installPath, out var configPaths))
{ {
throw new InvalidOperationException("Failed to find all installation config paths."); return false;
} }
// We need to know where all the texture and object resources are // We need to know where all the texture and object resources are
var installCfgLines = File.ReadAllLines(configPaths[(int)ConfigFile.Install]); var installCfgLines = File.ReadAllLines(configPaths[(int)ConfigFile.Install]);
if (!FindConfigVar(installCfgLines, "resname_base", out var resPaths)) if (!FindConfigVar(installCfgLines, "resname_base", out var resPaths))
{ {
throw new InvalidOperationException("Failed to find resnames in install config"); Log.Error("Failed to find `resname_base` in install config");
return false;
} }
var zipPaths = new List<string>(); var zipPaths = new List<string>();
@ -160,6 +172,7 @@ public class ResourcePathManager
var dir = Path.Join(installPath, ConvertSeparator(resPath)); var dir = Path.Join(installPath, ConvertSeparator(resPath));
if (!Directory.Exists(dir)) if (!Directory.Exists(dir))
{ {
Log.Warning("Install config references non-existent `resname_base`: {Path}", dir);
continue; continue;
} }
@ -183,7 +196,12 @@ public class ResourcePathManager
ZipFile.OpenRead(zipPath).ExtractToDirectory(extractPath, false); ZipFile.OpenRead(zipPath).ExtractToDirectory(extractPath, false);
} }
FindConfigVar(installCfgLines, "load_path", out var omsPath); if (!FindConfigVar(installCfgLines, "load_path", out var omsPath))
{
Log.Error("Failed to find `load_path` in install config");
return false;
}
omsPath = Path.Join(installPath, ConvertSeparator(omsPath)); omsPath = Path.Join(installPath, ConvertSeparator(omsPath));
_omResources = new CampaignResources(); _omResources = new CampaignResources();
_omResources.name = ""; _omResources.name = "";
@ -192,6 +210,7 @@ public class ResourcePathManager
var camModLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamMod]); var camModLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamMod]);
FindConfigVar(camModLines, "fm_path", out var fmsPath, "FMs"); FindConfigVar(camModLines, "fm_path", out var fmsPath, "FMs");
_fmsDir = Path.Join(installPath, fmsPath); _fmsDir = Path.Join(installPath, fmsPath);
Log.Information("Searching for FMS at: {FmsPath}", _fmsDir);
// Build up the map of FM campaigns. These are uninitialised, we just want // Build up the map of FM campaigns. These are uninitialised, we just want
// to have their name // to have their name
@ -205,6 +224,7 @@ public class ResourcePathManager
} }
_initialised = true; _initialised = true;
return true;
} }
public List<string> GetCampaignNames() public List<string> GetCampaignNames()
@ -222,17 +242,19 @@ public class ResourcePathManager
{ {
return _omResources; return _omResources;
} }
else if (_fmResources.TryGetValue(campaignName, out var campaign))
if (!_fmResources.TryGetValue(campaignName, out var campaign))
{ {
if (!campaign.initialised) Log.Error("Failed to find campaign: {CampaignName}", campaignName);
{ throw new ArgumentException("No campaign found with given name", nameof(campaignName));
var fmPath = Path.Join(_fmsDir, campaignName);
campaign.Initialise(fmPath, fmPath, _omResources);
}
return campaign;
} }
throw new ArgumentException("No campaign found with given name", nameof(campaignName)); if (!campaign.initialised)
{
var fmPath = Path.Join(_fmsDir, campaignName);
campaign.Initialise(fmPath, fmPath, _omResources);
}
return campaign;
} }
private static Dictionary<string, string> GetObjectTexturePaths(string root) private static Dictionary<string, string> GetObjectTexturePaths(string root)
@ -337,11 +359,12 @@ public class ResourcePathManager
} }
} }
Log.Error("No Thief executable found in {InstallPath}. Is this the right directory?", dir);
return false; return false;
} }
/// <summary> /// <summary>
/// Get an array of of all the Dark config file paths. /// Get an array of all the Dark config file paths.
/// </summary> /// </summary>
/// <param name="installPath">Root directory of the Thief installation.</param> /// <param name="installPath">Root directory of the Thief installation.</param>
/// <param name="configPaths">Output array of config file paths</param> /// <param name="configPaths">Output array of config file paths</param>
@ -362,20 +385,21 @@ public class ResourcePathManager
foreach (var path in Directory.GetFiles(installPath, "cam*", searchOptions)) foreach (var path in Directory.GetFiles(installPath, "cam*", searchOptions))
{ {
var name = Path.GetFileName(path).ToLower(); var name = Path.GetFileName(path).ToLower();
if (name == "cam.cfg") switch (name)
{ {
configPaths[(int)ConfigFile.Cam] = path; case "cam.cfg":
} configPaths[(int)ConfigFile.Cam] = path;
else if (name == "cam_ext.cfg") break;
{ case "cam_ext.cfg":
configPaths[(int)ConfigFile.CamExt] = path; configPaths[(int)ConfigFile.CamExt] = path;
} break;
else if (name == "cam_mod.ini") case "cam_mod.ini":
{ configPaths[(int)ConfigFile.CamMod] = path;
configPaths[(int)ConfigFile.CamMod] = path; break;
} }
} }
// TODO: Verify we found cam/cam_ext/cam_mod
var camExtLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamExt]); var camExtLines = File.ReadAllLines(configPaths[(int)ConfigFile.CamExt]);
var camLines = File.ReadAllLines(configPaths[(int)ConfigFile.Cam]); var camLines = File.ReadAllLines(configPaths[(int)ConfigFile.Cam]);
@ -386,9 +410,24 @@ public class ResourcePathManager
} }
FindCamVar("include_path", out var includePath, "./"); FindCamVar("include_path", out var includePath, "./");
FindCamVar("game", out var gameName);
FindCamVar($"{gameName}_include_install_cfg", out var installCfgName); if (!FindCamVar("game", out var gameName))
FindCamVar("include_user_cfg", out var userCfgName); {
Log.Error("`game` not specified in Cam/CamExt");
return false;
}
if (!FindCamVar($"{gameName}_include_install_cfg", out var installCfgName))
{
Log.Error("Install config file path not specified in Cam/CamExt");
return false;
}
if (!FindCamVar("include_user_cfg", out var userCfgName))
{
Log.Error("User config file path not specified in Cam/CamExt");
return false;
}
// TODO: How to handle case-insensitive absolute paths? // TODO: How to handle case-insensitive absolute paths?
// Fixup the include path to "work" cross-platform // Fixup the include path to "work" cross-platform
@ -396,6 +435,7 @@ public class ResourcePathManager
includePath = Path.Join(installPath, includePath); includePath = Path.Join(installPath, includePath);
if (!Directory.Exists(includePath)) if (!Directory.Exists(includePath))
{ {
Log.Error("Include path specified in Cam/CamExt does not exist: {IncludePath}", includePath);
return false; return false;
} }
@ -417,15 +457,25 @@ public class ResourcePathManager
} }
// Check we found everything // Check we found everything
var i = 0; var found = 0;
foreach (var path in configPaths) for (var i = 0; i < (int)ConfigFile.ConfigFileCount; i++)
{ {
var path = configPaths[i];
if (path == null || path == "") if (path == null || path == "")
{ {
return false; Log.Error("Failed to find {ConfigFile} config file", (ConfigFile)i);
continue;
} }
i++;
found++;
} }
if (found != (int)ConfigFile.ConfigFileCount)
{
Log.Error("Failed to find all required config files in Thief installation directory");
return false;
}
return true; return true;
} }

View File

@ -42,13 +42,20 @@ public class LightMapper
string campaignName, string campaignName,
string missionName) string missionName)
{ {
var pathManager = SetupPathManager(installPath); if (!SetupPathManager(installPath, out var pathManager))
{
Log.Error("Failed to configure path manager");
throw new Exception("Failed to configure path manager");
}
_campaign = pathManager.GetCampaign(campaignName); _campaign = pathManager.GetCampaign(campaignName);
_misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName); _misPath = _campaign.GetResourcePath(ResourceType.Mission, missionName);
_mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath)); _mission = Timing.TimeStage("Parse DB", () => new DbFile(_misPath));
_hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy); _hierarchy = Timing.TimeStage("Build Hierarchy", BuildHierarchy);
_lights = []; _lights = [];
VerifyRequiredChunksExist();
var mesh = Timing.TimeStage("Build Mesh", BuildMesh); var mesh = Timing.TimeStage("Build Mesh", BuildMesh);
_triangleTypeMap = mesh.TriangleSurfaceMap; _triangleTypeMap = mesh.TriangleSurfaceMap;
_scene = Timing.TimeStage("Build RT Scene", () => _scene = Timing.TimeStage("Build RT Scene", () =>
@ -121,12 +128,36 @@ public class LightMapper
Timing.TimeStage("Save DB", () => _mission.Save(savePath)); Timing.TimeStage("Save DB", () => _mission.Save(savePath));
} }
private static ResourcePathManager SetupPathManager(string installPath) private bool VerifyRequiredChunksExist()
{
var requiredChunkNames = new []
{
"RENDPARAMS",
"LM_PARAM",
"WREXT",
"BRLIST",
"P$AnimLight",
};
var allFound = true;
foreach (var name in requiredChunkNames)
{
if (!_mission.Chunks.ContainsKey(name))
{
Log.Warning("Failed to find required chunk: {ChunkName}", name);
allFound = false;
}
}
return allFound;
}
private static bool SetupPathManager(string installPath, out ResourcePathManager pathManager)
{ {
var tmpDir = Directory.CreateTempSubdirectory("KCLightmapper"); var tmpDir = Directory.CreateTempSubdirectory("KCLightmapper");
var resPathManager = new ResourcePathManager(tmpDir.FullName);
resPathManager.Init(installPath); pathManager = new ResourcePathManager(tmpDir.FullName);
return resPathManager; return pathManager.TryInit(installPath);
} }
private ObjectHierarchy BuildHierarchy() private ObjectHierarchy BuildHierarchy()
@ -144,6 +175,8 @@ public class LightMapper
{ {
return new ObjectHierarchy(_mission, new DbFile(paths[0])); return new ObjectHierarchy(_mission, new DbFile(paths[0]));
} }
Log.Warning("Failed to find GameSys");
return new ObjectHierarchy(_mission); return new ObjectHierarchy(_mission);
} }

View File

@ -2,6 +2,7 @@ using System.Numerics;
using KeepersCompound.LGS; using KeepersCompound.LGS;
using KeepersCompound.LGS.Database; using KeepersCompound.LGS.Database;
using KeepersCompound.LGS.Database.Chunks; using KeepersCompound.LGS.Database.Chunks;
using Serilog;
namespace KeepersCompound.Lightmapper; namespace KeepersCompound.Lightmapper;
@ -117,6 +118,7 @@ public class MeshBuilder
var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName); var modelPath = campaignResources.GetResourcePath(ResourceType.Object, modelName);
if (modelPath == null) if (modelPath == null)
{ {
Log.Warning("Failed to find model file: {Path}", modelPath);
continue; continue;
} }