diff --git a/File_Format_Library.zip b/File_Format_Library.zip new file mode 100644 index 00000000..b0125d7c Binary files /dev/null and b/File_Format_Library.zip differ diff --git a/File_Format_Library/FileFormats/Archives/RARC/RARC.cs b/File_Format_Library/FileFormats/Archives/RARC/RARC.cs new file mode 100644 index 00000000..a597ecd3 --- /dev/null +++ b/File_Format_Library/FileFormats/Archives/RARC/RARC.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Toolbox; +using System.Windows.Forms; +using System.IO; +using Toolbox.Library.Forms; +using Toolbox.Library; +using Toolbox.Library.IO; + +namespace FirstPlugin +{ + public class RARC : IArchiveFile, IFileFormat, IContextMenuNode + { + public FileType FileType { get; set; } = FileType.Archive; + + public bool CanSave { get; set; } + public string[] Description { get; set; } = new string[] { "RARC" }; + public string[] Extension { get; set; } = new string[] { "*.rarc", "*.arc", "*.crar", "*.yaz0" }; + public string FileName { get; set; } + public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + + public bool CanAddFiles { get; set; } + public bool CanRenameFiles { get; set; } = true; + public bool CanReplaceFiles { get; set; } + public bool CanDeleteFiles { get; set; } + + public bool Identify(System.IO.Stream stream) + { + using (var reader = new Toolbox.Library.IO.FileReader(stream, true)) + { + return reader.CheckSignature(4, "RARC") || reader.CheckSignature(4, "CRAR"); + } + } + + public Type[] Types + { + get + { + List types = new List(); + return types.ToArray(); + } + } + + RARC_Parser RarcFileData; + + public IEnumerable Files => RarcFileData.Files; + + public void ClearFiles() { RarcFileData.Files.Clear(); } + + public ToolStripItem[] GetContextMenuItems() + { + List Items = new List(); + Items.Add(new ToolStripMenuItem("Save", null, SaveAction, Keys.Control | Keys.S)); + Items.Add(new ToolStripMenuItem("Batch Rename Galaxy (Mario Galaxy)", null, BatchRenameGalaxy, Keys.Control | Keys.S)); + return Items.ToArray(); + } + + private void BatchRenameGalaxy(object sender, EventArgs args) + { + string ActorName = Path.GetFileNameWithoutExtension(FileName); + + RenameDialog dialog = new RenameDialog(); + dialog.SetString(ActorName); + + if (dialog.ShowDialog() == DialogResult.OK) + { + string NewActorName = dialog.textBox1.Text; + FileName = NewActorName + ".arc"; + + foreach (var file in RarcFileData.Files) + { + file.FileName = file.FileName.Replace(ActorName, NewActorName); + file.UpdateWrapper(); + } + } + } + + private void SaveAction(object sender, EventArgs args) + { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.Filter = Utils.GetAllFilters(this); + sfd.FileName = FileName; + + if (sfd.ShowDialog() == DialogResult.OK) + { + STFileSaver.SaveFileFormat(this, sfd.FileName); + } + } + + public bool IsLittleEndian { get; set; } = false; + + public void Load(System.IO.Stream stream) + { + CanSave = true; + CanRenameFiles = true; + CanReplaceFiles = true; + + RarcFileData= new RARC_Parser(stream); + } + + public void Save(System.IO.Stream stream) + { + RarcFileData.Save(stream); + } + + public void Unload() { } + + public bool AddFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + + public bool DeleteFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + } +} diff --git a/File_Format_Library/FileFormats/Archives/RARC.cs b/File_Format_Library/FileFormats/Archives/RARC/RARC_Parser.cs similarity index 57% rename from File_Format_Library/FileFormats/Archives/RARC.cs rename to File_Format_Library/FileFormats/Archives/RARC/RARC_Parser.cs index cee54d30..7a2eae6f 100644 --- a/File_Format_Library/FileFormats/Archives/RARC.cs +++ b/File_Format_Library/FileFormats/Archives/RARC/RARC_Parser.cs @@ -1,150 +1,40 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; -using Toolbox; -using System.Windows.Forms; -using System.IO; -using Toolbox.Library.Forms; using Toolbox.Library; using Toolbox.Library.IO; namespace FirstPlugin { - public class RARC : IArchiveFile, IFileFormat, IDirectoryContainer, IContextMenuNode + public class RARC_Parser { - public FileType FileType { get; set; } = FileType.Archive; - - public bool CanSave { get; set; } - public string[] Description { get; set; } = new string[] { "RARC" }; - public string[] Extension { get; set; } = new string[] { "*.rarc", "*.arc", "*.crar", "*.yaz0" }; - public string FileName { get; set; } - public string FilePath { get; set; } - public IFileInfo IFileInfo { get; set; } - - public bool CanAddFiles { get; set; } - public bool CanRenameFiles { get; set; } = true; - public bool CanReplaceFiles { get; set; } - public bool CanDeleteFiles { get; set; } - - public bool Identify(System.IO.Stream stream) - { - using (var reader = new Toolbox.Library.IO.FileReader(stream, true)) - { - return reader.CheckSignature(4, "RARC") || reader.CheckSignature(4, "CRAR"); - } - } - - public Type[] Types - { - get - { - List types = new List(); - return types.ToArray(); - } - } - - public List files = new List(); - public List nodes = new List(); - - public IEnumerable Files => files; - public IEnumerable Nodes => nodes; - - public void ClearFiles() { files.Clear(); } - - public string Name - { - get { return FileName; } - set { FileName = value; } - } + public List Files = new List(); private DirectoryEntry[] Directories; + public bool IsLittleEndian = false; + private uint HeaderSize = 32; private uint Unknown = 256; - public RamAllocation RamType = RamAllocation.MRAM; - - public enum RamAllocation + public RARC_Parser(Stream stream) { - None, - ARAM, - MRAM, - } - - public ToolStripItem[] GetContextMenuItems() - { - List Items = new List(); - Items.Add(new ToolStripMenuItem("Save", null, SaveAction, Keys.Control | Keys.S)); - Items.Add(new ToolStripMenuItem("Batch Rename Galaxy (Mario Galaxy)", null, BatchRenameGalaxy, Keys.Control | Keys.S)); - return Items.ToArray(); - } - - private void BatchRenameGalaxy(object sender, EventArgs args) - { - string ActorName = Path.GetFileNameWithoutExtension(FileName); - - RenameDialog dialog = new RenameDialog(); - dialog.SetString(ActorName); - - if (dialog.ShowDialog() == DialogResult.OK) - { - string NewActorName = dialog.textBox1.Text; - FileName = NewActorName + ".arc"; - - foreach (var file in files) - { - file.FileName = file.FileName.Replace(ActorName, NewActorName); - file.UpdateWrapper(); - } - } - } - - private void SaveAction(object sender, EventArgs args) - { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.Filter = Utils.GetAllFilters(this); - sfd.FileName = FileName; - - if (sfd.ShowDialog() == DialogResult.OK) - { - STFileSaver.SaveFileFormat(this, sfd.FileName); - } - } - - public bool IsLittleEndian { get; set; } = false; - - public void Load(System.IO.Stream stream) - { - CanSave = true; - CanRenameFiles = true; - CanReplaceFiles = true; - - files.Clear(); - - using (var reader = new FileReader(stream)) - { - _savedDirectories.Clear(); - - reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; + using (var reader = new FileReader(stream)) { + reader.SetByteOrder(true); string signature = reader.ReadString(4, Encoding.ASCII); if (signature == "CRAR") { IsLittleEndian = true; - reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; + reader.SetByteOrder(false); } + uint FileSize = reader.ReadUInt32(); HeaderSize = reader.ReadUInt32(); uint DataOffset = reader.ReadUInt32(); uint FileDataSize = reader.ReadUInt32(); - uint MRamSize = reader.ReadUInt32(); - uint ARamSize = reader.ReadUInt32(); - byte[] Padding = reader.ReadBytes(4); - - if (MRamSize != 0) - RamType |= RamAllocation.MRAM; - else if (ARamSize != 0) - RamType |= RamAllocation.ARAM; + uint EndOfFileOffset = reader.ReadUInt32(); + byte[] Padding = reader.ReadBytes(8); //Info Block long pos = reader.Position; @@ -159,79 +49,72 @@ namespace FirstPlugin Unknown = reader.ReadUInt16(); byte[] Padding2 = reader.ReadBytes(4); + reader.SeekBegin(DirectoryOffset); Directories = new DirectoryEntry[DirectoryCount]; - for (int dir = 0; dir < DirectoryCount; dir++) - Directories[dir] = new DirectoryEntry(this); - - Console.WriteLine($"DirectoryCount {DirectoryCount}"); - Console.WriteLine($"StringTablOffset {StringTablOffset}"); - - reader.SeekBegin(DirectoryOffset); for (int dir = 0; dir < DirectoryCount; dir++) { - Directories[dir].Read(reader); + Directories[dir] = new DirectoryEntry(); + Directories[dir].Identifier = reader.ReadUInt32(); + Directories[dir].NameOffset = reader.ReadUInt32(); + Directories[dir].Hash = reader.ReadUInt16(); + Directories[dir].NodeCount = reader.ReadUInt16(); + Directories[dir].FirstNodeIndex = reader.ReadUInt32(); + } + + for (int dir = 0; dir < DirectoryCount; dir++) { + uint NamePointer = StringTablOffset + Directories[dir].NameOffset; + Directories[dir].Name = ReadStringAtTable(reader, NamePointer); } for (int dir = 0; dir < DirectoryCount; dir++) { - uint NamePointer = StringTablOffset + Directories[dir].NameOffset; - Directories[dir].Name = ReadStringAtTable(reader, NamePointer); - - Console.WriteLine($"{ Directories[dir].Name } {dir}"); - for (int n = 0; n < Directories[dir].NodeCount; n++) { + //Seek to the child entry reader.SeekBegin(NodeOffset + ((n + Directories[dir].FirstNodeIndex) * 0x14)); FileEntry entry = new FileEntry(); entry.Read(reader, IsLittleEndian); - NamePointer = StringTablOffset + entry.NameOffset; + var NamePointer = StringTablOffset + entry.NameOffset; entry.Name = ReadStringAtTable(reader, NamePointer); - //Root and parent strings. Skip these unecessary nodes. + //Skip root strings if (entry.Name == "." || entry.Name == "..") continue; if (entry.IsDirectory) { - // Directories[entry.Offset].ID = entry.FileId; //0xFFFF or 0 Directories[dir].AddNode(Directories[entry.Offset]); _savedDirectories.Add(entry); } else { - using (reader.TemporarySeek(pos + DataOffset + entry.Offset, System.IO.SeekOrigin.Begin)) - { - entry.FileData = reader.ReadBytes((int)entry.Size); - } - entry.FileName = entry.Name; - files.Add(entry); + long dataPos = pos + DataOffset + entry.Offset; + entry.FileData = reader.getSection((int)dataPos, (int)entry.Size); + Files.Add(entry); Directories[dir].AddNode(entry); + + entry.FileName = GetFullDirectory(entry); + if (Utils.GetExtension(entry.FileName) == ".key") + entry.OpenFileFormatOnLoad = true; } } } - - this.Name = Directories[0].Name; - nodes.AddRange(Directories[0].Nodes); } } - private void LoadAllDirectoriesAndFiles(DirectoryEntry parentDir) + private string GetFullDirectory(INode file) { - for (int i = 0; i < parentDir.Children.Count; i++) - { - if (parentDir.Children[i] is DirectoryEntry) - { - //_savedDirectories.Add((DirectoryEntry)parentDir.nodes[i]); - } - else - _savedFiles.Add((FileEntry)parentDir.Children[i]); - } + if (file.Parent != null) + return GetFullDirectory(file.Parent) + "/" + file.Name; + else + return file.Name; + } - for (int i = 0; i < parentDir.Children.Count; i++) - { - if (parentDir.Children[i] is DirectoryEntry) - LoadAllDirectoriesAndFiles((DirectoryEntry)parentDir.Children[i]); + public void Save(Stream stream) + { + using (var writer = new FileWriter(stream)) { + SaveFile(writer); } } @@ -260,14 +143,12 @@ namespace FirstPlugin writer.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; writer.WriteSignature(IsLittleEndian ? "CRAR" : "RARC"); - writer.Write(uint.MaxValue); //FileSize writer.Write(HeaderSize); writer.Write(uint.MaxValue); //DataOffset writer.Write(uint.MaxValue); //File Size - writer.Write(0); //MRAM (saved later if used) - writer.Write(0); //ARAM (saved later if used) - writer.Seek(4); //padding + writer.Write(uint.MaxValue); //End of file + writer.Seek(8); //padding writer.SeekBegin(HeaderSize); long InfoPos = writer.Position; @@ -300,7 +181,7 @@ namespace FirstPlugin foreach (FileEntry entry in Files) entry.SaveFileFormat(); - writer.Align(32); + writer.Align(32); Console.WriteLine("Directories"); @@ -316,20 +197,11 @@ namespace FirstPlugin var dirEntry = (DirectoryEntry)_savedNodes[dir].Children[n]; int index = Directories.ToList().IndexOf(dirEntry); - writer.Write(dirEntry.ID); + writer.Write((ushort)0); writer.Write(dirEntry.Hash); - if (IsLittleEndian) - { - writer.Write((ushort)dirEntry.NameOffset); - writer.Write((byte)0); - writer.Write((byte)0x2); - } - else - { - writer.Write((byte)0x2); - writer.Write((byte)0); - writer.Write((ushort)dirEntry.NameOffset); - } + writer.Write((byte)0x2); + writer.Write((byte)0); + writer.Write((ushort)dirEntry.NameOffset); writer.Write((int)index); writer.Write((int)16); writer.Write((int)0); @@ -339,21 +211,12 @@ namespace FirstPlugin var fileEntry = (FileEntry)entry; writer.Write(fileEntry.FileId); writer.Write(fileEntry.Hash); - if (IsLittleEndian) - { - writer.Write((ushort)fileEntry.NameOffset); - writer.Write((byte)0); //Padding - writer.Write(fileEntry.Flags); - } - else - { - writer.Write(fileEntry.Flags); - writer.Write((byte)0); //Padding - writer.Write((ushort)fileEntry.NameOffset); - } + writer.Write(fileEntry.Flags); + writer.Write((byte)0); //Padding + writer.Write((ushort)fileEntry.NameOffset); fileEntry._dataOffsetPos = writer.Position; - writer.Write(0); + writer.Write(0); writer.Write((uint)fileEntry.FileData.Length); _savedFileData.Add(fileEntry.FileData); writer.Write(0); @@ -363,18 +226,9 @@ namespace FirstPlugin //Save parent and root nodes at end writer.Write((ushort)0); writer.Write((ushort)46); - if (IsLittleEndian) - { - writer.Write((ushort)0); - writer.Write((byte)0); - writer.Write((byte)2); - } - else - { - writer.Write((byte)2); - writer.Write((byte)0); - writer.Write((ushort)0); - } + writer.Write((byte)2); + writer.Write((byte)0); + writer.Write((ushort)0); writer.Write(dir); writer.Write(16); writer.Write(0); @@ -383,18 +237,9 @@ namespace FirstPlugin writer.Write((ushort)0); writer.Write((ushort)184); - if (IsLittleEndian) - { - writer.Write((ushort)2); - writer.Write((byte)0); - writer.Write((byte)2); - } - else - { - writer.Write((byte)2); - writer.Write((byte)0); - writer.Write((ushort)2); - } + writer.Write((byte)2); + writer.Write((byte)0); + writer.Write((ushort)2); writer.Write(rootIndex); writer.Write(16); writer.Write(0); @@ -404,21 +249,19 @@ namespace FirstPlugin long stringTablePos = writer.Position; WriteOffset(writer, 20, InfoPos); - foreach (var str in stringTable) - { + foreach (var str in stringTable) { writer.WriteString(str); } writer.Align(32); long stringTableSize = writer.Position - stringTablePos; var dataPos = writer.Position; - using (writer.TemporarySeek(pos + 12, System.IO.SeekOrigin.Begin)) - { + using (writer.TemporarySeek(pos + 12, System.IO.SeekOrigin.Begin)) { writer.Write((uint)(dataPos - InfoPos)); } uint dataOffset = 0; - SaveFileData(writer, ref dataOffset, Directories[0]); + SaveFileData(writer,ref dataOffset, Directories[0]); uint DataSize = (uint)(writer.Position - dataPos); writer.Align(32); @@ -426,28 +269,22 @@ namespace FirstPlugin uint EndFileSize = (uint)(writer.Position - dataPos); //Write data size - using (writer.TemporarySeek(pos + 16, System.IO.SeekOrigin.Begin)) - { + using (writer.TemporarySeek(pos + 16, System.IO.SeekOrigin.Begin)) { writer.Write((uint)DataSize); } - //Write ram size - if (RamType != RamAllocation.None) - { - using (writer.TemporarySeek(pos + (RamType.HasFlag(RamAllocation.MRAM) ? 20 : 24), System.IO.SeekOrigin.Begin)) { - writer.Write((uint)EndFileSize); - } + //Write end of file size + using (writer.TemporarySeek(pos + 20, System.IO.SeekOrigin.Begin)) { + writer.Write((uint)EndFileSize); } //Write file size - using (writer.TemporarySeek(pos + 0x4, System.IO.SeekOrigin.Begin)) - { + using (writer.TemporarySeek(pos + 0x4, System.IO.SeekOrigin.Begin)) { writer.Write((uint)writer.BaseStream.Length); } //Write string table size - using (writer.TemporarySeek(InfoPos + 16, System.IO.SeekOrigin.Begin)) - { + using (writer.TemporarySeek(InfoPos + 16, System.IO.SeekOrigin.Begin)) { writer.Write((uint)stringTableSize); } } @@ -468,10 +305,9 @@ namespace FirstPlugin } private void CreateStringTable(List stringList, - ref uint stringPos, DirectoryEntry parentDir) + ref uint stringPos, DirectoryEntry parentDir) { - if (!stringList.Contains(parentDir.Name)) - { + if (!stringList.Contains(parentDir.Name)) { parentDir.NameOffset = stringPos; stringList.Add(parentDir.Name); stringPos += (uint)parentDir.Name.Length + 1; @@ -479,15 +315,9 @@ namespace FirstPlugin for (int i = 0; i < parentDir.Children.Count; i++) { if (parentDir.Children[i] is FileEntry) - { ((FileEntry)parentDir.Children[i]).NameOffset = (ushort)stringPos; - ((FileEntry)parentDir.Children[i]).UpdateHash(); - } else - { ((DirectoryEntry)parentDir.Children[i]).NameOffset = (ushort)stringPos; - ((DirectoryEntry)parentDir.Children[i]).UpdateHash(); - } Console.WriteLine($"{parentDir.Children[i].Name} {stringPos}"); @@ -500,16 +330,14 @@ namespace FirstPlugin } //Saves data in recusive order - private void SaveFileData(FileWriter writer, ref uint dataPos, DirectoryEntry parentDir) + private void SaveFileData(FileWriter writer, ref uint dataPos, DirectoryEntry parentDir) { for (int i = 0; i < parentDir.Children.Count; i++) { - if (parentDir.Children[i] is FileEntry) - { + if (parentDir.Children[i] is FileEntry) { var entry = (FileEntry)parentDir.Children[i]; long pos = writer.Position; - using (writer.TemporarySeek(entry._dataOffsetPos, System.IO.SeekOrigin.Begin)) - { + using (writer.TemporarySeek(entry._dataOffsetPos, SeekOrigin.Begin)) { writer.Write((uint)dataPos); } writer.Write(entry.FileData); @@ -522,6 +350,25 @@ namespace FirstPlugin } } + private void LoadAllDirectoriesAndFiles(DirectoryEntry parentDir) + { + for (int i = 0; i < parentDir.Children.Count; i++) + { + if (parentDir.Children[i] is DirectoryEntry) + { + //_savedDirectories.Add((DirectoryEntry)parentDir.nodes[i]); + } + else + _savedFiles.Add((FileEntry)parentDir.Children[i]); + } + + for (int i = 0; i < parentDir.Children.Count; i++) + { + if (parentDir.Children[i] is DirectoryEntry) + LoadAllDirectoriesAndFiles((DirectoryEntry)parentDir.Children[i]); + } + } + private void WriteOffset(FileWriter writer, long Target, long RelativePosition) { long Position = writer.Position; @@ -531,10 +378,8 @@ namespace FirstPlugin } } - private string ReadStringAtTable(FileReader reader, uint NameOffset) - { - using (reader.TemporarySeek(NameOffset, System.IO.SeekOrigin.Begin)) - { + private static string ReadStringAtTable(FileReader reader, uint NameOffset) { + using (reader.TemporarySeek(NameOffset, System.IO.SeekOrigin.Begin)) { return reader.ReadZeroTerminatedString(); } } @@ -550,79 +395,36 @@ namespace FirstPlugin return Hash; } - private void CreateDirectoryEntry() + class DirectoryEntry : INode { - - } - - public class DirectoryEntry : IDirectoryContainer - { - public RARC ParentArchive { get; } - public string Name { get; set; } + public uint Identifier; internal uint NameOffset; //Relative to string table public ushort Hash { get; set; } public ushort NodeCount; public uint FirstNodeIndex { get; set; } - public ushort ID { get; set; } = 0xFFFF; - - public DirectoryEntry(RARC rarc) { ParentArchive = rarc; } - - public IEnumerable Nodes { get { return Children; } } public List Children = new List(); - public INode Parent { get; set; } public void AddNode(INode node) { - if (node is FileEntry) - ((FileEntry)node).Parent = this; - else - ((DirectoryEntry)node).Parent = this; - + node.Parent = this; Children.Add(node); } - public void Read(FileReader reader) - { - Identifier = reader.ReadUInt32(); - NameOffset = reader.ReadUInt32(); - Hash = reader.ReadUInt16(); - NodeCount = reader.ReadUInt16(); - FirstNodeIndex = reader.ReadUInt32(); - } - public void UpdateHash() { Hash = CalculateHash(Name); } } - public void Unload() + public class FileEntry : ArchiveFileInfo, INode { + public string Name { get; set; } - } + public INode Parent { get; set; } - public void Save(System.IO.Stream stream) - { - SaveFile(new FileWriter(stream)); - } - - public bool AddFile(ArchiveFileInfo archiveFileInfo) - { - return false; - } - - public bool DeleteFile(ArchiveFileInfo archiveFileInfo) - { - return false; - } - - public class FileEntry : ArchiveFileInfo - { - //According to this to determine directory or not - //https://github.com/LordNed/WArchive-Tools/blob/3c7fdefe54b4c7634a042847b7455de61705033f/ArchiveToolsLib/Archive/Archive.cs#L44 public bool IsDirectory { get { return (Flags & 2) >> 1 == 1; } } public ushort FileId { get; set; } @@ -635,13 +437,11 @@ namespace FirstPlugin internal long _dataOffsetPos; - public INode Parent { get; set; } - - public void Read(FileReader reader, bool IsLittleEndian) + public void Read(FileReader reader, bool isLittleEndian) { FileId = reader.ReadUInt16(); Hash = reader.ReadUInt16(); - if (IsLittleEndian) + if (isLittleEndian) { NameOffset = reader.ReadUInt16(); reader.Seek(1); //Padding @@ -657,10 +457,12 @@ namespace FirstPlugin Offset = reader.ReadUInt32(); Size = reader.ReadUInt32(); } + } - public void UpdateHash() { - Hash = CalculateHash(Name); - } + public interface INode + { + string Name { get; set; } + INode Parent { get; set; } } } } diff --git a/File_Format_Library/File_Format_Library.csproj b/File_Format_Library/File_Format_Library.csproj index ce36ab82..ad80efb1 100644 --- a/File_Format_Library/File_Format_Library.csproj +++ b/File_Format_Library/File_Format_Library.csproj @@ -225,6 +225,7 @@ + @@ -367,7 +368,7 @@ - +