using System; using System.Collections.Generic; using System.Linq; using System.Text; using LibEveryFileExplorer.Files; using System.Drawing; using System.IO; using LibEveryFileExplorer.Files.SimpleFileSystem; using _3DS.UI; using LibEveryFileExplorer.IO; using LibEveryFileExplorer.IO.Serialization; namespace _3DS { public class SARC : FileFormat, IViewable, IWriteable { public SARC(byte[] Data) { EndianBinaryReaderEx er = new EndianBinaryReaderEx(new MemoryStream(Data), Endianness.LittleEndian); try { Header = new SARCHeader(er); SFat = new SFAT(er); SFnt = new SFNT(er); er.BaseStream.Position = Header.FileDataOffset; this.Data = er.ReadBytes((int)(Header.FileSize - Header.FileDataOffset)); } finally { er.Close(); } } public System.Windows.Forms.Form GetDialog() { return new SARCViewer(this); } public string GetSaveDefaultFileFilter() { return "Simple Archive (*.sarc)|*.sarc"; } public byte[] Write() { MemoryStream m = new MemoryStream(); EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian); Header.Write(er); SFat.Write(er); SFnt.Write(er); while ((er.BaseStream.Position % 128) != 0) er.Write((byte)0); er.Write(Data, 0, Data.Length); er.BaseStream.Position = 8; er.Write((UInt32)(er.BaseStream.Length)); byte[] result = m.ToArray(); er.Close(); return result; } public SARCHeader Header; public class SARCHeader { public SARCHeader() { Signature = "SARC"; HeaderSize = 0x14; Endianness = 0xFFFE;//0xFEFF; FileSize = 0; FileDataOffset = 0; Unknown = 0x0100; } public SARCHeader(EndianBinaryReaderEx er) { er.ReadObject(this); } public void Write(EndianBinaryWriter er) { er.Write(Signature, Encoding.ASCII, false); er.Write(HeaderSize); er.Endianness = LibEveryFileExplorer.IO.Endianness.BigEndian; er.Write(Endianness); if (Endianness == 0xFFFE) er.Endianness = LibEveryFileExplorer.IO.Endianness.LittleEndian; er.Write(FileSize); er.Write(FileDataOffset); er.Write(Unknown); } [BinaryStringSignature("SARC")] [BinaryFixedSize(4)] public String Signature; public UInt16 HeaderSize; [BinaryBOM(0xFFFE)] public UInt16 Endianness; public UInt32 FileSize; public UInt32 FileDataOffset; public UInt32 Unknown; } public SFAT SFat; public class SFAT { public SFAT() { Signature = "SFAT"; HeaderSize = 0xC; NrEntries = 0; HashMultiplier = 0x65; Entries = new List(); } public SFAT(EndianBinaryReaderEx er) { Signature = er.ReadString(Encoding.ASCII, 4); if (Signature != "SFAT") throw new SignatureNotCorrectException(Signature, "SFAT", er.BaseStream.Position - 4); HeaderSize = er.ReadUInt16(); NrEntries = er.ReadUInt16(); HashMultiplier = er.ReadUInt32(); Entries = new List(); for (int i = 0; i < NrEntries; i++) { Entries.Add(new SFATEntry(er)); } } public void Write(EndianBinaryWriter er) { NrEntries = (ushort)Entries.Count; er.Write(Signature, Encoding.ASCII, false); er.Write(HeaderSize); er.Write(NrEntries); er.Write(HashMultiplier); foreach (var v in Entries) v.Write(er); } public String Signature; public UInt16 HeaderSize; public UInt16 NrEntries; public UInt32 HashMultiplier; public List Entries; public class SFATEntry { public SFATEntry() { } public SFATEntry(EndianBinaryReaderEx er) { er.ReadObject(this); } public void Write(EndianBinaryWriter er) { er.Write(FileNameHash); er.Write(FileNameOffset); er.Write(FileDataStart); er.Write(FileDataEnd); } public UInt32 FileNameHash; public UInt32 FileNameOffset;//If filenames are available public UInt32 FileDataStart; public UInt32 FileDataEnd; } } public SFNT SFnt; public class SFNT { public SFNT() { Signature = "SFNT"; HeaderSize = 8; Unknown1 = 0; } public SFNT(EndianBinaryReaderEx er) { Signature = er.ReadString(Encoding.ASCII, 4); if (Signature != "SFNT") throw new SignatureNotCorrectException(Signature, "SFNT", er.BaseStream.Position - 4); HeaderSize = er.ReadUInt16(); Unknown1 = er.ReadUInt16(); } public void Write(EndianBinaryWriter er) { er.Write(Signature, Encoding.ASCII, false); er.Write(HeaderSize); er.Write(Unknown1); } public String Signature; public UInt16 HeaderSize; public UInt16 Unknown1; } public byte[] Data; public byte[] GetFileDataByIndex(int Index) { if (SFat.Entries.Count <= Index) return null; SFAT.SFATEntry v = SFat.Entries[Index]; uint start = v.FileDataStart; uint length = v.FileDataEnd - start; byte[] Result = new byte[length]; Array.Copy(Data, start, Result, 0, length); return Result; } public byte[] GetFileDataByHash(UInt32 Hash) { foreach (SFAT.SFATEntry v in SFat.Entries) { if (v.FileNameHash == Hash) { uint start = v.FileDataStart; uint length = v.FileDataEnd - start; byte[] Result = new byte[length]; Array.Copy(Data, start, Result, 0, length); return Result; } } return null; } public UInt32 GetHashFromName(String Name) { return GetHashFromName(Name, SFat.HashMultiplier); } public static UInt32 GetHashFromName(String Name, UInt32 HashMultiplier) { uint res = 0; for (int i = 0; i < Name.Length; i++) { res = Name[i] + res * HashMultiplier; } return res; } public SFSDirectory ToFileSystem() { SFSDirectory Root = new SFSDirectory("/", true); foreach (var v in SFat.Entries) { var q = new SFSFile((int)v.FileNameHash, string.Format("0x{0:X8}", v.FileNameHash), Root); if (SARCHashTable.DefaultHashTable != null) { var vv = SARCHashTable.DefaultHashTable.GetEntryByHash(v.FileNameHash); if (vv != null) q.FileName = vv.Name; } q.Data = GetFileDataByHash(v.FileNameHash); Root.Files.Add(q); } return Root; } public void FromFileSystem(SFSDirectory Root) { Header = new SARCHeader(); SFat = new SFAT(); SFat.NrEntries = (ushort)Root.Files.Count; uint DataStart = 0; foreach (var v in Root.Files) { while ((DataStart % 128) != 0) DataStart++; uint hash; if (v.FileName == string.Format("0x{0:X8}", v.FileID)) hash = (uint)v.FileID; else hash = GetHashFromName(v.FileName); SFat.Entries.Add(new SFAT.SFATEntry() { FileDataStart = DataStart, FileDataEnd = (uint)(DataStart + v.Data.Length), FileNameHash = hash, FileNameOffset = 0 }); DataStart += (uint)v.Data.Length; } Data = new byte[DataStart]; int i = 0; foreach (var v in Root.Files) { Array.Copy(v.Data, 0, Data, SFat.Entries[i].FileDataStart, v.Data.Length); i++; } SFnt = new SFNT(); Header.FileSize = (uint)(0x14 + 0xC + SFat.NrEntries * 0x10 + 0x8); while ((Header.FileSize % 128) != 0) Header.FileSize++; Header.FileDataOffset = Header.FileSize; Header.FileSize += (uint)Data.Length; } public class SARCIdentifier : FileFormatIdentifier { public override string GetCategory() { return Category_Archives; } public override string GetFileDescription() { return "Simple Archive (SARC)"; } public override string GetFileFilter() { return "Simple Archive (*.sarc, *.szs)|*.sarc;*.szs"; } public override Bitmap GetIcon() { return null; } public override FormatMatch IsFormat(EFEFile File) { if (File.Data.Length > 20 && File.Data[0] == 'S' && File.Data[1] == 'A' && File.Data[2] == 'R' && File.Data[3] == 'C' && File.Data[0x14] == 'S' && File.Data[0x15] == 'F' && File.Data[0x16] == 'A' && File.Data[0x17] == 'T') return FormatMatch.Content; return FormatMatch.No; } } } }