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 72ab48571..99530a70e 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 { @@ -89,16 +90,29 @@ 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; + var OutputFileName = originalFileName; + if (string.IsNullOrEmpty(OutputFile)) - OutputFile = Path.Combine(baseDirectory, originalFileName); + { + OutputFile = Path.Combine(OutputBaseDirectory, OutputFileName); + } + + if (!File.Exists(OutputFile) && Directory.Exists(OutputFile)) + { + OutputBaseDirectory = OutputFile; + OutputFile = Path.Combine(OutputBaseDirectory, originalFileName); + } - if (!File.Exists(OutputFile) && Directory.Exists(OutputFile) && - File.GetAttributes(OutputFile).HasFlag(FileAttributes.Directory)) - OutputFile = Path.Combine(OutputFile, originalFileName); + var fullPath = Path.GetFullPath(OutputFile); - 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 484f5488a..51552e04b 100644 --- a/OpenKh.Command.Bbsa/Program.cs +++ b/OpenKh.Command.Bbsa/Program.cs @@ -3,9 +3,11 @@ 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 { @@ -140,10 +142,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 +179,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 +281,27 @@ 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); + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + 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 d546ef7ef..10aaa911e 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(Path.GetFullPath(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.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..c3be76f38 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; @@ -331,17 +330,40 @@ public static List FromFbx(string filePath) var assimp = new Assimp.AssimpContext(); var scene = assimp.ImportFile(filePath, Assimp.PostProcessSteps.PreTransformVertices); var baseFilePath = Path.GetDirectoryName(filePath); + if (String.IsNullOrEmpty(baseFilePath)) + { + baseFilePath = "."; + } TexList = new List(); TextureData = new List(); foreach (Assimp.Material mat in scene.Materials) { - TexList.Add(Path.GetFileName(mat.TextureDiffuse.FilePath)); - Stream str = File.OpenRead(TexList[TexList.Count - 1]); + if (String.IsNullOrEmpty(mat.TextureDiffuse.FilePath)) + { + throw new Exception($"Material '{mat.Name}' has no diffuse texture specified"); + } + + var texPath = Path.IsPathRooted(mat.TextureDiffuse.FilePath) + ? Path.GetFullPath(mat.TextureDiffuse.FilePath) + : Path.GetFullPath(Path.Combine(baseFilePath, mat.TextureDiffuse.FilePath)); - PngImage png = new PngImage(str); - Tm2 tmImage = Tm2.Create(png); - TextureData.Add(tmImage); + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!texPath.StartsWith(Path.GetFullPath(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 +386,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; diff --git a/OpenKh.Command.Rbin/Program.cs b/OpenKh.Command.Rbin/Program.cs index 0162edbee..473c57108 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(Path.GetFullPath(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,8 @@ 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)); - writeStream.Close(); } return true; diff --git a/OpenKh.Command.SpawnScript/Program.cs b/OpenKh.Command.SpawnScript/Program.cs index e04e3bd21..378e65a62 100644 --- a/OpenKh.Command.SpawnScript/Program.cs +++ b/OpenKh.Command.SpawnScript/Program.cs @@ -57,9 +57,21 @@ 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"; + 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); + + File.WriteAllText(fullPath, AreaDataScript.Decompile(spawnScript)); + } return 0; } } @@ -76,9 +88,22 @@ 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"; + var InputBaseDir = Path.GetDirectoryName(InputPath); + if (String.IsNullOrEmpty(InputBaseDir)) + { + InputBaseDir = Environment.CurrentDirectory; + } + OutputPath ??= Path.Combine(InputBaseDir, OutputName); + if (!String.IsNullOrEmpty(OutputPath)) + { + var spawnScript = AreaDataScript.Compile(File.ReadAllText(InputPath)); + + var fullPath = Path.GetFullPath(OutputPath); + + using var stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None); + AreaDataScript.Write(stream, spawnScript); + } return 0; } } diff --git a/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs b/OpenKh.Tools.Kh2MdlxEditor/Views/Main2_Window.xaml.cs index 976e57b8a..b6946128a 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,12 +195,15 @@ public void exportModel(AssimpGeneric.FileFormat fileFormat = AssimpGeneric.File sfd.ShowDialog(); if (sfd.FileName != "") { - string dirPath = System.IO.Path.GetDirectoryName(sfd.FileName); + + string dirPath; + + dirPath = Path.GetDirectoryName(sfd.FileName); if (!Directory.Exists(dirPath)) return; - dirPath += "\\"; + dirPath += Path.DirectorySeparatorChar; AssimpGeneric.ExportScene(scene, fileFormat, sfd.FileName); exportTextures(dirPath); @@ -213,8 +217,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 fileName = "Texture" + i.ToString("D4"); + string fullPath = filePath + fileName; string finalPath = fullPath; + int repeat = 0; while (File.Exists(finalPath)) { @@ -222,7 +228,18 @@ public void exportTextures(string filePath) finalPath = fullPath + " (" + repeat + ")"; } - AssimpGeneric.ExportBitmapSourceAsPng(bitmapImage, fullPath); + finalPath = Path.GetFullPath(finalPath); + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (!finalPath.StartsWith(Path.GetFullPath(filePath + Path.DirectorySeparatorChar), comparison)) + { + throw new Exception($"The file {fileName} is outside the output directory"); + } + + AssimpGeneric.ExportBitmapSourceAsPng(bitmapImage, finalPath); } } public void reloadModelControl()