From 314a880213a83dae0e80715a75bff4577bc3acb6 Mon Sep 17 00:00:00 2001 From: Teras <35451366+Terasol@users.noreply.github.com> Date: Wed, 30 Nov 2022 00:32:27 +0100 Subject: [PATCH] Update to TRPAK Implementation (#546) * Move TRPAK up the list of Fileformats prevents false detections of some of the Archives as other File Formats * add best guess for Paths if all known paths are the same add that one path to all other Files * Revert "Move TRPAK up the list of Fileformats" This reverts commit a1c0c2d6300c203aaf922ed081a9283f75fb19fb. * Move TRPAK up the list of Fileformats this time dont reformat the whole Class * Added Quick access Added Quick access Folders in the same style as GFPAK for all Texture Files and all Model Files * cleanup and set some ImageKeys * Prefer BNTX Filenames that mathc the Hash instead of relying on only getting the filenames from the file itself --- .../FileFormats/Archives/TRPAK/TRPAK.cs | 224 +++++++++++++++++- File_Format_Library/Main.cs | 2 +- 2 files changed, 219 insertions(+), 7 deletions(-) diff --git a/File_Format_Library/FileFormats/Archives/TRPAK/TRPAK.cs b/File_Format_Library/FileFormats/Archives/TRPAK/TRPAK.cs index 7f66745e..56a20baf 100644 --- a/File_Format_Library/FileFormats/Archives/TRPAK/TRPAK.cs +++ b/File_Format_Library/FileFormats/Archives/TRPAK/TRPAK.cs @@ -18,11 +18,29 @@ namespace FirstPlugin public bool CanRenameFiles => false; public bool CanReplaceFiles => false; public bool CanSave { get; set; } = false; + + public Dictionary CategoryLookup + { + get + { + return new Dictionary() + { + { ".bntx", "Textures" }, + { ".trmbf", "Models" }, + { ".trmdl", "Models" }, + { ".trmsh", "Models" }, + { ".trmtr", "Models" }, + { ".trskl", "Models" }, + }; + } + } + public string[] Description { get; set; } = new string[] { "tr Package" }; public string[] Extension { get; set; } = new string[] { "*.trpak" }; public string FileName { get; set; } public string FilePath { get; set; } - public IEnumerable Files { get; set; } + public List files { get; set; } + public IEnumerable Files => files; public FileType FileType { get; set; } = FileType.Archive; public IFileInfo IFileInfo { get; set; } @@ -58,12 +76,13 @@ namespace FirstPlugin public void Load(Stream stream) { GFPAKHashCache.EnsureHashCache(); - Files = new List(); + files = new List(); FlatBuffers.TRPAK.TRPAK trpak = FlatBuffers.TRPAK.TRPAK.GetRootAsTRPAK(new FlatBuffers.ByteBuffer(stream.ToBytes())); if (trpak.FilesLength != trpak.HashesLength) { throw new Exception("not the same amount of Hashes and File Entries in Trpak Container"); } + List paths = new List(); for (int i = 0; i < trpak.FilesLength; i++) { FlatBuffers.TRPAK.File? file = trpak.Files(i); @@ -84,10 +103,72 @@ namespace FirstPlugin } AFI.FileData = FileData; AFI.Name = GetName(hash, FileData); + if (AFI.Name.Contains("/")) + { + string tmppath = System.IO.Path.GetDirectoryName(AFI.Name); + if (!paths.Contains(tmppath)) paths.Add(tmppath); + } AFI.FileName = AFI.Name; - ((List)Files).Add(AFI); + files.Add(AFI); } } + if (paths.Count == 1) + { + string path = paths[0].Replace("\\", "/"); + foreach (var f in files) + { + if (!f.Name.Contains("/")) + { + f.Name = Path.Combine(path, f.Name).Replace("\\", "/"); + f.FileName = f.Name; + } + } + } + + TreeNode node = new QuickAccessFolder(this, "Quick access"); + Nodes.Add(node); + Dictionary folders = new Dictionary(); + foreach (var file in files) + { + string ext = Utils.GetExtension(file.FileName); + string folderName = "Other"; + if (CategoryLookup.ContainsKey(ext)) + folderName = CategoryLookup[ext]; + + if (!folders.ContainsKey(folderName)) + { + TreeNode folder = new GFPAK.QuickAccessFileFolder(folderName); + if (folderName == "Textures") + folder = new TextureFolder(this, "Textures"); + if (folderName == "Models") + folder = new GFPAK.QuickAccessFileFolder("Models"); + + node.Nodes.Add(folder); + folders.Add(folderName, folder); + } + + string name = Path.GetFileName(file.FileName); + + string imageKey = "fileBlank"; + switch (ext) + { + case ".bntx": imageKey = "bntx"; break; + case ".trmdl": imageKey = "model"; break; + case ".trskl": imageKey = "bone"; break; + case ".trmbf": imageKey = "mesh"; break; + case ".trmsh": imageKey = "mesh"; break; + case ".trmtr": imageKey = "material"; break; + } + + TreeNode fodlerNode = folders[folderName]; + fodlerNode.Nodes.Add(new QuickAccessFile(name) + { + Tag = file, + ImageKey = imageKey, + SelectedImageKey = imageKey, + }); + } + GFPAKHashCache.WriteCache(); } @@ -98,7 +179,7 @@ namespace FirstPlugin public void Unload() { - foreach (var file in Files) + foreach (var file in files) { if (file.FileFormat != null) file.FileFormat.Unload(); @@ -106,7 +187,7 @@ namespace FirstPlugin file.FileData = null; } - ((List)Files).Clear(); + files.Clear(); GC.SuppressFinalize(this); } @@ -142,7 +223,7 @@ namespace FirstPlugin { string fileHashName = GFPAKHashCache.GetHashName(fileHash) ?? ""; string ext = FindMatch(Data, fileHashName); - if (ext == ".bntx" || ext == ".bfres" || ext == ".bnsh" || ext == ".bfsha") + if ((ext == ".bntx" && fileHashName == "") || ext == ".bfres" || ext == ".bnsh" || ext == ".bfsha") { string fileName = GetBinaryHeaderName(Data); //Check for matches for shaders @@ -168,5 +249,136 @@ namespace FirstPlugin return $"{fileHash.ToString("X")}{ext}"; } } + + public class TextureFolder : TreeNodeCustom, IContextMenuNode + { + private IArchiveFile ArchiveFile; + private bool HasExpanded = false; + private List Textures = new List(); + + public TextureFolder(IArchiveFile archive, string text) + { + ArchiveFile = archive; + Text = text; + } + + public void AddTexture(string fileName) + { + BNTX bntx = BNTX.CreateBNTXFromTexture(fileName); + var mem = new MemoryStream(); + bntx.Save(mem); + + string filePath = fileName; + + ArchiveFile.AddFile(new ArchiveFileInfo() + { + FileData = mem.ToArray(), + FileFormat = bntx, + FileName = filePath, + }); + } + + public virtual ToolStripItem[] GetContextMenuItems() + { + List Items = new List(); + Items.Add(new ToolStripMenuItem("Export All", null, ExportAllAction, Keys.Control | Keys.E)); + return Items.ToArray(); + } + + public void LoadTextures() + { + if (HasExpanded) return; + + List subNodes = new List(); + + foreach (TreeNode node in Nodes) + { + var file = (ArchiveFileInfo)node.Tag; + if (file.FileFormat == null) + file.FileFormat = file.OpenFile(); + + BNTX bntx = file.FileFormat as BNTX; + foreach (var tex in bntx.Textures.Values) + { + tex.OnTextureDeleted += OnTextureDeleted; + //Set tree key for deletion + tex.Name = tex.Text; + tex.Tag = file; + var texNode = new TreeNode(tex.Text); + texNode.Tag = tex; + texNode.ImageKey = tex.ImageKey; + texNode.SelectedImageKey = tex.SelectedImageKey; + subNodes.Add(texNode); + Textures.Add(tex); + } + } + + Nodes.Clear(); + Nodes.AddRange(subNodes.ToArray()); + + HasExpanded = true; + } + + public override void OnExpand() + { + LoadTextures(); + } + + private void ExportAllAction(object sender, EventArgs args) + { + LoadTextures(); + + List Formats = new List(); + Formats.Add("Microsoft DDS (.dds)"); + Formats.Add("Portable Graphics Network (.png)"); + Formats.Add("Joint Photographic Experts Group (.jpg)"); + Formats.Add("Bitmap Image (.bmp)"); + Formats.Add("Tagged Image File Format (.tiff)"); + + FolderSelectDialog sfd = new FolderSelectDialog(); + + if (sfd.ShowDialog() == DialogResult.OK) + { + string folderPath = sfd.SelectedPath; + + BatchFormatExport form = new BatchFormatExport(Formats); + if (form.ShowDialog() == DialogResult.OK) + { + foreach (STGenericTexture tex in Textures) + { + if (form.Index == 0) + tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds"); + else if (form.Index == 1) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".png"); + else if (form.Index == 2) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".jpg"); + else if (form.Index == 3) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".bmp"); + else if (form.Index == 4) + tex.SaveBitMap(folderPath + '\\' + tex.Text + ".tiff"); + } + } + } + } + + private void OnTextureDeleted(object sender, EventArgs e) + { + var tex = (TextureData)sender; + foreach (var file in ArchiveFile.Files) + { + if (file.FileFormat != null && file.FileFormat is BNTX) + { + var bntx = (BNTX)file.FileFormat; + if (bntx.Textures.ContainsKey(tex.Text)) + { + bntx.RemoveTexture(tex); + bntx.Unload(); + ArchiveFile.DeleteFile(file); + Nodes.RemoveByKey(tex.Text); + } + } + } + } + } } } \ No newline at end of file diff --git a/File_Format_Library/Main.cs b/File_Format_Library/Main.cs index ba5b6754..15f0f52d 100644 --- a/File_Format_Library/Main.cs +++ b/File_Format_Library/Main.cs @@ -350,6 +350,7 @@ namespace FirstPlugin Formats.Add(typeof(BTI)); Formats.Add(typeof(TXE)); Formats.Add(typeof(SARC)); + Formats.Add(typeof(TRPAK)); Formats.Add(typeof(BNTX)); Formats.Add(typeof(BEA)); Formats.Add(typeof(BYAML)); @@ -460,7 +461,6 @@ namespace FirstPlugin Formats.Add(typeof(MTXT)); Formats.Add(typeof(NKN)); Formats.Add(typeof(MetroidDreadLibrary.BSMAT)); - Formats.Add(typeof(TRPAK)); //Formats.Add(typeof(XLINK_FILE));