using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Toolbox; using System.Windows.Forms; using Toolbox.Library; using Toolbox.Library.IO; namespace FirstPlugin { public class GAR : IArchiveFile, IFileFormat { public FileType FileType { get; set; } = FileType.Archive; public bool CanSave { get; set; } public string[] Description { get; set; } = new string[] { "Zelda/Grezzo Archive" }; public string[] Extension { get; set; } = new string[] { "*.zar", "*.gar" }; 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; } 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, "ZAR\x01") || reader.CheckSignature(4, "GAR\x02") || reader.CheckSignature(4, "GAR\x03") || reader.CheckSignature(4, "GAR\x04") || reader.CheckSignature(4, "GAR\x05"); } } public Type[] Types { get { List types = new List(); return types.ToArray(); } } public List files = new List(); public IEnumerable Files => files; public void ClearFiles() { files.Clear(); } public enum VersionMagic { ZAR1, //OOT3D GAR2, //MM3D GAR5, //LM3DS } public Header header; public void Load(System.IO.Stream stream) { header = new Header(); header.Read(new FileReader(stream), files); } public class Header { public VersionMagic Version; public string Signature; public uint FileSize; public ushort FileGroupCount; public ushort FileCount; public uint FileGroupOffset; public uint FileInfoOffset; public uint DataOffset; public string Codename; public List FileGroups = new List(); public List GarFileInfos = new List(); public void Read(FileReader reader, List files) { reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; Signature = reader.ReadString(4, Encoding.ASCII); switch (Signature) { case "ZAR\x01": Version = VersionMagic.ZAR1; break; case "GAR\x02": Version = VersionMagic.GAR2; break; case "GAR\x05": Version = VersionMagic.GAR5; break; } FileSize = reader.ReadUInt32(); FileGroupCount = reader.ReadUInt16(); FileCount = reader.ReadUInt16(); FileGroupOffset = reader.ReadUInt32(); FileInfoOffset = reader.ReadUInt32(); DataOffset = reader.ReadUInt32(); Codename = reader.ReadString(0x08); switch (Codename) { case "queen\0\0\0": case "jenkins\0": ReadZeldaArchive(reader, files); break; case "agora\0\0\0": case "SYSTEM\0\0": ReadSystemGrezzoArchive(reader, files); break; default: throw new Exception($"Unexpected codename! {Codename}"); } } public void Write(FileWriter writer) { writer.WriteSignature(Signature); writer.Write(uint.MaxValue); //file size writer.WriteSectionSizeU32(4, writer.BaseStream.Length); } private void ReadSystemGrezzoArchive(FileReader reader, List files) { reader.SeekBegin(FileGroupOffset); for (int i = 0; i < FileGroupCount; i++) { FileGroups.Add(new SystemFileGroup(reader)); reader.Align(0x20); } reader.BaseStream.Position = FileGroups.Min(x => ((SystemFileGroup)x).SubTableOffset); byte[] entries = reader.ReadBytes((int)FileInfoOffset - (int)reader.BaseStream.Position); reader.SeekBegin(FileInfoOffset); for (int i = 0; i < FileGroupCount; i++) { for (int f = 0; f < ((SystemFileGroup)FileGroups[i]).FileCount; f++) { GarFileInfos.Add(new SystemGarFileInfo(reader)); ((SystemGarFileInfo)GarFileInfos.Last()).ext = ((SystemFileGroup)FileGroups[i]).Name; } } reader.SeekBegin(DataOffset); for (int i = 0; i < GarFileInfos.Count; i++) { var info = (SystemGarFileInfo)GarFileInfos[i]; files.Add(new FileEntry() { OpenFileFormatOnLoad = info.ext == "csab", FileName = $"{info.Name}.{info.ext}", FileData = reader.getSection(info.FileOffset, info.FileSize) }); } } private void ReadZeldaArchive(FileReader reader, List files) { reader.SeekBegin(FileGroupOffset); for (int i = 0; i < FileGroupCount; i++) FileGroups.Add(new FileGroup(reader)); for (int i = 0; i < FileGroupCount; i++) ((FileGroup)FileGroups[i]).Ids = reader.ReadInt32s((int)((FileGroup)FileGroups[i]).FileCount); reader.SeekBegin(FileInfoOffset); for (int i = 0; i < FileGroupCount; i++) { for (int f = 0; f < ((FileGroup)FileGroups[i]).FileCount; f++) { if (Version == VersionMagic.ZAR1) GarFileInfos.Add(new ZarFileInfo(reader)); else GarFileInfos.Add(new GarFileInfo(reader)); } } reader.SeekBegin(DataOffset); uint[] Offsets = reader.ReadUInt32s(FileCount); for (int i = 0; i < GarFileInfos.Count; i++) { if (GarFileInfos[i] is ZarFileInfo) { files.Add(new FileEntry() { OpenFileFormatOnLoad = ((ZarFileInfo)GarFileInfos[i]).FileName.Contains("csab"), FileName = ((ZarFileInfo)GarFileInfos[i]).FileName, FileData = reader.getSection(Offsets[i], ((ZarFileInfo)GarFileInfos[i]).FileSize) }); } else { files.Add(new FileEntry() { OpenFileFormatOnLoad = ((GarFileInfo)GarFileInfos[i]).FileName.Contains("csab"), FileName = ((GarFileInfo)GarFileInfos[i]).FileName, FileData = reader.getSection(Offsets[i], ((GarFileInfo)GarFileInfos[i]).FileSize) }); } } } private void WriteZeldaArchive(FileWriter writer, List files) { for (int i = 0; i < FileGroups.Count; i++) { } } public interface IFileGroup { } public interface IFileInfo { } public class FileGroup : IFileGroup { public uint FileCount; public uint DataOffset; public uint InfoOffset; public int[] Ids; public FileGroup(FileReader reader) { FileCount = reader.ReadUInt32(); DataOffset = reader.ReadUInt32(); InfoOffset = reader.ReadUInt32(); reader.ReadUInt32(); //padding } } public class SystemFileGroup : IFileGroup { public uint FileCount; public uint Unknown; public uint InfoOffset; public string Name; public uint SubTableOffset; public int[] Ids; public SystemFileGroup(FileReader reader) { FileCount = reader.ReadUInt32(); Unknown = reader.ReadUInt32(); InfoOffset = reader.ReadUInt32(); Name = reader.LoadString(false, typeof(uint)); SubTableOffset = reader.ReadUInt32(); } } public class ZarFileInfo : IFileInfo { public uint FileSize; public string FileName; public ZarFileInfo(FileReader reader) { FileSize = reader.ReadUInt32(); FileName = reader.LoadString(false, typeof(uint)); } } public class GarFileInfo : IFileInfo { public uint FileSize; public string FileName; public string Name; public GarFileInfo(FileReader reader) { FileSize = reader.ReadUInt32(); Name = reader.LoadString(false, typeof(uint)); FileName = reader.LoadString(false, typeof(uint)); } } public class SystemGarFileInfo : IFileInfo { public uint FileSize; public uint FileOffset; public string FileName; public string Name; public string ext; public SystemGarFileInfo(FileReader reader) { FileSize = reader.ReadUInt32(); FileOffset = reader.ReadUInt32(); Name = reader.LoadString(false, typeof(uint)); reader.ReadUInt32();//padding } } } public void Unload() { } public void Save(System.IO.Stream stream) { } public bool AddFile(ArchiveFileInfo archiveFileInfo) { return false; } public bool DeleteFile(ArchiveFileInfo archiveFileInfo) { return false; } public class FileEntry : ArchiveFileInfo { public uint Unknown { get; set; } internal uint Size; internal uint Offset; internal uint NameOffset; public void Read(FileReader reader) { uint Unknown = reader.ReadUInt32(); NameOffset = reader.ReadUInt32(); Offset = reader.ReadUInt32(); Size = reader.ReadUInt32(); } } } }