mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
feat: support RepiPack v2
This commit is contained in:
@@ -44,6 +44,8 @@ namespace GameRes.Formats.Littlewitch
|
|||||||
internal class RepiEntry : PackedEntry
|
internal class RepiEntry : PackedEntry
|
||||||
{
|
{
|
||||||
public bool HasEncryptionKey;
|
public bool HasEncryptionKey;
|
||||||
|
public int Mode;
|
||||||
|
public uint[] LegacyKey;
|
||||||
|
|
||||||
public byte[] CreateKey ()
|
public byte[] CreateKey ()
|
||||||
{
|
{
|
||||||
@@ -81,7 +83,9 @@ namespace GameRes.Formats.Littlewitch
|
|||||||
if (!file.View.AsciiEqual (4, "Pack"))
|
if (!file.View.AsciiEqual (4, "Pack"))
|
||||||
return null;
|
return null;
|
||||||
int version = file.View.ReadInt32 (8);
|
int version = file.View.ReadInt32 (8);
|
||||||
if (version != 5)
|
if (version == 2 || version == 3)
|
||||||
|
return OpenV2 (file);
|
||||||
|
else if (version != 5)
|
||||||
return null;
|
return null;
|
||||||
uint name_length = file.View.ReadUInt32 (0xC);
|
uint name_length = file.View.ReadUInt32 (0xC);
|
||||||
if (name_length < 4)
|
if (name_length < 4)
|
||||||
@@ -106,6 +110,7 @@ namespace GameRes.Formats.Littlewitch
|
|||||||
entry.UnpackedSize = index.ToUInt32 (pos+0x18);
|
entry.UnpackedSize = index.ToUInt32 (pos+0x18);
|
||||||
if (!entry.CheckPlacement (file.MaxOffset))
|
if (!entry.CheckPlacement (file.MaxOffset))
|
||||||
return null;
|
return null;
|
||||||
|
entry.Mode = -1;
|
||||||
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||||
dir.Add (entry);
|
dir.Add (entry);
|
||||||
pos += 0x20;
|
pos += 0x20;
|
||||||
@@ -113,15 +118,62 @@ namespace GameRes.Formats.Littlewitch
|
|||||||
return new ArcFile (file, this, dir);
|
return new ArcFile (file, this, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArcFile OpenV2 (ArcView file)
|
||||||
|
{
|
||||||
|
uint name_length = file.View.ReadUInt32 (0xC);
|
||||||
|
if (name_length < 4)
|
||||||
|
return null;
|
||||||
|
uint name_key = file.View.ReadUInt32 (0x10);
|
||||||
|
var key = FindKey (file.Name, name_key);
|
||||||
|
if (null == key)
|
||||||
|
return null;
|
||||||
|
int count = file.View.ReadInt32 (0x10 + name_length);
|
||||||
|
if (!IsSaneCount (count))
|
||||||
|
return null;
|
||||||
|
var index = file.View.ReadBytes (0x14 + name_length, (uint)count * 0x50);
|
||||||
|
int pos = 0;
|
||||||
|
var dir = new List<Entry> (count);
|
||||||
|
var name_builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
DecryptData (index, pos, 0x50, key[2], key[1]);
|
||||||
|
var name = Binary.GetCString (index, pos, 0x40);
|
||||||
|
var entry = FormatCatalog.Instance.Create<RepiEntry> (name);
|
||||||
|
entry.Offset = index.ToUInt32 (pos+0x40);
|
||||||
|
entry.UnpackedSize = index.ToUInt32 (pos+0x44);
|
||||||
|
entry.Size = index.ToUInt32 (pos+0x48);
|
||||||
|
if (!entry.CheckPlacement (file.MaxOffset))
|
||||||
|
return null;
|
||||||
|
entry.Mode = index.ToInt32 (pos+0x4C);
|
||||||
|
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||||
|
entry.HasEncryptionKey = true;
|
||||||
|
entry.LegacyKey = key;
|
||||||
|
dir.Add (entry);
|
||||||
|
pos += 0x50;
|
||||||
|
}
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
|
|
||||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
{
|
{
|
||||||
var rent = entry as RepiEntry;
|
var rent = entry as RepiEntry;
|
||||||
if (null == rent || !rent.HasEncryptionKey)
|
if (null == rent || !rent.HasEncryptionKey)
|
||||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
var key = rent.CreateKey();
|
var key = rent.CreateKey();
|
||||||
uint enc_length = Math.Min ((uint)key.Length, entry.Size);
|
uint enc_length = rent.Mode == -1 ? Math.Min ((uint)key.Length, rent.Size) : rent.Size;
|
||||||
byte[] encrypted = arc.File.View.ReadBytes (entry.Offset, enc_length);
|
byte[] encrypted = arc.File.View.ReadBytes (entry.Offset, enc_length);
|
||||||
DecryptEntry (encrypted, key);
|
switch (rent.Mode)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
DecryptEntry (encrypted, key);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
DecryptData (encrypted, 0, (int)enc_length, rent.LegacyKey[2], rent.LegacyKey[1]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
DecryptData2 (encrypted, 0, (int)enc_length, (byte)rent.LegacyKey[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
Stream input;
|
Stream input;
|
||||||
if (enc_length == entry.Size)
|
if (enc_length == entry.Size)
|
||||||
{
|
{
|
||||||
@@ -163,6 +215,28 @@ namespace GameRes.Formats.Littlewitch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsafe void DecryptData2 (byte[] data, int pos, int length, byte key)
|
||||||
|
{
|
||||||
|
if (pos < 0 || pos + length > data.Length)
|
||||||
|
throw new ArgumentOutOfRangeException ("pos", "Invalid byte array index.");
|
||||||
|
uint seed = (uint)(key | key << 8 | key << 16 | key << 24);
|
||||||
|
fixed (byte* data8 = &data[pos])
|
||||||
|
{
|
||||||
|
uint* data32 = (uint*)data8;
|
||||||
|
for (int count = length / 4; count > 0; --count)
|
||||||
|
{
|
||||||
|
*data32 = seed ^ (*data32 << 6) ^ (((*data32 >> 2) ^ (*data32 << 6)) & 0x3F3F3F3F);
|
||||||
|
data32++;
|
||||||
|
}
|
||||||
|
byte* data_end = (byte*)data32;
|
||||||
|
for (int count = length % 4; count > 0; --count)
|
||||||
|
{
|
||||||
|
*data_end = (byte)(key ^ Binary.RotByteR (*data_end, 2));
|
||||||
|
data_end++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RepiEntry EntryFromMd5 (byte[] data, int pos, StringBuilder builder)
|
RepiEntry EntryFromMd5 (byte[] data, int pos, StringBuilder builder)
|
||||||
{
|
{
|
||||||
var key = new CowArray<byte> (data, pos, 16).ToArray();
|
var key = new CowArray<byte> (data, pos, 16).ToArray();
|
||||||
|
|||||||
Reference in New Issue
Block a user