From 1c42f389e4d661f7c77ffb2ad527eb6997a711b0 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 11:43:36 -0800 Subject: [PATCH 01/11] Change code flagged as suspicious by Avast in Bar, DoctChanger, Rbin, and SpawnScript projets --- OpenKh.Command.Bar/Program.cs | 12 +++++++- OpenKh.Command.DoctChanger/Program.cs | 20 +++++++++---- OpenKh.Command.Rbin/Program.cs | 23 +++++++++----- OpenKh.Command.SpawnScript/Program.cs | 43 +++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/OpenKh.Command.Bar/Program.cs b/OpenKh.Command.Bar/Program.cs index 72ab48571..b96ac530f 100644 --- a/OpenKh.Command.Bar/Program.cs +++ b/OpenKh.Command.Bar/Program.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; namespace OpenKh.Command.Bar { @@ -92,7 +93,16 @@ protected int OnExecute(CommandLineApplication app) var binarc = Core.ImportProject(InputProject, out var originalFileName); if (string.IsNullOrEmpty(OutputFile)) - OutputFile = Path.Combine(baseDirectory, originalFileName); + OutputFile = Path.GetFullPath(Path.Combine(baseDirectory, originalFileName)); + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!OutputFile.StartsWith(baseDirectory + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {originalFileName} is outside the output directory"); + } if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) && File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory)) diff --git a/OpenKh.Command.DoctChanger/Program.cs b/OpenKh.Command.DoctChanger/Program.cs index d546ef7ef..893b12362 100644 --- a/OpenKh.Command.DoctChanger/Program.cs +++ b/OpenKh.Command.DoctChanger/Program.cs @@ -1,13 +1,14 @@ using McMaster.Extensions.CommandLineUtils; -using OpenKh.Kh2; +using OpenKh.Command.DoctChanger.Utils; using OpenKh.Common; +using OpenKh.Kh2; +using OpenKh.Kh2.Utils; using System; using System.ComponentModel.DataAnnotations; using System.IO; -using System.Reflection; using System.Linq; -using OpenKh.Kh2.Utils; -using OpenKh.Command.DoctChanger.Utils; +using System.Reflection; +using System.Runtime.InteropServices; namespace OpenKh.Command.DoctChanger { @@ -76,7 +77,16 @@ protected int OnExecute(CommandLineApplication app) { Console.WriteLine(mapIn); - var mapOut = Path.Combine(Output, Path.GetFileName(mapIn)); + var fileName = Path.GetFileName(mapIn); + var mapOut = Path.GetFullPath(Path.Combine(Output, fileName)); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!mapOut.StartsWith(Output + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {fileName} is outside the output directory"); + } var entries = File.OpenRead(mapIn).Using(s => Bar.Read(s)) .Select( diff --git a/OpenKh.Command.Rbin/Program.cs b/OpenKh.Command.Rbin/Program.cs index 0162edbee..b142e983a 100644 --- a/OpenKh.Command.Rbin/Program.cs +++ b/OpenKh.Command.Rbin/Program.cs @@ -1,12 +1,13 @@ -using OpenKh.Common; using McMaster.Extensions.CommandLineUtils; +using OpenKh.Common; +using OpenKh.Common.Exceptions; using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Reflection; -using System.ComponentModel.DataAnnotations; -using OpenKh.Common.Exceptions; -using System.Collections.Generic; +using System.Runtime.InteropServices; namespace OpenKh.Command.Rbin { @@ -203,7 +204,15 @@ private static bool ExtractFile(FileStream stream, Ddd.Rbin.TocEntry tocEntry, s { try { - var outPath = Path.Combine(outputFolder, tocEntry.Name); + var outPath = Path.GetFullPath(Path.Combine(outputFolder, tocEntry.Name)); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!outPath.StartsWith(outputFolder + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {tocEntry.Name} is outside the output directory"); + } stream.Seek(tocEntry.Offset, SeekOrigin.Begin); if (tocEntry.IsCompressed) @@ -212,9 +221,9 @@ private static bool ExtractFile(FileStream stream, Ddd.Rbin.TocEntry tocEntry, s } else { - var writeStream = File.OpenWrite(outPath); + //var writeStream = File.OpenWrite(outPath); + using var writeStream = new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None); writeStream.Write(stream.ReadBytes((int)tocEntry.Size)); - writeStream.Close(); } return true; diff --git a/OpenKh.Command.SpawnScript/Program.cs b/OpenKh.Command.SpawnScript/Program.cs index e04e3bd21..4fffcd41b 100644 --- a/OpenKh.Command.SpawnScript/Program.cs +++ b/OpenKh.Command.SpawnScript/Program.cs @@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; namespace OpenKh.Command.SpawnScript { @@ -57,9 +58,24 @@ private class DecompileCommand protected int OnExecute(CommandLineApplication app) { - OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), $"{Path.GetFileNameWithoutExtension(InputPath)}.txt"); - var spawnScript = File.OpenRead(InputPath).Using(AreaDataScript.Read); - File.WriteAllText(OutputPath, AreaDataScript.Decompile(spawnScript)); + var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.txt"; + OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), OutputName); + if (!String.IsNullOrEmpty(OutputPath)) + { + var spawnScript = File.OpenRead(InputPath).Using(AreaDataScript.Read); + + var fullPath = Path.GetFullPath(OutputPath); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!fullPath.StartsWith(InputPath + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {OutputName} is outside the output directory"); + } + + File.WriteAllText(fullPath, AreaDataScript.Decompile(spawnScript)); + } return 0; } } @@ -76,9 +92,24 @@ private class CompileCommand protected int OnExecute(CommandLineApplication app) { - OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), $"{Path.GetFileNameWithoutExtension(InputPath)}.spawnscript"); - var spawnScript = AreaDataScript.Compile(File.ReadAllText(InputPath)); - File.Create(OutputPath).Using(stream => AreaDataScript.Write(stream, spawnScript)); + var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.spawnscript"; + OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), OutputName); + if (!String.IsNullOrEmpty(OutputPath)) + { + var spawnScript = AreaDataScript.Compile(File.ReadAllText(InputPath)); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + var fullPath = Path.GetFullPath(OutputPath); + + if (!fullPath.StartsWith(InputPath + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {OutputName} is outside the output directory"); + } + using var stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); + AreaDataScript.Write(stream, spawnScript); + } return 0; } } From 4e3108be2fd4d40b9b029cb0c8fcf7b77fd4c897 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 11:49:49 -0800 Subject: [PATCH 02/11] Change code flagged as suspicious by Avast in Kh2MdlxEditor --- .../Views/Main2_Window.xaml.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index 976e57b8a..ed5e8c088 100644 --- a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs +++ b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs @@ -6,6 +6,7 @@ using System; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media.Imaging; @@ -194,7 +195,14 @@ public void exportModel(AssimpGeneric.FileFormat fileFormat = AssimpGeneric.File sfd.ShowDialog(); if (sfd.FileName != "") { - string dirPath = System.IO.Path.GetDirectoryName(sfd.FileName); + + string dirPath; + if (String.IsNullOrEmpty(sfd.FileName)) + { + throw new Exception("No filename provided"); + } + + dirPath = Path.GetDirectoryName(sfd.FileName); if (!Directory.Exists(dirPath)) return; @@ -213,8 +221,10 @@ public void exportTextures(string filePath) ModelTexture.Texture texture = mainVM.TextureFile.Images[i]; BitmapSource bitmapImage = texture.GetBimapSource(); - string fullPath = filePath + "Texture" + i.ToString("D4"); - string finalPath = fullPath; + string fileName = "Texture" + i.ToString("D4"); + string fullPath = filePath + fileName; + string finalPath = Path.GetFullPath(fullPath); + int repeat = 0; while (File.Exists(finalPath)) { @@ -222,7 +232,16 @@ public void exportTextures(string filePath) finalPath = fullPath + " (" + repeat + ")"; } - AssimpGeneric.ExportBitmapSourceAsPng(bitmapImage, fullPath); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!finalPath.StartsWith(filePath + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {fileName} is outside the output directory"); + } + + AssimpGeneric.ExportBitmapSourceAsPng(bitmapImage, finalPath); } } public void reloadModelControl() From eec8bde8b29904573e7113dc9b4bf29000f477f2 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 12:28:10 -0800 Subject: [PATCH 03/11] Change code flagged as suspicious by Avast in Bbsa, PAMtoFBXConverter, and PmpConverter --- OpenKh.Command.Bbsa/Program.cs | 30 ++++++++--- OpenKh.Command.PAMtoFBXConverter/Program.cs | 7 ++- OpenKh.Command.PmpConverter/Program.cs | 55 ++++++++++++--------- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/OpenKh.Command.Bbsa/Program.cs b/OpenKh.Command.Bbsa/Program.cs index 484f5488a..3552e5848 100644 --- a/OpenKh.Command.Bbsa/Program.cs +++ b/OpenKh.Command.Bbsa/Program.cs @@ -6,6 +6,7 @@ using System.Text; using System.Linq; using System.Reflection; +using System.Threading; namespace OpenKh.Command.Bbsa { @@ -140,10 +141,9 @@ private static void ExtractArchives(IEnumerable bbsaFileNames, string ou if (bbsaFileStream == null) continue; - var destinationFileName = Path.Combine(Path.Combine(outputDir, $"{prefix}{file.ArchiveIndex}"), name); - var destinationFolder = Path.GetDirectoryName(destinationFileName); - if (!Directory.Exists(destinationFolder)) - Directory.CreateDirectory(destinationFolder); + var baseDir = Path.Combine(outputDir, $"{prefix}{file.ArchiveIndex}"); + var destinationFileName = MakeSafeDestination(baseDir, name); + Directory.CreateDirectory(Path.GetDirectoryName(destinationFileName)!); streams[0].Position = file.Location + 4; Bbs.Bbsa.CalculateArchiveOffset(bbsa.GetHeader(), file.offset, out var nuind, out var coffs); @@ -178,8 +178,8 @@ private static void ExtractArchives(IEnumerable bbsaFileNames, string ou } - using (var outStream = File.Create(destinationFileName)) - bbsaFileStream.CopyTo(outStream); + using var outStream = File.Create(destinationFileName); + bbsaFileStream.CopyTo(outStream); } if (log) @@ -280,5 +280,23 @@ protected int OnExecute(CommandLineApplication app) private static bool DoesContainBbsa(string path, string prefix) => File.Exists(Path.Combine(path, $"{prefix}{0}.DAT")); + + private static string MakeSafeDestination(string baseDir, string entryName) + { + // Prevent absolute paths + entryName = entryName.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + // Replace invalid filename chars (optional; you can be stricter) + foreach (var c in Path.GetInvalidPathChars()) + entryName = entryName.Replace(c, '_'); + + var dest = Path.GetFullPath(Path.Combine(baseDir, entryName)); + var fullBase = Path.GetFullPath(baseDir) + Path.DirectorySeparatorChar; + + if (!dest.StartsWith(fullBase, StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException($"Unsafe archive path: {entryName}"); + + return dest; + } } } diff --git a/OpenKh.Command.PAMtoFBXConverter/Program.cs b/OpenKh.Command.PAMtoFBXConverter/Program.cs index c739e9fa4..4427fc6e3 100644 --- a/OpenKh.Command.PAMtoFBXConverter/Program.cs +++ b/OpenKh.Command.PAMtoFBXConverter/Program.cs @@ -67,8 +67,13 @@ private static void Convert(string PMO_Path, string PAM_Path) pmoStream.Close(); pamStream.Close(); + + var OutputName = "Test.fbx"; + var OutputPath = "."; + var FullPath = Path.GetFullPath(Path.Combine(OutputPath, OutputName)); + using var ctx = new AssimpContext(); - ctx.ExportFile(nScene, "Test.fbx", "fbx"); + ctx.ExportFile(nScene, FullPath, "fbx"); } diff --git a/OpenKh.Command.PmpConverter/Program.cs b/OpenKh.Command.PmpConverter/Program.cs index b92436346..d73998718 100644 --- a/OpenKh.Command.PmpConverter/Program.cs +++ b/OpenKh.Command.PmpConverter/Program.cs @@ -1,17 +1,17 @@ +using Assimp; +using McMaster.Extensions.CommandLineUtils; +using OpenKh.Bbs; +using OpenKh.Common.Utils; using OpenKh.Engine.MonoGame; using OpenKh.Engine.Parsers; using OpenKh.Imaging; - using System; -using McMaster.Extensions.CommandLineUtils; -using System.IO; -using System.Reflection; -using System.ComponentModel.DataAnnotations; -using OpenKh.Bbs; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; using System.Numerics; -using OpenKh.Common.Utils; -using Assimp; +using System.Reflection; +using System.Runtime.InteropServices; namespace OpenKh.Command.PmpConverter { @@ -67,7 +67,6 @@ private static void Convert(string fileIn, string fileOut) Pmp pmp = MeshGroupList2PMP(p); using Stream stream = File.Create(fileOut); Pmp.Write(stream, pmp); - stream.Close(); } private static Pmp MeshGroupList2PMP(List meshGroup) @@ -118,10 +117,10 @@ private static Pmp MeshGroupList2PMP(List meshGroup) // Set extra flags. if (UsesUniformColor) { - var UniformColor = (uint)(desc.Vertices[0].A / 255f); - UniformColor += (uint)(desc.Vertices[0].B / 255f) << 8; - UniformColor += (uint)(desc.Vertices[0].G / 255f) << 16; - UniformColor += (uint)(desc.Vertices[0].R / 255f) << 24; + var UniformColor = (uint)(desc.Vertices[0].A * 255f); + UniformColor |= (uint)(desc.Vertices[0].B * 255f) << 8; + UniformColor |= (uint)(desc.Vertices[0].G * 255f) << 16; + UniformColor |= (uint)(desc.Vertices[0].R * 255f) << 24; chunk.SectionInfo.VertexFlags = BitsUtil.Int.SetBit(chunk.SectionInfo.VertexFlags, 24, true); chunk.SectionInfo_opt2 = new Pmo.MeshSectionOptional2(); chunk.SectionInfo_opt2.DiffuseColor = UniformColor; @@ -336,12 +335,24 @@ public static List FromFbx(string filePath) foreach (Assimp.Material mat in scene.Materials) { - TexList.Add(Path.GetFileName(mat.TextureDiffuse.FilePath)); - Stream str = File.OpenRead(TexList[TexList.Count - 1]); + var texPath = Path.GetFullPath(mat.TextureDiffuse.FilePath); + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; - PngImage png = new PngImage(str); - Tm2 tmImage = Tm2.Create(png); - TextureData.Add(tmImage); + if (!texPath.StartsWith(baseFilePath + Path.DirectorySeparatorChar, comparison)) + { + throw new Exception($"The file {texPath} is outside the output directory"); + } + + TexList.Add(Path.GetFileName(texPath)); + using (Stream str = new FileStream(texPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + PngImage png = new PngImage(str); + Tm2 tmImage = Tm2.Create(png); + TextureData.Add(tmImage); + } } for(int i = 0; i < scene.RootNode.ChildCount; i++) @@ -364,10 +375,10 @@ public static List FromFbx(string filePath) vertices[k].Z = x.Vertices[k].Z * Scale; vertices[k].Tu = x.TextureCoordinateChannels[0][k].X; vertices[k].Tv = 1.0f - x.TextureCoordinateChannels[0][k].Y; - vertices[k].R = x.VertexColorChannels[0][i].R; - vertices[k].G = x.VertexColorChannels[0][i].G; - vertices[k].B = x.VertexColorChannels[0][i].B; - vertices[k].A = x.VertexColorChannels[0][i].A; + vertices[k].R = x.VertexColorChannels[0][k].R; + vertices[k].G = x.VertexColorChannels[0][k].G; + vertices[k].B = x.VertexColorChannels[0][k].B; + vertices[k].A = x.VertexColorChannels[0][k].A; } meshDescriptor.Vertices = vertices; From 7f3e66a984716464742e202f4a4c70af7a2f2310 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 12:30:29 -0800 Subject: [PATCH 04/11] Remove commented out code from Rbin --- OpenKh.Command.Rbin/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/OpenKh.Command.Rbin/Program.cs b/OpenKh.Command.Rbin/Program.cs index b142e983a..456a2e35f 100644 --- a/OpenKh.Command.Rbin/Program.cs +++ b/OpenKh.Command.Rbin/Program.cs @@ -221,7 +221,6 @@ private static bool ExtractFile(FileStream stream, Ddd.Rbin.TocEntry tocEntry, s } else { - //var writeStream = File.OpenWrite(outPath); using var writeStream = new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None); writeStream.Write(stream.ReadBytes((int)tocEntry.Size)); } From 74baa3a15368eba11fdbffb061c9db73da5863c8 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 13:15:59 -0800 Subject: [PATCH 05/11] Fix bug introduced in Kh2MdlxEditor --- OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index ed5e8c088..9a1f2fa04 100644 --- a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs +++ b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs @@ -236,7 +236,7 @@ public void exportTextures(string filePath) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!finalPath.StartsWith(filePath + Path.DirectorySeparatorChar, comparison)) + if (!finalPath.StartsWith(filePath, comparison)) { throw new Exception($"The file {fileName} is outside the output directory"); } From fdccb9212edc966588562524849f1796eec6dd1a Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 15:38:47 -0800 Subject: [PATCH 06/11] Fixing issues raised by CodeRabbit including several bugs introduced by earlier commits --- OpenKh.Command.Arc/Program.cs | 2 +- OpenKh.Command.Bar/Program.cs | 24 +++++++++++++------ OpenKh.Command.Bbsa/Program.cs | 11 ++++++--- OpenKh.Command.DoctChanger/Program.cs | 2 +- OpenKh.Command.PmpConverter/Program.cs | 9 +++++-- OpenKh.Command.Rbin/Program.cs | 2 +- OpenKh.Command.SpawnScript/Program.cs | 10 ++++---- .../Views/Main2_Window.xaml.cs | 2 +- 8 files changed, 42 insertions(+), 20 deletions(-) diff --git a/OpenKh.Command.Arc/Program.cs b/OpenKh.Command.Arc/Program.cs index 0e48d13da..5c787ac64 100644 --- a/OpenKh.Command.Arc/Program.cs +++ b/OpenKh.Command.Arc/Program.cs @@ -85,7 +85,7 @@ private static void Unpack(string inputFile, string outputDirectory) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!outputPath.StartsWith(outputDirectory + Path.DirectorySeparatorChar, comparison)) + if (!outputPath.StartsWith(Path.GetFullPath(outputRoot + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {entry.Name} is outside the output directory"); } diff --git a/OpenKh.Command.Bar/Program.cs b/OpenKh.Command.Bar/Program.cs index b96ac530f..f800090e1 100644 --- a/OpenKh.Command.Bar/Program.cs +++ b/OpenKh.Command.Bar/Program.cs @@ -92,23 +92,33 @@ protected int OnExecute(CommandLineApplication app) var baseDirectory = Path.GetDirectoryName(InputProject); var binarc = Core.ImportProject(InputProject, out var originalFileName); + var OutputBaseDirectory = baseDirectory; + var OutputFileName = originalFileName; + if (string.IsNullOrEmpty(OutputFile)) - OutputFile = Path.GetFullPath(Path.Combine(baseDirectory, originalFileName)); + { + OutputFile = Path.Combine(OutputBaseDirectory, OutputFileName); + } + + if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) && + File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory)) + { + OutputBaseDirectory = OutputFile; + OutputFile = Path.Combine(OutputBaseDirectory, originalFileName); + } + + var fullPath = Path.GetFullPath(OutputFile); var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!OutputFile.StartsWith(baseDirectory + Path.DirectorySeparatorChar, comparison)) + if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDirectory + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {originalFileName} is outside the output directory"); } - if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) && - File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory)) - OutputFile = Path.Combine(OutputFile, originalFileName); - - using var outputStream = File.Create(OutputFile); + using var outputStream = File.Create(fullPath); Kh2.Bar.Write(outputStream, binarc); foreach (var entry in binarc) diff --git a/OpenKh.Command.Bbsa/Program.cs b/OpenKh.Command.Bbsa/Program.cs index 3552e5848..51552e04b 100644 --- a/OpenKh.Command.Bbsa/Program.cs +++ b/OpenKh.Command.Bbsa/Program.cs @@ -3,9 +3,10 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; -using System.Text; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; using System.Threading; namespace OpenKh.Command.Bbsa @@ -291,9 +292,13 @@ private static string MakeSafeDestination(string baseDir, string entryName) entryName = entryName.Replace(c, '_'); var dest = Path.GetFullPath(Path.Combine(baseDir, entryName)); - var fullBase = Path.GetFullPath(baseDir) + Path.DirectorySeparatorChar; + var fullBase = Path.GetFullPath(baseDir + Path.DirectorySeparatorChar); + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; - if (!dest.StartsWith(fullBase, StringComparison.OrdinalIgnoreCase)) + if (!dest.StartsWith(fullBase, comparison)) throw new InvalidOperationException($"Unsafe archive path: {entryName}"); return dest; diff --git a/OpenKh.Command.DoctChanger/Program.cs b/OpenKh.Command.DoctChanger/Program.cs index 893b12362..10aaa911e 100644 --- a/OpenKh.Command.DoctChanger/Program.cs +++ b/OpenKh.Command.DoctChanger/Program.cs @@ -83,7 +83,7 @@ protected int OnExecute(CommandLineApplication app) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!mapOut.StartsWith(Output + Path.DirectorySeparatorChar, comparison)) + if (!mapOut.StartsWith(Path.GetFullPath(Output + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {fileName} is outside the output directory"); } diff --git a/OpenKh.Command.PmpConverter/Program.cs b/OpenKh.Command.PmpConverter/Program.cs index d73998718..ef667789a 100644 --- a/OpenKh.Command.PmpConverter/Program.cs +++ b/OpenKh.Command.PmpConverter/Program.cs @@ -329,19 +329,24 @@ public static List FromFbx(string filePath) const float Scale = 1.0f; var assimp = new Assimp.AssimpContext(); var scene = assimp.ImportFile(filePath, Assimp.PostProcessSteps.PreTransformVertices); - var baseFilePath = Path.GetDirectoryName(filePath); + var baseFilePath = Path.GetDirectoryName(filePath) ?? "."; TexList = new List(); TextureData = new List(); foreach (Assimp.Material mat in scene.Materials) { + if (String.IsNullOrEmpty(mat.TextureDiffuse.FilePath)) + { + throw new Exception($"Material '{mat.Name}' has no diffuse texture specified"); + } + var texPath = Path.GetFullPath(mat.TextureDiffuse.FilePath); var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!texPath.StartsWith(baseFilePath + Path.DirectorySeparatorChar, comparison)) + if (!texPath.StartsWith(Path.GetFullPath(baseFilePath + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {texPath} is outside the output directory"); } diff --git a/OpenKh.Command.Rbin/Program.cs b/OpenKh.Command.Rbin/Program.cs index 456a2e35f..473c57108 100644 --- a/OpenKh.Command.Rbin/Program.cs +++ b/OpenKh.Command.Rbin/Program.cs @@ -209,7 +209,7 @@ private static bool ExtractFile(FileStream stream, Ddd.Rbin.TocEntry tocEntry, s ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!outPath.StartsWith(outputFolder + Path.DirectorySeparatorChar, comparison)) + if (!outPath.StartsWith(Path.GetFullPath(outputFolder + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {tocEntry.Name} is outside the output directory"); } diff --git a/OpenKh.Command.SpawnScript/Program.cs b/OpenKh.Command.SpawnScript/Program.cs index 4fffcd41b..5d27d5d01 100644 --- a/OpenKh.Command.SpawnScript/Program.cs +++ b/OpenKh.Command.SpawnScript/Program.cs @@ -59,7 +59,8 @@ private class DecompileCommand protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.txt"; - OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), OutputName); + var OutputBaseDir = Path.GetDirectoryName(InputPath); + OutputPath ??= Path.Combine(OutputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { var spawnScript = File.OpenRead(InputPath).Using(AreaDataScript.Read); @@ -69,7 +70,7 @@ protected int OnExecute(CommandLineApplication app) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!fullPath.StartsWith(InputPath + Path.DirectorySeparatorChar, comparison)) + if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDir + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {OutputName} is outside the output directory"); } @@ -93,7 +94,8 @@ private class CompileCommand protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.spawnscript"; - OutputPath ??= Path.Combine(Path.GetDirectoryName(InputPath), OutputName); + var OutputBaseDir = Path.GetDirectoryName(InputPath); + OutputPath ??= Path.Combine(OutputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { var spawnScript = AreaDataScript.Compile(File.ReadAllText(InputPath)); @@ -103,7 +105,7 @@ protected int OnExecute(CommandLineApplication app) var fullPath = Path.GetFullPath(OutputPath); - if (!fullPath.StartsWith(InputPath + Path.DirectorySeparatorChar, comparison)) + if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDir + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {OutputName} is outside the output directory"); } diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index 9a1f2fa04..82bb1068f 100644 --- a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs +++ b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs @@ -236,7 +236,7 @@ public void exportTextures(string filePath) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - if (!finalPath.StartsWith(filePath, comparison)) + if (!finalPath.StartsWith(Path.GetFullPath(filePath + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {fileName} is outside the output directory"); } From 74392d4c7ed54731639b1a169024e9345fbb1171 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 15:42:14 -0800 Subject: [PATCH 07/11] Fix minor issue raised by CodeRabbit --- OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index 82bb1068f..5bfb66119 100644 --- a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs +++ b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs @@ -223,7 +223,7 @@ public void exportTextures(string filePath) string fileName = "Texture" + i.ToString("D4"); string fullPath = filePath + fileName; - string finalPath = Path.GetFullPath(fullPath); + string finalPath = fullPath; int repeat = 0; while (File.Exists(finalPath)) @@ -232,6 +232,8 @@ public void exportTextures(string filePath) finalPath = fullPath + " (" + repeat + ")"; } + finalPath = Path.GetFullPath(finalPath); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; From bae2d1624e01c0b4e0e1eeb7eef9a496b5f63a5e Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 15:50:08 -0800 Subject: [PATCH 08/11] Fix issue raised by CodeRabbit in PmpConverter where relative texture paths were attempting to save to the incorrect location --- OpenKh.Command.PmpConverter/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenKh.Command.PmpConverter/Program.cs b/OpenKh.Command.PmpConverter/Program.cs index ef667789a..4fdee5a10 100644 --- a/OpenKh.Command.PmpConverter/Program.cs +++ b/OpenKh.Command.PmpConverter/Program.cs @@ -340,7 +340,9 @@ public static List FromFbx(string filePath) throw new Exception($"Material '{mat.Name}' has no diffuse texture specified"); } - var texPath = Path.GetFullPath(mat.TextureDiffuse.FilePath); + var texPath = Path.IsPathRooted(mat.TextureDiffuse.FilePath) + ? Path.GetFullPath(mat.TextureDiffuse.FilePath) + : Path.GetFullPath(Path.Combine(baseFilePath, mat.TextureDiffuse.FilePath)); var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase From 71f26acecbf45ad929bc8d2f85f06d6ab9c50e38 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Sun, 1 Feb 2026 15:58:07 -0800 Subject: [PATCH 09/11] Fix additional issues raised by CodeRabbit --- OpenKh.Command.PmpConverter/Program.cs | 6 +++++- OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs | 6 +----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenKh.Command.PmpConverter/Program.cs b/OpenKh.Command.PmpConverter/Program.cs index 4fdee5a10..c3be76f38 100644 --- a/OpenKh.Command.PmpConverter/Program.cs +++ b/OpenKh.Command.PmpConverter/Program.cs @@ -329,7 +329,11 @@ public static List FromFbx(string filePath) const float Scale = 1.0f; var assimp = new Assimp.AssimpContext(); var scene = assimp.ImportFile(filePath, Assimp.PostProcessSteps.PreTransformVertices); - var baseFilePath = Path.GetDirectoryName(filePath) ?? "."; + var baseFilePath = Path.GetDirectoryName(filePath); + if (String.IsNullOrEmpty(baseFilePath)) + { + baseFilePath = "."; + } TexList = new List(); TextureData = new List(); diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index 5bfb66119..b6946128a 100644 --- a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs +++ b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs @@ -197,17 +197,13 @@ public void exportModel(AssimpGeneric.FileFormat fileFormat = AssimpGeneric.File { string dirPath; - if (String.IsNullOrEmpty(sfd.FileName)) - { - throw new Exception("No filename provided"); - } dirPath = Path.GetDirectoryName(sfd.FileName); if (!Directory.Exists(dirPath)) return; - dirPath += "\\"; + dirPath += Path.DirectorySeparatorChar; AssimpGeneric.ExportScene(scene, fileFormat, sfd.FileName); exportTextures(dirPath); From 344f970efe55d978924cc5d0a43a48e5d25c0da8 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Mon, 2 Feb 2026 20:27:17 -0800 Subject: [PATCH 10/11] Minor fixes to Bar and SpawnScript --- OpenKh.Command.Bar/Program.cs | 3 +++ OpenKh.Command.SpawnScript/Program.cs | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/OpenKh.Command.Bar/Program.cs b/OpenKh.Command.Bar/Program.cs index f800090e1..65527efae 100644 --- a/OpenKh.Command.Bar/Program.cs +++ b/OpenKh.Command.Bar/Program.cs @@ -109,14 +109,17 @@ protected int OnExecute(CommandLineApplication app) var fullPath = Path.GetFullPath(OutputFile); + /* var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDirectory + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {originalFileName} is outside the output directory"); } + */ using var outputStream = File.Create(fullPath); Kh2.Bar.Write(outputStream, binarc); diff --git a/OpenKh.Command.SpawnScript/Program.cs b/OpenKh.Command.SpawnScript/Program.cs index 5d27d5d01..6d6c03fd6 100644 --- a/OpenKh.Command.SpawnScript/Program.cs +++ b/OpenKh.Command.SpawnScript/Program.cs @@ -59,13 +59,14 @@ private class DecompileCommand protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.txt"; - var OutputBaseDir = Path.GetDirectoryName(InputPath); - OutputPath ??= Path.Combine(OutputBaseDir, OutputName); + var InputBaseDir = Path.GetDirectoryName(InputPath); + OutputPath ??= Path.Combine(InputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { var spawnScript = File.OpenRead(InputPath).Using(AreaDataScript.Read); var fullPath = Path.GetFullPath(OutputPath); + /* var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; @@ -74,6 +75,7 @@ protected int OnExecute(CommandLineApplication app) { throw new Exception($"The file {OutputName} is outside the output directory"); } + //*/ File.WriteAllText(fullPath, AreaDataScript.Decompile(spawnScript)); } @@ -94,21 +96,24 @@ private class CompileCommand protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.spawnscript"; - var OutputBaseDir = Path.GetDirectoryName(InputPath); - OutputPath ??= Path.Combine(OutputBaseDir, OutputName); + var InputBaseDir = Path.GetDirectoryName(InputPath); + OutputPath ??= Path.Combine(InputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { var spawnScript = AreaDataScript.Compile(File.ReadAllText(InputPath)); - var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal; - + var fullPath = Path.GetFullPath(OutputPath); + /* + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDir + Path.DirectorySeparatorChar), comparison)) { throw new Exception($"The file {OutputName} is outside the output directory"); } + //*/ using var stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); AreaDataScript.Write(stream, spawnScript); } From 806e751ac4451e1d254c7233c6066bbcb5d4e5a7 Mon Sep 17 00:00:00 2001 From: some1fromthedark Date: Mon, 2 Feb 2026 23:02:56 -0800 Subject: [PATCH 11/11] Fix issues raised by CodeRabbit in Bar and SpawnScript --- OpenKh.Command.Bar/Program.cs | 19 +++++------------- OpenKh.Command.SpawnScript/Program.cs | 29 ++++++++------------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/OpenKh.Command.Bar/Program.cs b/OpenKh.Command.Bar/Program.cs index 65527efae..99530a70e 100644 --- a/OpenKh.Command.Bar/Program.cs +++ b/OpenKh.Command.Bar/Program.cs @@ -90,6 +90,10 @@ private class PackCommand protected int OnExecute(CommandLineApplication app) { var baseDirectory = Path.GetDirectoryName(InputProject); + if (String.IsNullOrEmpty(baseDirectory)) + { + baseDirectory = Environment.CurrentDirectory; + } var binarc = Core.ImportProject(InputProject, out var originalFileName); var OutputBaseDirectory = baseDirectory; @@ -100,8 +104,7 @@ protected int OnExecute(CommandLineApplication app) OutputFile = Path.Combine(OutputBaseDirectory, OutputFileName); } - if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) && - File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory)) + if (!File.Exists(OutputFile) && Directory.Exists(OutputFile)) { OutputBaseDirectory = OutputFile; OutputFile = Path.Combine(OutputBaseDirectory, originalFileName); @@ -109,18 +112,6 @@ protected int OnExecute(CommandLineApplication app) var fullPath = Path.GetFullPath(OutputFile); - /* - var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal; - - - if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDirectory + Path.DirectorySeparatorChar), comparison)) - { - throw new Exception($"The file {originalFileName} is outside the output directory"); - } - */ - using var outputStream = File.Create(fullPath); Kh2.Bar.Write(outputStream, binarc); diff --git a/OpenKh.Command.SpawnScript/Program.cs b/OpenKh.Command.SpawnScript/Program.cs index 6d6c03fd6..378e65a62 100644 --- a/OpenKh.Command.SpawnScript/Program.cs +++ b/OpenKh.Command.SpawnScript/Program.cs @@ -6,7 +6,6 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; namespace OpenKh.Command.SpawnScript { @@ -60,22 +59,16 @@ protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.txt"; var InputBaseDir = Path.GetDirectoryName(InputPath); + if (String.IsNullOrEmpty(InputBaseDir)) + { + InputBaseDir = Environment.CurrentDirectory; + } OutputPath ??= Path.Combine(InputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { var spawnScript = File.OpenRead(InputPath).Using(AreaDataScript.Read); var fullPath = Path.GetFullPath(OutputPath); - /* - var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal; - - if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDir + Path.DirectorySeparatorChar), comparison)) - { - throw new Exception($"The file {OutputName} is outside the output directory"); - } - //*/ File.WriteAllText(fullPath, AreaDataScript.Decompile(spawnScript)); } @@ -97,6 +90,10 @@ protected int OnExecute(CommandLineApplication app) { var OutputName = $"{Path.GetFileNameWithoutExtension(InputPath)}.spawnscript"; var InputBaseDir = Path.GetDirectoryName(InputPath); + if (String.IsNullOrEmpty(InputBaseDir)) + { + InputBaseDir = Environment.CurrentDirectory; + } OutputPath ??= Path.Combine(InputBaseDir, OutputName); if (!String.IsNullOrEmpty(OutputPath)) { @@ -104,16 +101,6 @@ protected int OnExecute(CommandLineApplication app) var fullPath = Path.GetFullPath(OutputPath); - /* - var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? StringComparison.OrdinalIgnoreCase - : StringComparison.Ordinal; - - if (!fullPath.StartsWith(Path.GetFullPath(OutputBaseDir + Path.DirectorySeparatorChar), comparison)) - { - throw new Exception($"The file {OutputName} is outside the output directory"); - } - //*/ using var stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); AreaDataScript.Write(stream, spawnScript); }