using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Switch_Toolbox; using System.Windows.Forms; using Switch_Toolbox.Library; using System.IO; using BezelEngineArchive_Lib; using Switch_Toolbox.Library.IO; using Switch_Toolbox.Library.Forms; namespace FirstPlugin { public class BEA : TreeNodeFile, IFileFormat { public FileType FileType { get; set; } = FileType.Archive; public bool CanAddFiles { get; set; } = false; public bool CanRenameFiles { get; set; } = false; public bool CanDeleteFiles { get; set; } = false; public bool CanReplaceFiles { get; set; } = true; public IEnumerable Files { get; } public bool CanSave { get; set; } public string[] Description { get; set; } = new string[] { "Bevel Engine Archive" }; public string[] Extension { get; set; } = new string[] { "*.bea" }; public string FileName { get; set; } public string FilePath { get; set; } public IFileInfo IFileInfo { get; set; } public bool Identify(Stream stream) { using (var reader = new FileReader(stream, true)) { return reader.CheckSignature(4, "SCNE"); } } public Type[] Types { get { List types = new List(); types.Add(typeof(MenuExt)); return types.ToArray(); } } class MenuExt : IFileMenuExtension { public STToolStripItem[] NewFileMenuExtensions => null; public STToolStripItem[] NewFromFileMenuExtensions => null; public STToolStripItem[] ToolsMenuExtensions => null; public STToolStripItem[] TitleBarExtensions => null; public STToolStripItem[] CompressionMenuExtensions => null; public STToolStripItem[] ExperimentalMenuExtensions => experimentalMenu; public STToolStripItem[] EditMenuExtensions => null; public ToolStripButton[] IconButtonMenuExtensions => null; STToolStripItem[] experimentalMenu = new STToolStripItem[1]; public MenuExt() { experimentalMenu[0] = new STToolStripItem("BEA"); STToolStripItem batchLUA = new STToolStripItem("Batch Extract LUA"); batchLUA.Click += BatchExtractLUA; experimentalMenu[0].DropDownItems.Add(batchLUA); } private void BatchExtractLUA(object sender, EventArgs e) { FolderSelectDialog ofd = new FolderSelectDialog(); if (ofd.ShowDialog() == DialogResult.OK) { string folderPath = ofd.SelectedPath; foreach (string file in Directory.GetFiles(folderPath)) { Console.WriteLine(file); if (Path.GetExtension(file) == ".bea") { BEA bea = new BEA(); bea.FileName = file; bea.Load(new FileStream(file, FileMode.Open)); foreach (FileEntry asset in bea.Nodes) { if (Path.GetExtension(asset.FullName) == ".lua") { try { if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName($"{folderPath}/{bea.Name}/{asset.FullName}"))) { if (!File.Exists(asset.FullName)) { if (!Directory.Exists($"{folderPath}/{bea.Name}/{asset.FullName}")) { Directory.CreateDirectory(Path.GetDirectoryName($"{folderPath}/{bea.Name}/{asset.FullName}")); } } } File.WriteAllBytes($"{folderPath}/{bea.Name}/{asset.FullName}", GetASSTData(asset)); } catch { } } } } } } } } public BezelEngineArchive beaFile; static STProgressBar progressBar; public void Load(System.IO.Stream stream) { Text = FileName; CanSave = true; beaFile = new BezelEngineArchive(stream); FillTreeNodes(this, beaFile.FileList); ContextMenuStrip = new STContextMenuStrip(); ContextMenuStrip.Items.Add(new STToolStipMenuItem("Save", null, Save, Keys.Control | Keys.S)); ContextMenuStrip.Items.Add(new STToolStripSeparator()); ContextMenuStrip.Items.Add(new STToolStipMenuItem("Preview Window", null, PreviewWindow, Keys.Control | Keys.P)); ContextMenuStrip.Items.Add(new STToolStripSeparator()); ContextMenuStrip.Items.Add(new STToolStipMenuItem("Export All", null, ExportAll, Keys.Control | Keys.E)); CanDelete = true; } public void Unload() { } public bool AddFile(ArchiveFileInfo archiveFileInfo) { return false; } public bool DeleteFile(ArchiveFileInfo archiveFileInfo) { return false; } IEnumerable Collect(TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { yield return node; foreach (var child in Collect(node.Nodes)) yield return child; } } private void Save(object sender, EventArgs args) { Cursor.Current = Cursors.WaitCursor; List formats = new List(); formats.Add(this); SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = Utils.GetAllFilters(formats); sfd.FileName = FileName; if (sfd.ShowDialog() == DialogResult.OK) { STFileSaver.SaveFileFormat(this, sfd.FileName); } GC.Collect(); } public byte[] Save() { beaFile.FileList.Clear(); beaFile.FileDictionary.Clear(); foreach (TreeNode node in Collect(Nodes)) { if (node is TreeNodeFile && node != this) { IFileFormat fileFormat = (IFileFormat)node; if (fileFormat != null && fileFormat.CanSave) { byte[] uncomrompressedData = new byte[0]; //Save any active files in the editor if supported if (fileFormat.CanSave) uncomrompressedData = fileFormat.Save(); //Create a new asset entry ASST asset = new ASST(); asset.unk = 2; asset.unk2 = 2; asset.UncompressedSize = uncomrompressedData.LongLength; if (fileFormat.IFileInfo.FileIsCompressed) asset.FileData = STLibraryCompression.ZSTD.Compress(uncomrompressedData); else asset.FileData = uncomrompressedData; asset.FileName = fileFormat.FilePath; beaFile.FileList.Add(fileFormat.FilePath, asset); beaFile.FileDictionary.Add(fileFormat.FilePath); } } else if (node is FileEntry) { ASST asset = new ASST(); asset.unk = ((FileEntry)node).unk1; asset.unk2 = ((FileEntry)node).unk2; asset.FileName = ((FileEntry)node).FullName; asset.FileData = ((FileEntry)node).data; byte[] uncomp = GetASSTData((FileEntry)node); asset.UncompressedSize = uncomp.Length; beaFile.FileList.Add(asset.FileName, asset); beaFile.FileDictionary.Add(asset.FileName); } } MemoryStream mem = new MemoryStream(); beaFile.Save(mem); return mem.ToArray(); } private void ExportAll(object sender, EventArgs args) { FolderSelectDialog fsd = new FolderSelectDialog(); if (fsd.ShowDialog() == DialogResult.OK) { progressBar = new STProgressBar(); progressBar.Task = "Extracing Files..."; progressBar.Refresh(); progressBar.Value = 0; progressBar.StartPosition = FormStartPosition.CenterScreen; progressBar.Show(); ExportAll(fsd.SelectedPath, progressBar); } } private void ExportAll(string Folder, STProgressBar progressBar) { int Curfile = 0; foreach (FileEntry asst in Nodes) { int value = (Curfile * 100) / beaFile.FileList.Count; progressBar.Value = value; progressBar.Refresh(); try { if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName($"{Folder}/{beaFile.Name}/{asst.FullName}"))) { if (!File.Exists(asst.FullName)) { if (!Directory.Exists($"{Folder}/{beaFile.Name}/{asst.FullName}")) { Directory.CreateDirectory(Path.GetDirectoryName($"{Folder}/{beaFile.Name}/{asst.FullName}")); } } } File.WriteAllBytes($"{Folder}/{beaFile.Name}/{asst.FullName}", GetASSTData(asst)); } catch { } Curfile++; if (value == 99) value = 100; progressBar.Value = value; progressBar.Refresh(); } } private void CallRecursive(TreeView treeView) { // Print each node recursively. TreeNodeCollection nodes = treeView.Nodes; foreach (TreeNode n in nodes) { PrintRecursive(n); } } private void PrintRecursive(TreeNode treeNode) { if (treeNode is FileEntry) { ((FileEntry)treeNode).OnDoubleMouseClick(treeNode.TreeView); } // Print each node recursively. foreach (TreeNode tn in treeNode.Nodes) { PrintRecursive(tn); } } public void PreviewWindow(object sender, EventArgs args) { PreviewFormatList previewFormatList = new PreviewFormatList(); if (previewFormatList.ShowDialog() == DialogResult.OK) { CallRecursive(TreeView); Console.WriteLine("Loaded files"); Console.WriteLine(PluginRuntime.bntxContainers.Count); PreviewEditor previewWindow = new PreviewEditor(); previewWindow.Show(); } } public bool Compressed; public class FolderEntry : TreeNode { public FolderEntry() { ImageKey = "folder"; SelectedImageKey = "folder"; } public FolderEntry(string Name) { Text = Name; } } public class FileEntry : TreeNodeCustom { public FileEntry() { ImageKey = "fileBlank"; SelectedImageKey = "fileBlank"; ContextMenu = new ContextMenu(); MenuItem export = new MenuItem("Export"); ContextMenu.MenuItems.Add(export); export.Click += Export; MenuItem replace = new MenuItem("Replace"); ContextMenu.MenuItems.Add(replace); replace.Click += Replace; } public string FullName; public IFileFormat FileHandle; //Load file instance to save later if possible public byte[] data; public ushort unk1; public ushort unk2; public bool IsCompressed; private void Export(object sender, EventArgs args) { SaveFileDialog sfd = new SaveFileDialog(); sfd.FileName = Text; sfd.DefaultExt = Path.GetExtension(Text); sfd.Filter = "All files(*.*)|*.*"; if (sfd.ShowDialog() == DialogResult.OK) { File.WriteAllBytes(sfd.FileName, GetASSTData(this)); } } private void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.FileName = Text; ofd.DefaultExt = Path.GetExtension(Text); ofd.Filter = "All files(*.*)|*.*"; if (ofd.ShowDialog() == DialogResult.OK) { SetASST(this, File.ReadAllBytes(ofd.FileName)); } } public override void OnClick(TreeView treeView) { HexEditor editor = (HexEditor)LibraryGUI.Instance.GetActiveContent(typeof(HexEditor)); if (editor == null) { editor = new HexEditor(); LibraryGUI.Instance.LoadEditor(editor); } editor.Text = Text; editor.Dock = DockStyle.Fill; editor.LoadData(GetASSTData(this)); } public override void OnDoubleMouseClick(TreeView treeview) { if (GetASSTData(this) != null) { TreeNode node = STFileLoader.GetNodeFileFormat(FullName, GetASSTData(this), true, this, true, IsCompressed, CompressionType.Zstb); if (node != null) ReplaceNode(this.Parent, this, node); } } } public static void ReplaceNode(TreeNode node, TreeNode replaceNode, TreeNode NewNode) { int index = node.Nodes.IndexOf(replaceNode); node.Nodes.RemoveAt(index); node.Nodes.Insert(index, NewNode); } public static byte[] GetASSTData(FileEntry entry) { if (entry.IsCompressed) return STLibraryCompression.ZSTD.Decompress(entry.data); else return entry.data; } public static void SetASST(FileEntry fileEntry, byte[] data) { if (fileEntry.IsCompressed) fileEntry.data = STLibraryCompression.ZSTD.Compress(data); else fileEntry.data = data; } void FillTreeNodes(TreeNode root, Dictionary files) { var rootText = root.Text; var rootTextLength = rootText.Length; var nodeStrings = files; foreach (var node in nodeStrings) { string nodeString = node.Key; var roots = nodeString.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); // The initial parent is the root node TreeNode parentNode = root; var sb = new StringBuilder(rootText, nodeString.Length + rootTextLength); for (int rootIndex = 0; rootIndex < roots.Length; rootIndex++) { // Build the node name var parentName = roots[rootIndex]; sb.Append("/"); sb.Append(parentName); var nodeName = sb.ToString(); // Search for the node var index = parentNode.Nodes.IndexOfKey(nodeName); if (index == -1) { // Node was not found, add it var temp = new TreeNode(parentName, 0, 0); if (rootIndex == roots.Length - 1) temp = SetupFileEntry(node.Value,parentName, node.Value.FileName, node.Value.IsCompressed); else temp = SetupFolderEntry(temp); temp.Name = nodeName; parentNode.Nodes.Add(temp); parentNode = temp; } else { // Node was found, set that as parent and continue parentNode = parentNode.Nodes[index]; } } } } public FolderEntry SetupFolderEntry(TreeNode node) { FolderEntry folder = new FolderEntry(); folder.Text = node.Text; return folder; } List BuildFinalList(List paths) { var finalList = new List(); foreach (var path in paths) { bool found = false; foreach (var item in finalList) { if (item.StartsWith(path, StringComparison.Ordinal)) { found = true; break; } } if (!found) { finalList.Add(path); } } return finalList; } public FileEntry SetupFileEntry(ASST asset,string name, string fullName, bool IsCompressed) { FileEntry fileEntry = new FileEntry(); fileEntry.FullName = fullName; fileEntry.Name = name; fileEntry.Text = name; fileEntry.unk1 = asset.unk; fileEntry.unk2 = asset.unk2; fileEntry.IsCompressed = IsCompressed; fileEntry.data = asset.FileData; //Now check magic //Todo clean this part up byte[] data = asset.FileData; if (IsCompressed) { try { data = STLibraryCompression.ZSTD.Decompress(asset.FileData); } catch { Console.WriteLine("Unkwon compression for file " + fileEntry.Name); } } string ext = Path.GetExtension(name); string SarcEx = SARCExt.SARC.GuessFileExtension(data); if (name.EndsWith("bfres") || name.EndsWith("fmdb") || name.EndsWith("fskb") || name.EndsWith("ftsb") || name.EndsWith("fvmb") || name.EndsWith("fvbb") || name.EndsWith("fspb") || name.EndsWith("fsnb")) { fileEntry.ImageKey = "bfres"; fileEntry.SelectedImageKey = "bfres"; } if (SarcEx == ".bntx") { fileEntry.ImageKey = "bntx"; fileEntry.SelectedImageKey = "bntx"; } if (SarcEx == ".byaml") { fileEntry.ImageKey = "byaml"; fileEntry.SelectedImageKey = "byaml"; } if (SarcEx == ".aamp") { fileEntry.ImageKey = "aamp"; fileEntry.SelectedImageKey = "aamp"; } if (ext == ".lua") { } data = new byte[0]; return fileEntry; } } }