public ModelFiles GatherAllFilesInSubdirs() { List <IFile> bmdFiles = new(); List <IFile> bcxFiles = new(); List <IFile> btiFiles = new(); var queue = new FinQueue <ModelFileTreeNode>(this); while (queue.TryDequeue(out var node)) { var impl = node.Impl.Impl; bmdFiles.AddRange(impl.BmdFiles); bcxFiles.AddRange(impl.BcxFiles); btiFiles.AddRange(impl.BtiFiles); queue.Enqueue(node.Children); } FileComparer fileComparer = new(); bmdFiles.Sort(fileComparer); bcxFiles.Sort(fileComparer); btiFiles.Sort(fileComparer); return(new ModelFiles(this.directory_.Name, bmdFiles, bcxFiles, btiFiles)); }
public IModelDirectory <IHaloWarsModelFileBundle>?GatherModelFileBundles( bool assert) { var haloWarsSteamDirectory = SteamUtils.GetGameDirectory("HaloWarsDE", assert); if (haloWarsSteamDirectory == null) { return(null); } var scratchDirectory = DirectoryConstants.ROMS_DIRECTORY .GetSubdir("halo_wars", true); var context = new HWContext(haloWarsSteamDirectory.FullName, scratchDirectory.FullName); // Expand all compressed/encrypted game files. This also handles the .xmb -> .xml conversion context.ExpandAllEraFiles(); var fileHierarchy = new FileHierarchy(scratchDirectory); var rootNode = new ModelDirectory <IHaloWarsModelFileBundle>("halo_wars"); var mapDirectories = fileHierarchy.Root .TryToGetSubdir("scenario/skirmish/design") .Subdirs; foreach (var srcMapDirectory in mapDirectories) { var xtdFile = srcMapDirectory.FilesWithExtension(".xtd").Single(); var xttFile = srcMapDirectory.FilesWithExtension(".xtt").Single(); rootNode.AddFileBundleRelative( new XtdModelFileBundle(xtdFile, xttFile, context)); } var artDirectory = fileHierarchy.Root.TryToGetSubdir("art"); var artSubdirQueue = new FinQueue <IFileHierarchyDirectory>(artDirectory); // TODO: Switch to DFS instead, it's more intuitive as a user while (artSubdirQueue.TryDequeue(out var artSubdir)) { // TODO: Skip a file if it's already been extracted // TODO: Parse UGX files instead, as long as they specify their own animations var visFiles = artSubdir.FilesWithExtension(".vis"); foreach (var visFile in visFiles) { rootNode.AddFileBundleRelative(new VisModelFileBundle(visFile, context)); } artSubdirQueue.Enqueue(artSubdir.Subdirs); } return(rootNode); }
public bool HasOnlyOneBmdInSubdirs() { var queue = new FinQueue <ModelFileTreeNode>(this); var total = 0; while (queue.TryDequeue(out var node)) { total += node.Impl.Impl.BmdFiles.Count(); if (total > 1) { return(false); } queue.Enqueue(node.Children); } return(total == 1); }
public static IList <ModelFiles> GatherModels(IDirectory rootDir) { List <ModelFiles> models = new(); var rootNode = new ModelFileTreeNode(rootDir); var queue = new FinQueue <ModelFileTreeNode>(rootNode); while (queue.TryDequeue(out var node)) { if (node.HasOnlyOneBmdInSubdirs()) { models.Add(node.GatherAllFilesInSubdirs()); } else { queue.Enqueue(node.Children); } } models.Sort(new ModelFilesComparer()); return(models); }
public void Run(string scratchDirectoryPath, string outputDirectoryPath) { string gameDirectory = null; if (OperatingSystem.IsWindows()) { gameDirectory = SteamInterop.GetGameInstallDirectory("HaloWarsDE"); Console.WriteLine( $"Found Halo Wars Definitive Edition install at {gameDirectory}"); } // Point the framework to the game install and working directories var context = new HWContext(gameDirectory, scratchDirectoryPath); // Expand all compressed/encrypted game files. This also handles the .xmb -> .xml conversion context.ExpandAllEraFiles(); var scratchDirectory = new FinDirectory(scratchDirectoryPath); var mapDirectories = scratchDirectory .GetSubdir("scenario/skirmish/design") .GetExistingSubdirs(); var outputDirectory = new FinDirectory(outputDirectoryPath); var baseDstMapDirectory = outputDirectory.GetSubdir("scenario/skirmish/design", true); foreach (var srcMapDirectory in mapDirectories) { var mapName = srcMapDirectory.Name; var dstMapDirectory = baseDstMapDirectory.GetSubdir(mapName, true); var gltfFile = new FinFile( Path.Combine(dstMapDirectory.FullName, $"{mapName}.gltf")); if (gltfFile.Exists) { continue; } var xttFile = srcMapDirectory.GetExistingFiles() .Single(file => file.Extension == ".xtt"); var xtdFile = srcMapDirectory.GetExistingFiles() .Single(file => file.Extension == ".xtd"); var xtt = HWXttResource.FromFile(context, xttFile.FullName); var xtd = HWXtdResource.FromFile(context, xtdFile.FullName); var finModel = xtd.Mesh; var xttMaterial = finModel.MaterialManager.AddStandardMaterial(); xttMaterial.DiffuseTexture = finModel.MaterialManager.CreateTexture( xtt.AlbedoTexture); xttMaterial.DiffuseTexture.Name = $"{mapName}_albedo"; xttMaterial.AmbientOcclusionTexture = finModel.MaterialManager.CreateTexture( xtd.AmbientOcclusionTexture); xttMaterial.AmbientOcclusionTexture.Name = $"{mapName}_ao"; foreach (var primitive in finModel.Skin.Meshes[0].Primitives) { primitive.SetMaterial(xttMaterial); } var exporter = new AssimpIndirectExporter { LowLevel = true }; exporter.Export(gltfFile.CloneWithExtension(".fbx"), finModel); // Cleans up any remaining .bin files. var binFiles = dstMapDirectory.GetExistingFiles() .Where(file => file.Extension == ".bin"); foreach (var binFile in binFiles) { binFile.Info.Delete(); } // Forces an immediate garbage-collection cleanup. This is required to // prevent OOM errors, since Halo Wars maps are just so huge. GC.Collect(); GC.WaitForFullGCComplete(); GC.WaitForPendingFinalizers(); } var artDirectory = scratchDirectory.GetSubdir("art"); var artSubdirQueue = new FinQueue <IDirectory>(artDirectory); // TODO: Switch to DFS instead, it's more intuitive as a user while (artSubdirQueue.TryDequeue(out var artSubdir)) { // TODO: Skip a file if it's already been extracted // TODO: Parse UGX files instead, as long as they specify their own animations var visFiles = artSubdir.GetExistingFiles() .Where(f => f.Extension == ".vis") .ToList(); foreach (var visFile in visFiles) { var vis = HWVisResource.FromFile(context, visFile.FullName); var finModel = vis.Model; var outFilePath = visFile.FullName.Replace(scratchDirectoryPath, outputDirectoryPath); var outFile = new FinFile(outFilePath).CloneWithExtension(".fbx"); outFile.GetParent().Create(); var exporter = new AssimpIndirectExporter(); exporter.Export(outFile, finModel); Console.WriteLine($"Processed {visFile.FullName}"); } artSubdirQueue.Enqueue(artSubdir.GetExistingSubdirs()); } /*var gls = HWGlsResource.FromFile(context, * "scenario\\skirmish\\design\\blood_gulch\\blood_gulch.gls"); * Console.WriteLine($"Processed {gls}"); * * var scn = HWScnResource.FromFile(context, * "scenario\\skirmish\\design\\blood_gulch\\blood_gulch.scn"); * PrintScenarioObjects(scn); * Console.WriteLine($"Processed {scn}"); * * var sc2 = HWSc2Resource.FromFile(context, * "scenario\\skirmish\\design\\blood_gulch\\blood_gulch.sc2"); * PrintScenarioObjects(sc2); * Console.WriteLine($"Processed {sc2}"); * * var sc3 = HWSc3Resource.FromFile(context, * "scenario\\skirmish\\design\\blood_gulch\\blood_gulch.sc3"); * PrintScenarioObjects(sc3); * Console.WriteLine($"Processed {sc3}"); * }*/ }