mirror of
https://github.com/Gericom/EveryFileExplorer.git
synced 2025-06-18 17:05:33 -04:00
306 lines
7.6 KiB
C#
306 lines
7.6 KiB
C#
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<SARC.SARCIdentifier>, 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 = 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.Write(Endianness);
|
|
er.Write(FileSize);
|
|
er.Write(FileDataOffset);
|
|
er.Write(Unknown);
|
|
}
|
|
[BinaryStringSignature("SARC")]
|
|
[BinaryFixedSize(4)]
|
|
public String Signature;
|
|
public UInt16 HeaderSize;
|
|
//[BinaryBOM(0xFEFF)]
|
|
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<SFATEntry>();
|
|
}
|
|
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<SFATEntry>();
|
|
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<SFATEntry> 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;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|