Skip to content

Commit abd579b

Browse files
committed
Add storing full file hash on SARC files
1 parent 43c08c1 commit abd579b

3 files changed

Lines changed: 86 additions & 8 deletions

File tree

3DS/SARC.cs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public SARCHeader()
7373
Endianness = 0xFFFE;//0xFEFF;
7474
FileSize = 0;
7575
FileDataOffset = 0;
76-
Unknown = 0x0100;
76+
Version = 0x0100;
77+
Reserved = 0;
7778
}
7879
public SARCHeader(EndianBinaryReaderEx er)
7980
{
@@ -88,7 +89,8 @@ public void Write(EndianBinaryWriter er)
8889
if (Endianness == 0xFFFE) er.Endianness = LibEveryFileExplorer.IO.Endianness.LittleEndian;
8990
er.Write(FileSize);
9091
er.Write(FileDataOffset);
91-
er.Write(Unknown);
92+
er.Write(Version);
93+
er.Write(Reserved);
9294
}
9395
[BinaryStringSignature("SARC")]
9496
[BinaryFixedSize(4)]
@@ -98,7 +100,8 @@ public void Write(EndianBinaryWriter er)
98100
public UInt16 Endianness;
99101
public UInt32 FileSize;
100102
public UInt32 FileDataOffset;
101-
public UInt32 Unknown;
103+
public UInt16 Version;
104+
public UInt16 Reserved;
102105
}
103106

104107
public SFAT SFat;
@@ -192,17 +195,29 @@ public void Write(EndianBinaryWriter er)
192195
public bool CompressDuplicateFiles {
193196
get
194197
{
195-
return (Unknown1 & 1) == 1;
198+
return (Unknown1 & 1) != 0;
196199
}
197200
set
198201
{
199202
Unknown1 = (ushort)((Unknown1 & ~1) | (value ? 1 : 0));
200203
}
201204
}
205+
public bool HashEntireFile
206+
{
207+
get
208+
{
209+
return (Unknown1 & 2) != 0;
210+
}
211+
set
212+
{
213+
Unknown1 = (ushort)((Unknown1 & ~2) | (value ? 2 : 0));
214+
}
215+
}
202216
}
203217

204218
private class SARCDataBuilder
205219
{
220+
private bool HashFile = false;
206221
private class FileEntry
207222
{
208223
public long position;
@@ -225,12 +240,21 @@ public int GetHashCode(byte[] obj)
225240
}
226241
}
227242

228-
public SARCDataBuilder(bool duplicateFileCompress)
243+
public SARCDataBuilder(bool duplicateFileCompress, bool StoreFileHash)
229244
{
230245
DuplicateFileCompress = duplicateFileCompress;
231246
FileDataHashes = new Dictionary<byte[], FileEntry>(new ByteArrayComparer());
232247
MemoryStream = new MemoryStream();
233248
MD5Provider = MD5.Create();
249+
HashFile = StoreFileHash;
250+
if (HashFile) {
251+
// Make room for the hash
252+
for (int i = 0; i < 8; i++)
253+
MemoryStream.WriteByte(0);
254+
255+
while ((MemoryStream.Position % 128) != 0)
256+
MemoryStream.WriteByte(0);
257+
}
234258
}
235259

236260
public long Append(SFSFile file)
@@ -260,6 +284,39 @@ public long Append(SFSFile file)
260284
}
261285
}
262286

287+
public void UpdateFullFileHash()
288+
{
289+
if (!HashFile)
290+
return;
291+
292+
var position = MemoryStream.Position;
293+
294+
MemoryStream.Position = 128;
295+
UInt32 hash1 = 0, hash2 = 0;
296+
byte[] buffer = new byte[4];
297+
long size = MemoryStream.Length - 128;
298+
// Align size to the next multiple of 8,
299+
// 0 will be read for the padding bytes.
300+
size = (size + 7) & ~7L;
301+
for (int i = 0; i < size / sizeof(UInt32); i+=2)
302+
{
303+
buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0;
304+
MemoryStream.Read(buffer, 0, 4);
305+
var data1 = BitConverter.ToUInt32(buffer, 0);
306+
buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0;
307+
MemoryStream.Read(buffer, 0, 4);
308+
var data2 = BitConverter.ToUInt32(buffer, 0);
309+
310+
hash1 += data1 * 47U;
311+
hash2 += data2 * 47U;
312+
}
313+
314+
var finalHash = BitConverter.GetBytes((UInt64)hash1 | ((UInt64)hash2 << 32));
315+
MemoryStream.Position = 0;
316+
MemoryStream.Write(finalHash, 0, finalHash.Length);
317+
MemoryStream.Position = position;
318+
}
319+
263320
public byte[] Get()
264321
{
265322
return MemoryStream.ToArray();
@@ -360,7 +417,8 @@ public void FromFileSystem(SFSDirectory Root)
360417
SFat = new SFAT();
361418
SFat.NrEntries = (ushort)Root.Files.Count;
362419
bool CompressDuplicateFiles = SFnt != null ? SFnt.CompressDuplicateFiles : false;
363-
SARCDataBuilder DataBuilder = new SARCDataBuilder(CompressDuplicateFiles);
420+
bool HashEntireFile = SFnt != null ? SFnt.HashEntireFile : false;
421+
SARCDataBuilder DataBuilder = new SARCDataBuilder(CompressDuplicateFiles, HashEntireFile);
364422
Root.Files.Sort(delegate (SFSFile a, SFSFile b) {
365423
uint hasha = (uint)a.FileID;
366424
uint hashb = (uint)b.FileID;
@@ -372,9 +430,12 @@ public void FromFileSystem(SFSDirectory Root)
372430
long DataStart = DataBuilder.Append(v);
373431
SFat.Entries.Add(new SFAT.SFATEntry() { FileDataStart = (uint)DataStart, FileDataEnd = (uint)(DataStart + v.Data.Length), FileNameHash = hash, FileNameOffset = 0 });
374432
}
433+
if (HashEntireFile)
434+
DataBuilder.UpdateFullFileHash();
375435
Data = DataBuilder.Get();
376436
SFnt = new SFNT();
377437
SFnt.CompressDuplicateFiles = CompressDuplicateFiles;
438+
SFnt.HashEntireFile = HashEntireFile;
378439
Header.FileSize = (uint)(0x14 + 0xC + SFat.NrEntries * 0x10 + 0x8);
379440
while ((Header.FileSize % 128) != 0) Header.FileSize++;
380441
Header.FileDataOffset = Header.FileSize;

3DS/UI/SARCViewer.Designer.cs

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

3DS/UI/SARCViewer.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ private void SARCViewer_Load(object sender, EventArgs e)
4141
fileBrowser1.DeleteEnabled = false;
4242
fileBrowser1.RenameEnabled = false;
4343
fileBrowser1.UpdateDirectories(Root.GetTreeNodes());
44-
duplicateFileCompression.Checked = (Archive.SFnt.Unknown1 & 1) == 1;
44+
duplicateFileCompression.Checked = Archive.SFnt.CompressDuplicateFiles;
45+
storeFullFileHash.Checked = Archive.SFnt.HashEntireFile;
4546
}
4647

4748
private void fileBrowser1_OnDirectoryChanged(string Path)
@@ -221,5 +222,12 @@ private void RunHintProviders()
221222
hint(this);
222223
}
223224
}
225+
226+
private void storeFullFileHash_Click(object sender, EventArgs e)
227+
{
228+
storeFullFileHash.Checked = !storeFullFileHash.Checked;
229+
Archive.SFnt.HashEntireFile = storeFullFileHash.Checked;
230+
Archive.FromFileSystem(Root);
231+
}
224232
}
225233
}

0 commit comments

Comments
 (0)