//! \file ArcG2.cs //! \date Mon Dec 07 18:35:41 2015 //! \brief Glib2 resource archive. // // Copyright (C) 2015 by morkt // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; using System.Linq; using GameRes.Utility; namespace GameRes.Formats.Glib2 { public delegate int PermutationDelegate (int offset, int value); public class G2Scheme { public byte[] SrcOrder; public byte[] DstOrder; public PermutationDelegate FirstPermutation; public PermutationDelegate SecondPermutation; public void Decrypt (byte[] input, int input_origin, byte[] output, int output_origin, int length) { int i; for (i = 0; i < (length & ~3); ++i) { int src = input_origin + (i & ~3) + SrcOrder[i & 3]; int dst = output_origin + (i & ~3) + DstOrder[i & 3]; output[dst] = (byte)SecondPermutation (i, FirstPermutation (i, input[src])); } for (; i < length; ++i) { output[output_origin+i] = (byte)SecondPermutation (i, FirstPermutation (i, input[input_origin+i])); } } public void Decrypt (byte[] input, byte[] output) { Decrypt (input, 0, output, 0, input.Length); } public byte[] Decrypt (byte[] encrypted) { var data = new byte[encrypted.Length]; Decrypt (encrypted, data); return data; } } internal class G2Entry : Entry { public readonly uint[] Keys = new uint[4]; } [Export(typeof(ArchiveFormat))] public class G2Opener : ArchiveFormat { public override string Tag { get { return "G2"; } } public override string Description { get { return "Glib2 engine resource archive"; } } public override uint Signature { get { return 0x47D33310; } } public override bool IsHierarchic { get { return true; } } public override bool CanWrite { get { return false; } } public G2Opener () { Extensions = new string[] { "g2", "stx" }; } static readonly G2Scheme HeaderEncryption = G2MetaScheme.CreateInstance (0x8465B49B); public override ArcFile TryOpen (ArcView file) { var header = file.View.ReadBytes (0, 0x5C); if (header.Length != 0x5C) return null; header = HeaderEncryption.Decrypt (header); if (!Binary.AsciiEqual (header, "GLibArchiveData2.") || header[0x12] != 0) return null; int version = header[0x11] - '0'; if (version != 0 && version != 1) return null; uint index_offset = LittleEndian.ToUInt32 (header, 0x54); uint index_size = LittleEndian.ToUInt32 (header, 0x58); byte[][] encrypted_index = new byte[2][]; encrypted_index[0] = file.View.ReadBytes (index_offset, index_size); if (encrypted_index[0].Length != index_size) return null; encrypted_index[1] = new byte[index_size]; uint[] keys = { LittleEndian.ToUInt32 (header, 0x44), LittleEndian.ToUInt32 (header, 0x34), LittleEndian.ToUInt32 (header, 0x24), LittleEndian.ToUInt32 (header, 0x14), }; int i = 0; foreach (var key in keys) { var decoder = G2MetaScheme.CreateInstance (key); decoder.Decrypt (encrypted_index[i], encrypted_index[i^1]); i ^= 1; } byte[] index = encrypted_index[i]; if (!Binary.AsciiEqual (index, "CDBD")) return null; int count = LittleEndian.ToInt32 (index, 4); int current_offset = 0x10; int info_base = current_offset + LittleEndian.ToInt32 (index, 8); int names_base = current_offset + count * 0x18; var dir = new List<Entry> (count); for (i = 0; i < count; ++i) { int name_offset = names_base + LittleEndian.ToInt32 (index, current_offset); int parent_dir = LittleEndian.ToInt32 (index, current_offset+8); int attr = LittleEndian.ToInt32 (index, current_offset+0xC); var name = Binary.GetCString (index, name_offset, info_base-name_offset); if (parent_dir != -1) name = Path.Combine (dir[parent_dir].Name, name); var entry = new G2Entry { Name = name }; if (0x100 == attr) { int info_offset = info_base + LittleEndian.ToInt32 (index, current_offset+0x10); entry.Size = LittleEndian.ToUInt32 (index, info_offset+8); entry.Offset = LittleEndian.ToUInt32 (index, info_offset+0xC); entry.Type = FormatCatalog.Instance.GetTypeFromName (name); for (int j = 0; j < 4; ++j) { info_offset += 0x10; entry.Keys[j] = LittleEndian.ToUInt32 (index, info_offset); } } dir.Add (entry); current_offset += 0x18; } return new ArcFile (file, this, dir.Where (e => e.Offset != -1).ToList()); } const int EntryChunkSize = 0x20000; public override Stream OpenEntry (ArcFile arc, Entry entry) { var g2ent = entry as G2Entry; if (null == g2ent) return base.OpenEntry (arc, entry); int entry_size = (int)g2ent.Size; int offset = 0; var decoders = new G2Scheme[4]; for (int i = 0; i < 4 && offset < entry_size; ++i) { decoders[i] = G2MetaScheme.CreateInstance (g2ent.Keys[i]); if (null != decoders[i]) offset += EntryChunkSize; } var output = new byte[entry_size]; var input = new byte[EntryChunkSize]; int current_decoder = 0; offset = 0; while (offset < entry_size) { int current_chunk_size = Math.Min (EntryChunkSize, entry_size - offset); if (null != decoders[current_decoder]) { arc.File.View.Read (g2ent.Offset+offset, input, 0, (uint)current_chunk_size); decoders[current_decoder].Decrypt (input, 0, output, offset, current_chunk_size); } else { arc.File.View.Read (g2ent.Offset+offset, output, offset, (uint)current_chunk_size); } current_decoder = (current_decoder + 1) & 3; offset += current_chunk_size; } return new BinMemoryStream (output, entry.Name); } } public class G2MetaScheme { public static G2Scheme CreateInstance (uint key) { ushort hash = (ushort)((key * 0x5F) >> 13); int i = Array.IndexOf (PermutationHashes, hash); if (-1 == i) return null; int src_order = i / 150; int dst_order = i / 30 - 5 * src_order; if (dst_order >= src_order) ++dst_order; int second_action = (i / 5) % 6; int first_action = i % 5; if (first_action >= second_action) ++first_action; return new G2Scheme { SrcOrder = MutationOrder[src_order], DstOrder = MutationOrder[dst_order], FirstPermutation = Permutations[first_action], SecondPermutation = Permutations[second_action], }; } static readonly byte[][] MutationOrder = new byte[][] { new byte[] { 3, 2, 1, 0 }, new byte[] { 0, 2, 1, 3 }, new byte[] { 1, 0, 3, 2 }, new byte[] { 3, 0, 2, 1 }, new byte[] { 2, 1, 3, 0 }, new byte[] { 3, 2, 1, 0 }, }; static readonly PermutationDelegate[] Permutations = { (i, x) => Binary.RotByteR ((byte)x, i), (i, x) => x ^ i, (i, x) => ~x, (i, x) => ~(x - 100), (i, x) => x + i, (i, x) => Binary.RotByteL ((byte)x, 4), }; static readonly ushort[] PermutationHashes = { 0x0DF0, 0xB0B7, 0x45B8, 0xC9B4, 0xCFB3, 0xF0B0, 0xA85F, 0x2C0B, 0x648D, 0xD4E1, 0x11EA, 0xDAA7, 0xFB5F, 0xE83A, 0x82A4, 0x0F5D, 0xAE64, 0x6F6F, 0xEC17, 0x040E, 0x25F2, 0xD2A1, 0x3F4E, 0x9EB3, 0xA3C3, 0x126C, 0xCBE3, 0x8597, 0x6E19, 0xA4E5, 0x644E, 0xBD12, 0x43FF, 0x635D, 0xB5FB, 0x8B1E, 0x7840, 0xFFB8, 0x6385, 0x8C22, 0x2D2F, 0x479B, 0x185C, 0x1970, 0x0C73, 0xE029, 0xE04D, 0xF855, 0xA1F2, 0xDCE5, 0x464B, 0x997A, 0x50F3, 0xC59D, 0x4FCF, 0x4434, 0x03E6, 0x4440, 0xB794, 0xAD98, 0xC4D3, 0x851C, 0x7643, 0x0824, 0x624B, 0xF308, 0x8BC9, 0x8FCD, 0x0805, 0x28C6, 0x7FDA, 0x7171, 0xC93C, 0x3F85, 0xE0B9, 0x64A8, 0xBF46, 0x5652, 0xEBAA, 0x9507, 0x4DF4, 0x692C, 0x773D, 0x65CF, 0x2298, 0x7D43, 0xC1F2, 0x2DE8, 0x6758, 0xEA01, 0x8B65, 0xE7FE, 0x5466, 0x8F59, 0x44B2, 0x1CCB, 0x92DA, 0xB384, 0xC5C4, 0x5179, 0xDC2E, 0xBC2A, 0xA07D, 0x74D5, 0x3492, 0x0BF6, 0xC3F5, 0xB68C, 0xAABB, 0x114E, 0xB7B8, 0x6636, 0xA419, 0xF92F, 0xE2B4, 0x273B, 0xCC0B, 0x4F64, 0x2EFE, 0xBF77, 0xD00A, 0x4FA9, 0x1AFF, 0x5930, 0xC18B, 0x80BD, 0x22D2, 0xD8D2, 0x8B8C, 0x1F8C, 0x6B41, 0x27A1, 0xE38A, 0xF328, 0x910B, 0x6D91, 0x81A2, 0xC5AE, 0xE7B9, 0x98F7, 0x044D, 0x7851, 0xCF13, 0x5EE3, 0x8C5A, 0x5D13, 0xE3A9, 0x5599, 0x60BE, 0x0775, 0x81AC, 0x63C8, 0x80A1, 0x1EAB, 0xC3EB, 0x2847, 0x29C6, 0x5D02, 0xB5DB, 0xDA13, 0xC3DB, 0x8B4C, 0x0347, 0x89B1, 0x8743, 0x3407, 0x9870, 0xA97D, 0xBC0F, 0x2452, 0xE7B5, 0x1016, 0x1759, 0xE219, 0x41FE, 0x6BEE, 0x2EB6, 0xAE15, 0x9010, 0xD486, 0x7C58, 0xBD7F, 0xBDC9, 0x5385, 0xC1DF, 0x55FD, 0x1543, 0x9DD2, 0xDF04, 0x561C, 0xB184, 0x0E81, 0xB145, 0x23D7, 0xBBA8, 0x4C08, 0xC6DC, 0x68FE, 0xB6FC, 0xA2E1, 0x49C2, 0x4C6E, 0x8474, 0x3FBC, 0x4059, 0xE75C, 0xF748, 0x2542, 0x3E89, 0x1932, 0xAEC3, 0x1A71, 0x3229, 0x5F8F, 0xDF64, 0x34EA, 0xB7AF, 0xBEA4, 0x5129, 0xBA1B, 0x00A0, 0x0731, 0x3F10, 0x1B52, 0xD0D6, 0xCB01, 0x0CD2, 0xC522, 0xAAF5, 0x3B34, 0x7EEA, 0x1FE8, 0xAE3B, 0x144D, 0xCA14, 0x3CD4, 0x9AFB, 0x673A, 0x3B41, 0x5EA0, 0x8A5F, 0x0289, 0x7DDC, 0x9FBB, 0x75DC, 0x59D3, 0x4907, 0x83D5, 0x747C, 0xFC0D, 0x650B, 0xBA84, 0xF6CF, 0x21AF, 0xFF7E, 0xACBA, 0xC8E1, 0x4DBB, 0xCBD9, 0x3415, 0x2BBB, 0x6DA3, 0x349E, 0xD30B, 0xCEDB, 0x739E, 0xE0F3, 0xCB44, 0x7E1F, 0x274B, 0x3FCE, 0xEEC8, 0x4306, 0x7EC3, 0x4CBF, 0x766B, 0x582C, 0x47D1, 0x688E, 0x15BC, 0xEEEA, 0xD2B8, 0x8674, 0x088B, 0xE90E, 0x3A3F, 0xF70A, 0x2393, 0x68B3, 0x871E, 0xF6FC, 0x4631, 0xFEAD, 0x89B5, 0x1242, 0xD68C, 0x1214, 0x028A, 0xE3F3, 0xBC86, 0x679E, 0xB301, 0x2827, 0xC90F, 0xA4DD, 0x0E14, 0x4225, 0x46EF, 0x0C69, 0x12BB, 0xA14B, 0x4319, 0x5054, 0x1BBE, 0x1144, 0x29B8, 0x74E7, 0x94C8, 0xA217, 0x2001, 0xE86F, 0x01B3, 0x4713, 0x737A, 0x45B2, 0x68BB, 0x86FD, 0x696D, 0xB604, 0x0D2E, 0xC643, 0x6F65, 0x6EC4, 0xCBC0, 0xE505, 0x0677, 0x6427, 0x6481, 0xB195, 0xE877, 0x4216, 0xD938, 0xD165, 0xBB70, 0x9980, 0x1F0A, 0xA96C, 0x1DB9, 0x7B0A, 0xB400, 0xC636, 0x17EA, 0x129A, 0x46F8, 0x40DC, 0xEDC0, 0x7BC5, 0x4521, 0x15E5, 0xB0F5, 0x4E1D, 0xD7AF, 0xF85C, 0xF100, 0x490E, 0x74E6, 0x9D7E, 0x112D, 0x5063, 0x6704, 0xE9B9, 0xA519, 0xA626, 0x0CBC, 0x5880, 0x8224, 0xF706, 0x2A08, 0x317B, 0x2E86, 0x46CF, 0xFE7E, 0x9D15, 0xBC5E, 0x35FC, 0x9BCD, 0xA1B0, 0x9191, 0xFBFC, 0x54D9, 0xE1B3, 0xD381, 0xE3DA, 0x41BD, 0xD27D, 0x9B0C, 0xBAB2, 0xC6FA, 0xB910, 0xD4C1, 0xAB62, 0x756D, 0xD4FF, 0xE1C4, 0xC052, 0x32E2, 0x6063, 0x6CDF, 0x8BD6, 0xBCE0, 0x8026, 0x63E8, 0x2201, 0x9800, 0x5572, 0x674C, 0xDD3B, 0x1E95, 0xE9EF, 0x698E, 0xF509, 0xA410, 0xCA5F, 0xD175, 0xB525, 0xAD0F, 0x4E44, 0x3E58, 0x554E, 0x97B3, 0x50F5, 0x8BAC, 0x5A32, 0x77AA, 0xD14F, 0x3ED8, 0x7892, 0x5A02, 0x0E66, 0x8269, 0x120A, 0xADEA, 0x82A1, 0xE1F0, 0x8B35, 0xE7E1, 0xFD1F, 0xFD65, 0x7E3A, 0xB4B7, 0x5FFC, 0xF75A, 0xB2DB, 0x5EEA, 0xE63E, 0x13C7, 0xB5F9, 0x02B9, 0x9F2E, 0x620A, 0x2108, 0x10BB, 0xB124, 0x6617, 0x774D, 0x49A5, 0x93CD, 0xEDEC, 0xA50B, 0x7996, 0x8E82, 0x1782, 0x87A5, 0x1D08, 0x8C23, 0x7208, 0x5FD5, 0x3FFC, 0xE23C, 0x182C, 0x0338, 0xA32B, 0x9C08, 0x907C, 0xDF0E, 0xAF61, 0x5613, 0xACB7, 0x8AA9, 0xF1BA, 0x6AC2, 0xCAD5, 0xED60, 0x2C74, 0xA172, 0x4DC4, 0x30CE, 0x02AB, 0x3C3C, 0x260E, 0xAEDD, 0xCD27, 0x8D18, 0x8E4E, 0x94CD, 0x8A82, 0x573A, 0x84A8, 0x5B10, 0x8E93, 0x1DB2, 0x8D7E, 0xB892, 0xCD2E, 0x4D24, 0xC8FD, 0x571D, 0x978E, 0x94E2, 0x7471, 0x288C, 0x8722, 0x7B92, 0x48ED, 0x2583, 0xBE7F, 0x0D86, 0x347B, 0x90EE, 0x955F, 0x6277, 0x64E3, 0xDAFE, 0xBC9B, 0x96CE, 0xEBE4, 0xEFBA, 0x6C19, 0x42A8, 0xC2DE, 0xA5FD, 0x68BD, 0xD469, 0x5511, 0x6C69, 0x671E, 0x2E77, 0xCF3B, 0x2052, 0x88EC, 0x6577, 0xED28, 0xA811, 0xB7F5, 0x213F, 0x1F24, 0xEEB5, 0x9511, 0xC44D, 0x6532, 0xD969, 0xAA6B, 0xF7E7, 0xC645, 0xC868, 0xB65B, 0x0029, 0xEAD2, 0x58A4, 0x0153, 0x9E90, 0x6E05, 0xDC61, 0x2F9B, 0x8CD4, 0xCC13, 0x5621, 0x056C, 0xC7A3, 0xD666, 0x40CC, 0x4ED9, 0x6071, 0x0E42, 0x23E5, 0xE18F, 0x1126, 0x4889, 0x2AF2, 0xACBB, 0x2AD3, 0xDC2B, 0x54E8, 0x1478, 0x9FF3, 0x9DD0, 0x63BB, 0x0430, 0xE5D4, 0x91C9, 0xB055, 0xE925, 0x6292, 0x6228, 0x14DE, 0x4D80, 0x962A, 0x801E, 0x1576, 0x2D36, 0xE3C3, 0x6F7F, 0x07EA, 0xAA9F, 0xCAE9, 0x8F76, 0xD1CB, 0xE490, 0x6956, 0x98CA, 0x4DB0, 0x922E, 0x3E05, 0x1538, 0xFFA3, 0x4A24, 0x0467, 0x7940, 0x7B14, 0xD4E8, 0xA9E8, 0xF9D2, 0xDDD3, 0xCEF2, 0x3A55, 0xF118, 0xD0F2, 0x5688, 0xE2BE, 0xC0ED, 0xFED3, 0x33B1, 0xAC96, 0xA6F2, 0x0F0B, 0xE596, 0x0A88, 0xB346, 0x2208, 0x18A1, 0xAE40, 0x7ECC, 0xA2E4, 0xB661, 0xBDEE, 0xC14C, 0xD218, 0x8280, 0x7F61, 0x8FA3, 0x4AA3, 0xA61B, 0xDD56, 0xB4AD, 0xE436, 0x5AF9, 0xE595, 0x8603, 0x6FF3, 0xBF3F, 0x2FCD, 0xD446, 0x475A, 0x508F, 0xC27E, 0x6096, 0x9A42, 0x19C4, 0x6109, 0xEC7A, 0x5937, 0x218F, 0x6FFF, 0x2033, 0x922C, 0x5C66, 0xC20D, 0xE424, 0xF193, 0x159D, 0xCF98, 0x7AE0, 0x79BA, 0xF8AC, 0x0927, 0x0BAC, 0xE3F4, 0x26A7, 0x1C7C, 0x8D05, 0x5A6A, 0x6A85, 0x6947, 0xEB07, 0x03F4, 0x166F, 0x8DE4, 0xFB48, 0xF828, 0x3977, 0x5A67, 0x4D08, 0x838D, 0xCBB8, 0xD737, 0x0EFE, 0xED83, 0xF808, 0xB4AC, 0x721E, 0x1F51, 0xD55C, 0xD2E0, 0xE243, 0x301F, 0x4630, 0x26D7, 0x3D02, 0xC71E, 0x14F0, 0xE56A, 0xABC7, 0x26B7, 0x1609, 0x1A11, 0xADA1, 0xCB41, 0x6C4F, 0x84F2, 0xB45D, 0x1CE3, 0x70A3, 0x9661, 0x4374, 0x6C5A, 0xE76C, 0x7333, 0xE26D, 0xEFA8, 0xF66A, 0x6E77, 0xF1CC, 0xB7E8, 0x0433, 0x96F6, 0x8779, 0x8181, 0xF982, 0xD352, 0x6338, 0x89E9, 0x9C75, 0x30F7, 0x4B4A, 0x2B4E, 0xE942, 0x1A26, 0xB237, 0xD4F4, 0x5300, 0xC4D0, 0x58EF, 0xA81D, 0xD733, 0x1A6C, 0x8096, 0xA37C, 0x9B5B, 0x390D, 0xE007, 0xED2F, 0x8287, 0xA549, 0xB63B, 0xA3E4, 0xF115, 0xCD68, 0xF39A, 0xD125, 0x3F16, 0x47CD, 0x582E, 0x508D, 0xED45, 0x9073, 0x60EF, 0x2591, 0xF962, 0x7C2E, 0xADB1, 0x2FB5, 0x376F, 0x4F41, 0xA24A, 0x2AAA, 0xFA2B, 0x5FA6, 0xADCF, 0x1644, 0x575E, 0x8583, 0x055D, 0x2AE7, 0xA515, 0x2700, 0x5FC3, 0x6001, 0xCB0E, 0x1351, 0x7C19, 0xBFD3, 0xF360, 0x706C, 0x1227, 0x6411, 0x3649, 0xF843, 0x7CFE, 0x5DC3, 0x640D, 0x7E05, 0x251C, 0x8EDF, 0x1F31, 0x39F8, 0x075B, 0x3BBA, 0x75B8, 0x4CBC, 0xAA60, 0xE1D4, 0x35C9, 0x3516, 0xB0D0, 0x7B3B, 0xF861, 0x6B87, 0x3362, 0x1704, 0x7F1C, 0x587B, 0x966B, 0xD7DF, 0xBE5D, 0x82EF, 0xE193, 0xD841, 0xBC82, 0x9AF9, 0x8526, 0x7018, 0x8E13, 0x5E23, 0xF49D, 0x4035, 0x9118, 0x2C41, 0x826C, 0x561A, 0x5E7B, 0x38D4, 0xC263, 0x5979, 0xB15A, 0x4D89, 0xC11C, 0x8516, 0x0343, 0xF590, 0xFF38, 0x8385, 0x4B5B, 0xEEF2, 0x99B4, 0xB11B, 0x94D6, 0x8961, 0xE9F9, 0x3138, 0xB3A0, 0x09CC, 0x7A4F, 0x47DE, 0xE478, 0xD9FF, 0xE62C, 0x9453, 0x6D0C, 0x2FC2, 0x7444, }; } }