Add working SDF (Mario and Rabbids) archive support.

This commit is contained in:
KillzXGaming 2019-06-16 19:35:46 -04:00
parent 3ab44e1f63
commit 8f849bf260
25 changed files with 737 additions and 176 deletions

Binary file not shown.

View file

@ -7,10 +7,11 @@ using Switch_Toolbox;
using System.Windows.Forms;
using Switch_Toolbox.Library;
using Switch_Toolbox.Library.IO;
using Switch_Toolbox.Library.Forms;
namespace FirstPlugin
{
public class SDF : TreeNodeFile, IFileFormat
public class SDF : IArchiveFile, IFileFormat
{
public FileType FileType { get; set; } = FileType.Archive;
@ -21,6 +22,11 @@ namespace FirstPlugin
public string FilePath { get; set; }
public IFileInfo IFileInfo { get; set; }
public bool CanAddFiles { get; set; } = false;
public bool CanRenameFiles { get; set; } = false;
public bool CanReplaceFiles { get; set; } = false;
public bool CanDeleteFiles { get; set; } = false;
public bool Identify(System.IO.Stream stream)
{
using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true))
@ -38,15 +44,19 @@ namespace FirstPlugin
}
}
public List<FileEntry> files = new List<FileEntry>();
public IEnumerable<ArchiveFileInfo> Files => files;
SDFTOC_Header Header;
SDFTOC_ID startId;
int[] block1;
SDFTOC_ID[] blockIds;
SDFTOC_Block2[] block2Array;
public SDFTOC_Block2[] block2Array;
byte[] DecompressedBlock;
SDFTOC_ID endId;
List<string> FilePaths = new List<string>();
//Temp but just for now as this is expeirmental. Need to optmize tree loading
private readonly int MAX_FILE_DISPLAY = 4000;
//Thanks to https://github.com/GoldFarmer/rouge_sdf/blob/master/main.cpp for docs/structs
public void Load(System.IO.Stream stream)
@ -97,30 +107,14 @@ namespace FirstPlugin
//Read last id
endId = new SDFTOC_ID(reader);
Text = FileName;
MessageBox.Show("Note! Support for this format is experimental. The tool will only load < 4000 files atm due to slow loading");
LoadTree();
for (int i = 0; i < FileEntries.Count; i++)
if (i < MAX_FILE_DISPLAY)
files.Add(FileEntries[i]);
}
}
private void LoadTree()
{
// Get a list of everything under the users' temp folder as an example
string[] fileList;
fileList = FilePaths.ToArray();
// Parse the file list into a TreeNode collection
TreeNode node = GetNodes(new TreeNode(), fileList);
Nodes.Add(node); // Add the new nodes
// Copy the new nodes to an array
int nodeCount = node.Nodes.Count;
TreeNode[] nodes = new TreeNode[nodeCount];
node.Nodes.CopyTo(nodes, 0);
Nodes.AddRange(nodes); // Add the new nodes
}
private TreeNode GetNodes(TreeNode parent, string[] fileList)
{
// build a TreeNode collection from the file list
@ -151,53 +145,6 @@ namespace FirstPlugin
return parent;
}
void FillTreeNodes(TreeNode root, List<string> files)
{
var rootText = root.Text;
var rootTextLength = rootText.Length;
var nodeStrings = files;
foreach (var node in nodeStrings)
{
string nodeString = node;
nodeString = nodeString.Replace(@"\", "/");
var roots = nodeString.Split(new char[] { '/' },
StringSplitOptions.RemoveEmptyEntries);
// The initial parent is the root node
var 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 = new TreeNode(parentName); //File entry
temp.Name = nodeName;
parentNode.Nodes.Add(temp);
parentNode = temp;
}
else
{
// Node was found, set that as parent and continue
parentNode = parentNode.Nodes[index];
}
}
}
}
public void DecompressNameBlock(uint magic, byte[] CompressedBlock, SDFTOC_Header header)
{
byte[] decomp = null;
@ -226,11 +173,108 @@ namespace FirstPlugin
return result;
}
public bool AddFile(ArchiveFileInfo archiveFileInfo)
{
return false;
}
public bool DeleteFile(ArchiveFileInfo archiveFileInfo)
{
return false;
}
public class FileEntry : ArchiveFileInfo
{
public SDF SDFParent;
public string FilePath;
public string FolderPath;
public string FileBlockPath;
public ulong PackageID;
public ulong Offset;
public ulong DecompressedSize;
public List<ulong> CompressedSizes;
public ulong DdsType;
public bool UseDDS;
public bool IsCompressed = false;
public override byte[] FileData
{
get => GetFileBytes();
set => base.FileData = value;
}
public override IFileFormat OpenFile()
{
var FileFormat = STFileLoader.OpenFileFormat(
IOExtensions.RemoveIllegaleFolderNameCharacters(FileName), FileData, true);
if (FileFormat is DDS)
((DDS)FileFormat).SwitchSwizzle = true;
return FileFormat;
}
public byte[] GetFileBytes()
{
List<byte[]> Data = new List<byte[]>();
if (File.Exists(FileBlockPath))
{
var block = File.Open(FileBlockPath, FileMode.Open);
using (var stream = new FileReader(block))
{
if (CompressedSizes.Count == 0)
{
//Decompressed File
string FileNameBlock = Path.Combine(FolderPath, FilePath);
string FolerPath = Path.GetDirectoryName(FileNameBlock);
if (!Directory.Exists(FolerPath))
Directory.CreateDirectory(FolerPath);
return stream.getSection((int)Offset, (int)DecompressedSize);
}
else
{
var PageSize = (double)0x10000;
var DecompOffset = 0;
var CompOffset = 0;
IsCompressed = true;
if (UseDDS)
{
Data.Add(SDFParent.block2Array[DdsType].Data);
}
for (var i = 0; i < CompressedSizes.Count; i++)
{
var decompSize = (int)Math.Min((int)DecompressedSize - DecompOffset, PageSize);
if (CompressedSizes[i] == 0 || decompSize == (int)CompressedSizes[i])
{
stream.Seek((int)Offset + CompOffset, SeekOrigin.Begin);
CompressedSizes[i] = (ulong)decompSize;
Data.Add( stream.ReadBytes(decompSize));
}
else
{
stream.Seek((int)Offset + CompOffset, SeekOrigin.Begin);
Data.Add(STLibraryCompression.ZSTD.Decompress(stream.ReadBytes((int)CompressedSizes[i])));
}
DecompOffset += (int)decompSize;
CompOffset += (int)CompressedSizes[i];
}
}
}
block.Dispose();
}
return Utils.CombineByteArray(Data.ToArray());
}
}
public List<FileEntry> FileEntries = new List<FileEntry>();
public void ParseNames(FileReader reader, string Name = "")
{
if (!Name.Contains("dummy") && FilePaths.Count < 200)
FilePaths.Add(Name);
char ch = reader.ReadChar();
if (ch == 0)
@ -253,7 +297,7 @@ namespace FirstPlugin
int flag1 = (ch >> 3) & 1;
// int flag1 = ch & 8;
if (count1 != 0)
if (count1 > 0)
{
uint strangeId = reader.ReadUInt32();
byte chr2 = reader.ReadByte();
@ -264,10 +308,10 @@ namespace FirstPlugin
for (int chunkIndex = 0; chunkIndex < count1; chunkIndex++)
{
byte ch3 = reader.ReadByte();
if (ch3 == 0)
{
break;
}
// if (ch3 == 0)
// {
// break;
// }
int compressedSizeByteCount = (ch3 & 3) + 1;
int packageOffsetByteCount = (ch3 >> 2) & 7;
@ -295,7 +339,7 @@ namespace FirstPlugin
if (packageId >= Header.Block1Count)
{
throw new InvalidDataException("SDF Package ID outside of TOC range");
// throw new InvalidDataException($"SDF Package ID ({packageId}) outside of TOC range ({ Header.Block1Count})");
}
@ -317,8 +361,8 @@ namespace FirstPlugin
if (Header.Version <= 0x16)
{
fileId = (long)readVariadicInteger(4, reader);
}
// fileId = (long)readVariadicInteger(4, reader);
}
if (compSizeArray.Count == 0 && hasCompression)
compSizeArray.Add(compressedSize);
@ -333,7 +377,7 @@ namespace FirstPlugin
{
reader.ReadByte();
reader.ReadByte();
}
}
}
}
else
@ -345,32 +389,43 @@ namespace FirstPlugin
}
}
public static ulong NextMultiple(ulong value, ulong multiple) => NextMultiple((long)value, multiple);
public static ulong NextMultiple(long value, ulong multiple)
{
return (ulong)Math.Ceiling(value / (double)multiple) * multiple;
}
public void DumpFile(string Name, ulong packageId, ulong packageOffset, ulong decompresedSize,
List<ulong> compressedSize, ulong ddsType, bool Append, bool UseDDS)
{
string PathFolder = Path.GetDirectoryName(FileName);
string PathFolder = Path.GetDirectoryName(FilePath);
string layer;
Console.WriteLine(Name + " " + packageId + " " + packageOffset + " " + decompresedSize + " " + ddsType + " " + UseDDS);
if (packageId < 1000)
if (packageId < 1000) layer = "A";
else if (packageId < 2000) layer = "B";
else if (packageId < 3000) layer = "C";
else layer = "D";
string ID = packageId.ToString("D" + 4);
string BlockFilePath = Path.Combine(PathFolder, $"sdf-{layer}-{ID}.sdfdata");
if (Append)
{
layer = "A";
}
else if (packageId < 2000)
{
layer = "B";
}
else
{
layer = "C";
}
bool IsFile = !Name.Contains("dummy") && decompresedSize > 5;
if (IsFile)
{
FileEntries.Add(new FileEntry()
{
SDFParent = this,
FileName = Name,
FileBlockPath = BlockFilePath,
FilePath = Name,
FolderPath = PathFolder,
CompressedSizes = compressedSize,
DdsType = ddsType,
UseDDS = UseDDS,
DecompressedSize = decompresedSize,
PackageID = packageId,
Offset = packageOffset,
});
}
}
public void Unload()

View file

@ -413,15 +413,16 @@ namespace FirstPlugin
if (SelectedSection is ExternalFileData)
{
HexEditor editor = (HexEditor)bfresEditor.GetActiveEditor(typeof(HexEditor));
ArchiveFilePanel editor = (ArchiveFilePanel)LibraryGUI.Instance.GetActiveContent(typeof(ArchiveFilePanel));
if (editor == null)
{
editor = new HexEditor();
editor = new ArchiveFilePanel();
editor.Dock = DockStyle.Fill;
bfresEditor.LoadEditor(editor);
LibraryGUI.Instance.LoadEditor(editor);
}
editor.Text = Text;
editor.LoadData(((ExternalFileData)SelectedSection).Data);
editor.LoadFile(((ExternalFileData)SelectedSection).ArchiveFileInfo);
editor.UpdateEditor();
return;
}
@ -781,6 +782,8 @@ namespace FirstPlugin
((BNTX)node).Unload();
}
Nodes.Clear();
GC.SuppressFinalize(this);
}
public byte[] Save()

View file

@ -9,23 +9,106 @@ namespace Bfres.Structs
{
public class ExternalFileData : STGenericWrapper
{
public ArchiveFileInfo ArchiveFileInfo;
public override string ExportFilter => "All files (*.*)|*.*";
//Format to attach
public IFileFormat FileFormat;
public byte[] Data
{
get
{
return ArchiveFileInfo.FileData;
}
set
{
ArchiveFileInfo.FileData = value;
}
}
public byte[] Data;
public ExternalFileData(string name, byte[] data)
{
ImageKey = "folder";
Text = name;
Data = data;
CanDelete = true;
CanRename = true;
CanReplace = true;
CanExport = true;
ArchiveFileInfo = new ArchiveFileInfo();
ArchiveFileInfo.FileData = data;
}
//Todo move all of this data into one single class
//Using ArchiveFileWrapper would be used wrong due to requring an IArchiveFile
public override void OnDoubleMouseClick(TreeView treeview)
{
IFileFormat file = ArchiveFileInfo.OpenFile();
if (file == null) //Format not supported so return
return;
ArchiveFileInfo.FileFormat = file;
if (Utils.HasInterface(file.GetType(), typeof(IEditor<>)))
{
OpenFormDialog(file);
}
else if (file != null && file is TreeNodeFile)
ReplaceNode(this, (TreeNodeFile)file);
}
public static void ReplaceNode(TreeNode replaceNode, TreeNodeFile NewNode)
{
if (NewNode == null)
return;
// node.Nodes.RemoveAt(index);
// node.Nodes.Insert(index, NewNode);
NewNode.ImageKey = replaceNode.ImageKey;
NewNode.SelectedImageKey = replaceNode.SelectedImageKey;
NewNode.Text = replaceNode.Text;
}
private void OpenFormDialog(IFileFormat fileFormat)
{
UserControl form = GetEditorForm(fileFormat);
form.Text = (((IFileFormat)fileFormat).FileName);
var parentForm = LibraryGUI.Instance.GetActiveForm();
GenericEditorForm editorForm = new GenericEditorForm(true, form);
editorForm.FormClosing += (sender, e) => FormClosing(sender, e, fileFormat);
if (editorForm.ShowDialog() == DialogResult.OK)
{
if (fileFormat.CanSave)
{
ArchiveFileInfo.FileData = fileFormat.Save();
UpdateEditor();
}
}
}
private void FormClosing(object sender, EventArgs args, IFileFormat fileFormat)
{
if (((Form)sender).DialogResult != DialogResult.OK)
return;
}
public UserControl GetEditorForm(IFileFormat fileFormat)
{
Type objectType = fileFormat.GetType();
foreach (var inter in objectType.GetInterfaces())
{
if (inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IEditor<>))
{
System.Reflection.MethodInfo method = objectType.GetMethod("OpenForm");
return (UserControl)method.Invoke(fileFormat, new object[0]);
}
}
return null;
}
public override void OnClick(TreeView treeview) {

View file

@ -193,6 +193,8 @@ namespace FirstPlugin
if (PluginRuntime.bntxContainers.Contains(this))
PluginRuntime.bntxContainers.Remove(this);
GC.SuppressFinalize(this);
}
public static byte[] CreateNewBNTX(string Name)
@ -225,6 +227,20 @@ namespace FirstPlugin
Textures.Remove(textureData.Text);
LibraryGUI.Instance.UpdateViewport();
}
public override UserControl GetEditor()
{
STPropertyGrid editor = new STPropertyGrid();
editor.Text = Text;
editor.Dock = DockStyle.Fill;
return editor;
}
public override void FillEditor(UserControl control)
{
((STPropertyGrid)control).LoadProperty(BinaryTexFile, OnPropertyChanged);
}
public override void OnClick(TreeView treeView)
{
if (Parent != null && Parent is BFRES)

View file

@ -69,17 +69,17 @@ namespace FirstPlugin
public List<GTXDataBlock> blocks = new List<GTXDataBlock>();
public override void OnClick(TreeView treeview)
public override UserControl GetEditor()
{
STPropertyGrid editor = (STPropertyGrid)LibraryGUI.Instance.GetActiveContent(typeof(STPropertyGrid));
if (editor == null)
{
editor = new STPropertyGrid();
LibraryGUI.Instance.LoadEditor(editor);
}
STPropertyGrid editor = new STPropertyGrid();
editor.Text = Text;
editor.Dock = DockStyle.Fill;
editor.LoadProperty(header, null);
return editor;
}
public override void FillEditor(UserControl control)
{
((STPropertyGrid)control).LoadProperty(header);
}
public void Load(System.IO.Stream stream)

View file

@ -318,12 +318,12 @@ namespace FirstPlugin
Formats.Add(typeof(Turbo.Course_MapCamera_bin));
Formats.Add(typeof(Turbo.PartsBIN));
Formats.Add(typeof(SDF));
//Unfinished wip formats not ready for use
if (Runtime.DEVELOPER_DEBUG_MODE)
{
Formats.Add(typeof(GFBMDL));
Formats.Add(typeof(SDF));
Formats.Add(typeof(NCA));
Formats.Add(typeof(XCI));
Formats.Add(typeof(BFLAN));

View file

@ -766,10 +766,10 @@ namespace Switch_Toolbox.Library
ArrayCount = DX10header.arrayFlag;
}
public bool Swizzle = false;
public bool SwitchSwizzle = false;
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0)
{
if (Swizzle)
if (SwitchSwizzle)
return TegraX1Swizzle.GetImageData(this, bdata, ArrayLevel, MipLevel);
return GetArrayFaces(this, ArrayCount)[ArrayLevel].mipmaps[MipLevel];

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
namespace Switch_Toolbox.Library
{
public class ZIP : TreeNodeFile, IFileFormat
{
const int MagicFileConstant = 0x504B0304;
public FileType FileType { get; set; } = FileType.Archive;
public bool CanSave { get; set; }
public string[] Description { get; set; } = new string[] { "ZIP" };
public string[] Extension { get; set; } = new string[] { "*.zip" };
public string FileName { get; set; }
public string FilePath { get; set; }
public IFileInfo IFileInfo { get; set; }
public bool Identify(System.IO.Stream stream)
{
using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true))
{
reader.SetByteOrder(true);
return reader.ReadInt32() == MagicFileConstant;
}
}
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
return types.ToArray();
}
}
public void Load(System.IO.Stream stream)
{
}
public byte[] Save()
{
return null;
}
public void Unload()
{
}
}
}

View file

@ -52,7 +52,6 @@ namespace Switch_Toolbox.Library.Forms
return null;
}
public void UpdateEditor()
{
if (GetEditor() == 0)
@ -65,18 +64,25 @@ namespace Switch_Toolbox.Library.Forms
private void UpdateFileEditor()
{
if (!ArchiveFileInfo.IsSupportedFileFormat())
{
AddControl(new STUserControl());
return;
}
var File = ArchiveFileInfo.FileFormat;
if (File == null)
if (File == null) //If the file is not open yet, try temporarily for a preview
File = ArchiveFileInfo.OpenFile();
UserControl control = GetEditorForm(File);
if (control != null)
{
AddControl(control);
// if (CheckActiveType(control.GetType()))
// AddControl(control);
}
//If the format isn't active we can just dispose it
if (ArchiveFileInfo.FileFormat == null)
File.Unload();
}
private bool CheckActiveType(Type type)
@ -89,13 +95,30 @@ namespace Switch_Toolbox.Library.Forms
if (fileFormat == null)
return new STUserControl() { Dock = DockStyle.Fill };
if (fileFormat is TreeNodeFile)
{
var Editor = ((TreeNodeFile)fileFormat).GetEditor();
var ActiveEditor = GetActiveEditor(Editor.GetType());
if (ActiveEditor != null)
Editor = ActiveEditor;
((TreeNodeFile)fileFormat).FillEditor(Editor);
return Editor;
}
Type objectType = fileFormat.GetType();
foreach (var inter in objectType.GetInterfaces())
{
if (inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IEditor<>))
{
System.Reflection.MethodInfo method = objectType.GetMethod("OpenForm");
return (UserControl)method.Invoke(fileFormat, new object[0]);
var Editor = (UserControl)method.Invoke(fileFormat, new object[0]);
var ActiveEditor = GetActiveEditor(Editor.GetType());
if (ActiveEditor != null)
Editor = ActiveEditor;
return Editor;
}
}
return null;

View file

@ -22,6 +22,7 @@ namespace Switch_Toolbox.Library
{
}
}
public class TreeNodeFile : TreeNodeCustom
{
public bool CanDelete
@ -38,6 +39,23 @@ namespace Switch_Toolbox.Library
}
}
public virtual UserControl GetEditor() { return new STUserControl(); }
public virtual void FillEditor(UserControl control) { }
public override void OnClick(TreeView treeview)
{
var Editor = GetEditor();
Editor.Dock = DockStyle.Fill;
var ActiveEditor = LibraryGUI.Instance.GetActiveContent(Editor.GetType());
if (ActiveEditor != null)
Editor = ActiveEditor;
else
LibraryGUI.Instance.LoadEditor(Editor);
FillEditor(Editor);
}
public TreeNodeFile()
{

View file

@ -77,13 +77,13 @@
this.findToolStripMenuItem.Text = "Find";
this.findToolStripMenuItem.Click += new System.EventHandler(this.findToolStripMenuItem_Click);
//
// HexEditor
// HexEditor2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.hexBox1);
this.Controls.Add(this.stContextMenuStrip1);
this.Name = "HexEditor";
this.Name = "HexEditor2";
this.Size = new System.Drawing.Size(529, 454);
this.stContextMenuStrip1.ResumeLayout(false);
this.stContextMenuStrip1.PerformLayout();

View file

@ -11,10 +11,38 @@ using Be.Windows.Forms;
namespace Switch_Toolbox.Library.Forms
{
public partial class HexEditor : UserControl
public partial class HexEditor : STUserControl
{
FindOptions _findOptions = new FindOptions();
public HexEditor()
{
InitializeComponent();
hexBox1.BackColor = FormThemes.BaseTheme.FormBackColor;
hexBox1.ForeColor = FormThemes.BaseTheme.FormForeColor;
hexBox1.SelectionBackColor = FormThemes.BaseTheme.FormContextMenuSelectColor;
hexBox1.SelectionForeColor = FormThemes.BaseTheme.FormForeColor;
}
public override void OnControlClosing()
{
Cleanup();
}
private void Cleanup()
{
if (hexBox1.ByteProvider != null)
{
hexBox1.ByteProvider.DeleteBytes(0, hexBox1.ByteProvider.Length);
IDisposable byteProvider = hexBox1.ByteProvider as IDisposable;
if (byteProvider != null)
byteProvider.Dispose();
hexBox1.ByteProvider = null;
}
}
public bool EnableMenuBar
{
set
@ -30,18 +58,12 @@ namespace Switch_Toolbox.Library.Forms
}
}
public HexEditor()
{
InitializeComponent();
hexBox1.BackColor = FormThemes.BaseTheme.FormBackColor;
hexBox1.ForeColor = FormThemes.BaseTheme.FormForeColor;
hexBox1.SelectionBackColor = FormThemes.BaseTheme.FormContextMenuSelectColor;
hexBox1.SelectionForeColor = FormThemes.BaseTheme.FormForeColor;
}
public void LoadData(byte[] data)
{
hexBox1.ByteProvider = new DynamicByteProvider(data);
Cleanup();
IByteProvider provider = new DynamicByteProvider(data);
hexBox1.ByteProvider = provider;
}
private void findToolStripMenuItem_Click(object sender, EventArgs e)

View file

@ -0,0 +1,56 @@
namespace Switch_Toolbox.Library.Forms.Old
{
partial class HexEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.stPanel1 = new Switch_Toolbox.Library.Forms.STPanel();
this.SuspendLayout();
//
// stPanel1
//
this.stPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.stPanel1.Location = new System.Drawing.Point(0, 0);
this.stPanel1.Name = "stPanel1";
this.stPanel1.Size = new System.Drawing.Size(529, 454);
this.stPanel1.TabIndex = 2;
//
// HexEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.stPanel1);
this.Name = "HexEditor";
this.Size = new System.Drawing.Size(529, 454);
this.ResumeLayout(false);
}
#endregion
private STPanel stPanel1;
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.ComponentModel.Design;
using System.Windows.Forms;
using Be.Windows.Forms;
namespace Switch_Toolbox.Library.Forms.Old
{
public partial class HexEditor : STUserControl
{
FindOptions _findOptions = new FindOptions();
ByteViewer ByteViewer;
public HexEditor()
{
InitializeComponent();
ByteViewer = new ByteViewer();
ByteViewer.BackColor = FormThemes.BaseTheme.FormBackColor;
ByteViewer.ForeColor = FormThemes.BaseTheme.FormForeColor;
ByteViewer.Dock = DockStyle.Fill;
stPanel1.Controls.Add(ByteViewer);
}
public override void OnControlClosing()
{
Cleanup();
ByteViewer.Dispose();
}
private void Cleanup()
{
}
public void LoadData(byte[] data)
{
Cleanup();
ByteViewer.SetBytes(data);
}
private void findToolStripMenuItem_Click(object sender, EventArgs e)
{
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -114,52 +114,63 @@ namespace Switch_Toolbox.Library.Forms
{
var rootText = root.Text;
var rootTextLength = rootText.Length;
var nodeStrings = archiveFile.Files;
foreach (var node in nodeStrings)
var nodeFiles = archiveFile.Files;
if (nodeFiles.Count() > 400)
{
string nodeString = node.FileName;
var roots = nodeString.Split(new char[] { '/' },
StringSplitOptions.RemoveEmptyEntries);
// The initial parent is the root node
var parentNode = root;
var sb = new StringBuilder(rootText, nodeString.Length + rootTextLength);
for (int rootIndex = 0; rootIndex < roots.Length; rootIndex++)
foreach (var node in nodeFiles)
{
// Build the node name
var parentName = roots[rootIndex];
sb.Append("/");
sb.Append(parentName);
var nodeName = sb.ToString();
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(node.FileName, node, archiveFile);
root.Nodes.Add(wrapperFile);
}
}
else
{
foreach (var node in nodeFiles)
{
string nodeString = node.FileName;
// Search for the node
var index = parentNode.Nodes.IndexOfKey(nodeName);
if (index == -1)
var roots = nodeString.Split(new char[] { '/' },
StringSplitOptions.RemoveEmptyEntries);
// The initial parent is the root node
var parentNode = root;
var sb = new StringBuilder(rootText, nodeString.Length + rootTextLength);
for (int rootIndex = 0; rootIndex < roots.Length; rootIndex++)
{
// Node was not found, add it
// Build the node name
var parentName = roots[rootIndex];
sb.Append("/");
sb.Append(parentName);
var nodeName = sb.ToString();
var folder = new ArchiveFolderNodeWrapper(parentName, archiveFile);
if (rootIndex == roots.Length - 1)
// Search for the node
var index = parentNode.Nodes.IndexOfKey(nodeName);
if (index == -1)
{
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, archiveFile);
wrapperFile.Name = nodeName;
parentNode.Nodes.Add(wrapperFile);
parentNode = wrapperFile;
// Node was not found, add it
var folder = new ArchiveFolderNodeWrapper(parentName, archiveFile);
if (rootIndex == roots.Length - 1)
{
ArchiveFileWrapper wrapperFile = new ArchiveFileWrapper(parentName, node, archiveFile);
wrapperFile.Name = nodeName;
parentNode.Nodes.Add(wrapperFile);
parentNode = wrapperFile;
}
else
{
folder.Name = nodeName;
parentNode.Nodes.Add(folder);
parentNode = folder;
}
}
else
{
folder.Name = nodeName;
parentNode.Nodes.Add(folder);
parentNode = folder;
// Node was found, set that as parent and continue
parentNode = parentNode.Nodes[index];
}
}
else
{
// Node was found, set that as parent and continue
parentNode = parentNode.Nodes[index];
}
}
}
}

View file

@ -40,12 +40,32 @@ namespace Switch_Toolbox.Library
public FileType FileDataType = FileType.Default;
public IFileFormat OpenFile()
public virtual IFileFormat OpenFile()
{
return STFileLoader.OpenFileFormat(
IOExtensions.RemoveIllegaleFolderNameCharacters(FileName), FileData, true);
}
public bool IsSupportedFileFormat()
{
if (FileData == null || FileData.Length <= 4)
return false;
using (var stream = new MemoryStream(FileData))
{
foreach (IFileFormat fileFormat in FileManager.GetFileFormats())
{
fileFormat.FileName = FileName;
if (fileFormat.Identify(stream))
return true;
}
return false;
}
}
public virtual void Replace()
{
string fileName = Path.GetFileName(FileName.RemoveIllegaleFileNameCharacters());

View file

@ -35,6 +35,9 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="AssimpNet">
<HintPath>..\Toolbox\Lib\AssimpNet.dll</HintPath>
@ -224,6 +227,7 @@
<Compile Include="FileFormats\R4G4.cs" />
<Compile Include="FileFormats\SizeTables\RSTB.cs" />
<Compile Include="FileFormats\SizeTables\TPFileSizeTable.cs" />
<Compile Include="FileFormats\ZIP.cs" />
<Compile Include="Forms\Archive\ArchiveFilePanel.cs">
<SubType>UserControl</SubType>
</Compile>
@ -370,6 +374,12 @@
<DependentUpon>AudioPlayer.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Editors\Audio\AudioFile.cs" />
<Compile Include="Forms\Editors\HexEditor\HexEditorNew.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Forms\Editors\HexEditor\HexEditorNew.Designer.cs">
<DependentUpon>HexEditorNew.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Editors\HexEditor\HexEditor.cs">
<SubType>UserControl</SubType>
</Compile>
@ -780,6 +790,9 @@
<EmbeddedResource Include="Forms\Editors\CubeMapFaceCreator.resx">
<DependentUpon>CubeMapFaceCreator.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Editors\HexEditor\HexEditorNew.resx">
<DependentUpon>HexEditorNew.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Editors\HexEditor\HexEditor.resx">
<DependentUpon>HexEditor.cs</DependentUpon>
</EmbeddedResource>

View file

@ -6,6 +6,7 @@ using System.Runtime.Serialization.Formatters.Binary;
using System.Drawing;
using OpenTK;
using System.Reflection;
using Switch_Toolbox.Library.IO;
namespace Switch_Toolbox.Library
{
@ -135,6 +136,16 @@ namespace Switch_Toolbox.Library
}
return rv;
}
public static byte[] SubArray(Stream data, uint offset, uint length)
{
using (var reader = new FileReader(data))
{
reader.Position = offset;
return reader.ReadBytes((int)length);
}
}
public static byte[] SubArray(byte[] data, uint offset, uint length)
{
return data.Skip((int)offset).Take((int)length).ToArray();

View file

@ -20,7 +20,7 @@
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
<PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>