diff --git a/ArcFormats/MangaGamer/ArcMGPK.cs b/ArcFormats/MangaGamer/ArcMGPK.cs index 0cc08e1f..537f0eec 100644 --- a/ArcFormats/MangaGamer/ArcMGPK.cs +++ b/ArcFormats/MangaGamer/ArcMGPK.cs @@ -71,8 +71,9 @@ namespace GameRes.Formats.Mg public override ArcFile TryOpen (ArcView file) { + int version = file.View.ReadInt32 (4); int count = file.View.ReadInt32 (8); - if (count > 0xFFFFF) + if (version < 1 || !IsSaneCount (count)) return null; long cur_offset = 0x0C; var dir = new List (count); @@ -81,7 +82,7 @@ namespace GameRes.Formats.Mg { uint name_length = file.View.ReadByte (cur_offset); string name = file.View.ReadString (cur_offset+1, name_length, Encoding.UTF8); - var entry = FormatCatalog.Instance.Create (name); + var entry = Create (name); entry.Offset = file.View.ReadUInt32 (cur_offset+0x20); entry.Size = file.View.ReadUInt32 (cur_offset+0x24); if (!entry.CheckPlacement (file.MaxOffset)) @@ -92,9 +93,9 @@ namespace GameRes.Formats.Mg } if (has_encrypted && KnownKeys.Count > 0) { - var options = Query (arcStrings.ArcEncryptedNotice); - if (options.Key != null) - return new MgArchive (file, this, dir, options.Key); + var key = QueryKey (file.Name); + if (key != null) + return new MgArchive (file, this, dir, key); } return new ArcFile (file, this, dir); } @@ -109,14 +110,14 @@ namespace GameRes.Formats.Mg { byte[] data = new byte[entry.Size]; input.Read (data, 0, data.Length); - data = DecryptBlock (data, mgarc.Key); + DecryptData (data, mgarc.Key); if (entry.Name.HasExtension ("txt")) return DecompressStream (data); return new BinMemoryStream (data, entry.Name); } } - private byte[] DecryptBlock (byte[] input, byte[] key) + protected virtual void DecryptData (byte[] input, byte[] key) { key = (byte[])key.Clone(); for (int i = 0; i < input.Length; i++) @@ -124,14 +125,13 @@ namespace GameRes.Formats.Mg input[i] ^= key[i % key.Length]; key[i % key.Length] += 27; } - return input; } - private Stream DecompressStream (byte[] input) + internal Stream DecompressStream (byte[] input) { byte[] output = new byte[input.Length * 2]; int output_size = lzf_decompress (input, ref output); - return new MemoryStream (output, 0, output_size); + return new BinMemoryStream (output, 0, output_size); } private static int lzf_decompress (byte[] input, ref byte[] output) @@ -175,6 +175,12 @@ namespace GameRes.Formats.Mg return dst; } + internal byte[] QueryKey (string arc_name) + { + var options = Query (arcStrings.ArcEncryptedNotice); + return options.Key; + } + public override ResourceOptions GetDefaultOptions () { return new MgOptions { Key = GetKey (Properties.Settings.Default.MGPKTitle) }; @@ -193,12 +199,64 @@ namespace GameRes.Formats.Mg return key; } - public static Dictionary KnownKeys = new Dictionary(); + public static Dictionary KnownKeys { get { return DefaultScheme.KnownKeys; } } + + static MgScheme DefaultScheme = new MgScheme { KnownKeys = new Dictionary() }; public override ResourceScheme Scheme { - get { return new MgScheme { KnownKeys = KnownKeys }; } - set { KnownKeys = ((MgScheme)value).KnownKeys; } + get { return DefaultScheme; } + set { DefaultScheme = (MgScheme)value; } } } + + [Export(typeof(ArchiveFormat))] + public class Mgpk0Opener : MgpkOpener + { + public override string Tag { get { return "MGPK0"; } } + public override string Description { get { return "MG resource archive"; } } + public override uint Signature { get { return 0x4B50474D; } } // MGPK + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + int version = file.View.ReadInt32 (4); + int count = file.View.ReadInt32 (8); + if (version != 0 || !IsSaneCount (count)) + return null; + long cur_offset = 0x0C; + var dir = new List (count); + bool has_encrypted = false; + for (int i = 0; i < count; ++i) + { + string name = file.View.ReadString (cur_offset, 0x20, Encoding.UTF8); + var entry = Create (name); + entry.Offset = file.View.ReadUInt32 (cur_offset+0x20); + entry.Size = file.View.ReadUInt32 (cur_offset+0x2C); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + has_encrypted = has_encrypted || name.HasAnyOfExtensions ("png", "txt"); + dir.Add (entry); + cur_offset += 0x30; + } + if (has_encrypted) + { + var key = QueryKey (file.Name); + if (key != null) + return new MgArchive (file, this, dir, key); + } + return new ArcFile (file, this, dir); + } + + protected override void DecryptData (byte[] input, byte[] key) + { + for (int i = 0; i < input.Length; i++) + { + input[i] ^= key[i % key.Length]; + } + } + + public override ResourceScheme Scheme { get; set; } + } }