implemented G2 archives and PGX images.

This commit is contained in:
morkt 2015-12-08 21:19:13 +04:00
parent b442bd5afb
commit 341800cda9
4 changed files with 621 additions and 0 deletions

View File

@ -82,6 +82,8 @@
<Compile Include="FC01\WidgetMCG.xaml.cs"> <Compile Include="FC01\WidgetMCG.xaml.cs">
<DependentUpon>WidgetMCG.xaml</DependentUpon> <DependentUpon>WidgetMCG.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Glib2\ArcG2.cs" />
<Compile Include="Glib2\ImagePGX.cs" />
<Compile Include="ImagePSD.cs" /> <Compile Include="ImagePSD.cs" />
<Compile Include="Kaguya\ImageAO.cs" /> <Compile Include="Kaguya\ImageAO.cs" />
<Compile Include="Propeller\ArcMGR.cs" /> <Compile Include="Propeller\ArcMGR.cs" />

347
ArcFormats/Glib2/ArcG2.cs Normal file
View File

@ -0,0 +1,347 @@
//! \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 CanCreate { 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 MemoryStream (output);
}
}
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,
};
}
}

View File

@ -0,0 +1,266 @@
//! \file ImagePGX.cs
//! \date Tue Dec 08 02:50:45 2015
//! \brief Glib2 image format.
//
// 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 GameRes.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Media;
namespace GameRes.Formats.Glib2
{
internal class PgxMetaData : ImageMetaData
{
public int PackedSize;
}
internal class StxLayerInfo
{
public string Path;
public Rectangle? Rect;
public string Effect;
public int Blend;
}
[Export(typeof(ImageFormat))]
public class PgxFormat : ImageFormat
{
public override string Tag { get { return "PGX"; } }
public override string Description { get { return "Glib2 engine image format"; } }
public override uint Signature { get { return 0x00584750; } } // 'PGX'
static readonly InfoReader InfoCache = new InfoReader();
public override ImageMetaData ReadMetaData (Stream stream)
{
var header = new byte[0x18];
if (header.Length != stream.Read (header, 0, header.Length))
return null;
return new PgxMetaData
{
Width = LittleEndian.ToUInt32 (header, 8),
Height = LittleEndian.ToUInt32 (header, 12),
BPP = LittleEndian.ToInt16 (header, 0x10) == 0 ? 24 : 32,
PackedSize = LittleEndian.ToInt32 (header, 0x14),
};
}
public override ImageData Read (Stream stream, ImageMetaData info)
{
var meta = (PgxMetaData)info;
PixelFormat format = 32 == meta.BPP ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
int stride = (int)meta.Width * 4;
var pixels = new byte[stride * (int)meta.Height];
stream.Seek (-meta.PackedSize, SeekOrigin.End);
LzssUnpack (stream, pixels);
var layer = InfoCache.GetInfo (info.FileName);
if (null != layer && null != layer.Rect)
{
info.OffsetX = layer.Rect.Value.X;
info.OffsetY = layer.Rect.Value.Y;
}
return ImageData.Create (info, format, null, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("PgxFormat.Write not implemented");
}
static void LzssUnpack (Stream input, byte[] output)
{
var frame = new byte[0x1000];
int frame_pos = 0xFEE;
using (var lz = new ArcView.Reader (input))
{
int dst = 0;
int bits = 1;
while (dst < output.Length)
{
if (1 == bits)
bits = lz.ReadByte() | 0x100;
if (0 != (bits & 1))
{
byte b = lz.ReadByte();
output[dst++] = b;
frame[frame_pos++] = b;
frame_pos &= 0xFFF;
}
else
{
byte lo = lz.ReadByte();
byte hi = lz.ReadByte();
int offset = (hi & 0xF0) << 4 | lo;
int count = Math.Min ((~hi & 0xF) + 3, output.Length-dst);
for (int i = 0; i < count; ++i)
{
byte b = frame[offset++ & 0xFFF];
output[dst++] = b;
frame[frame_pos++] = b;
frame_pos &= 0xFFF;
}
}
bits >>= 1;
}
}
}
}
internal class InfoReader
{
string m_last_info_dir;
Dictionary<string, StxLayerInfo> m_layer_map;
internal class StxEntry
{
public string FullName;
public string Name;
public int Attr;
public uint InfoOffset;
public uint InfoSize;
}
public StxLayerInfo GetInfo (string image_name)
{
try
{
var info_name = VFS.CombinePath (Path.GetDirectoryName (image_name), "info");
if (!VFS.FileExists (info_name))
return null;
if (string.IsNullOrEmpty (m_last_info_dir)
|| string.Join (":", VFS.FullPath) != m_last_info_dir)
ParseInfo (info_name);
var layer_name = Path.GetFileName (image_name);
return GetLayerInfo (layer_name);
}
catch (Exception X)
{
Trace.WriteLine (X.Message, "[Glib2] STX parse error");
return null;
}
}
StxLayerInfo GetLayerInfo (string layer_name)
{
if (null == m_layer_map)
return null;
StxLayerInfo info;
m_layer_map.TryGetValue (layer_name, out info);
return info;
}
void ParseInfo (string info_name)
{
if (null == m_layer_map)
m_layer_map = new Dictionary<string, StxLayerInfo>();
else
m_layer_map.Clear();
using (var info = VFS.OpenView (info_name))
{
if (!info.View.AsciiEqual (0, "CDBD"))
return;
int count = info.View.ReadInt32 (4);
uint current_offset = 0x10;
uint info_base = current_offset + info.View.ReadUInt32 (8);
uint info_total_size = info.View.ReadUInt32 (12);
info.View.Reserve (0, info_base+info_total_size);
uint names_base = current_offset + (uint)count * 0x18;
var dir = new List<StxEntry> (count);
for (int i = 0; i < count; ++i)
{
uint name_offset = names_base + info.View.ReadUInt32 (current_offset);
int parent_dir = info.View.ReadInt32 (current_offset+8);
int attr = info.View.ReadInt32 (current_offset+0xC);
uint info_offset = info.View.ReadUInt32 (current_offset+0x10);
uint info_size = info.View.ReadUInt32 (current_offset+0x14);
var name = info.View.ReadString (name_offset, info_base-name_offset);
string path_name = name;
if (parent_dir != -1)
path_name = Path.Combine (dir[parent_dir].FullName, path_name);
if (attr != -1 && info_size != 0)
info_offset += info_base;
var entry = new StxEntry {
FullName = path_name,
Name = name,
Attr = attr,
InfoOffset = info_offset,
InfoSize = info_size,
};
if (name == "filename" && parent_dir != -1 && info_size != 0)
{
uint filename_length = info.View.ReadUInt32 (info_offset);
var filename = info.View.ReadString (info_offset+4, filename_length);
m_layer_map[filename] = new StxLayerInfo {
Path = dir[parent_dir].FullName + Path.DirectorySeparatorChar,
};
}
dir.Add (entry);
current_offset += 0x18;
}
foreach (var layer in m_layer_map.Values)
{
foreach (var field in dir.Where (e => e.Attr != -1 && e.FullName.StartsWith (layer.Path)))
{
if ("rect" == field.Name && 0x14 == field.InfoSize)
{
int left = info.View.ReadInt32 (field.InfoOffset+4);
int top = info.View.ReadInt32 (field.InfoOffset+8);
int right = info.View.ReadInt32 (field.InfoOffset+12);
int bottom = info.View.ReadInt32 (field.InfoOffset+16);
layer.Rect = new Rectangle (left, top, right-left, bottom-top);
}
else if ("effect" == field.Name && field.InfoSize > 4)
{
// "norm"
uint effect_length = info.View.ReadUInt32 (field.InfoOffset);
layer.Effect = info.View.ReadString (field.InfoOffset+4, effect_length);
if (layer.Effect != "norm")
Trace.WriteLine (string.Format ("{0}: {1}effect = {2}",
info_name, layer.Path, layer.Effect), "[Glib2.STX]");
}
else if ("blend" == field.Name && 4 == field.InfoSize)
{
// 0xFF -> opaque
layer.Blend = info.View.ReadInt32 (field.InfoOffset);
if (layer.Blend != 0xFF)
Trace.WriteLine (string.Format ("{0}: {1}blend = {2}",
info_name, layer.Path, layer.Blend), "[Glib2.STX]");
}
}
}
}
m_last_info_dir = string.Join (":", VFS.FullPath);
}
}
}

View File

@ -66,6 +66,7 @@ Utatemeguri<br/>
Aoiro Rinne<br/> Aoiro Rinne<br/>
Cartagra<br/> Cartagra<br/>
PP -Pianissimo-<br/> PP -Pianissimo-<br/>
Ryoujoku Guerilla Gari 3<br/>
Nagomibako<br/> Nagomibako<br/>
</td></tr> </td></tr>
<tr><td>DATA.Pack</td><td><tt>KCAP</tt></td><td>No</td><td>Interheart<br/>Willow Soft</td><td> <tr><td>DATA.Pack</td><td><tt>KCAP</tt></td><td>No</td><td>Interheart<br/>Willow Soft</td><td>
@ -82,6 +83,7 @@ Yakuchu!<br/>
<tr><td>*.tgf</td><td>-</td><td>No</td></tr> <tr><td>*.tgf</td><td>-</td><td>No</td></tr>
<tr class="odd"><td>*.arc</td><td><tt>MajiroArcV1.000</tt><br/><tt>MajiroArcV2.000</tt><br/><tt>MajiroArcV3.000</tt></td><td>Yes</td><td rowspan="3">Majiro</td><td rowspan="3"> <tr class="odd"><td>*.arc</td><td><tt>MajiroArcV1.000</tt><br/><tt>MajiroArcV2.000</tt><br/><tt>MajiroArcV3.000</tt></td><td>Yes</td><td rowspan="3">Majiro</td><td rowspan="3">
Amber Quartz<br/> Amber Quartz<br/>
Arpeggio ~Kimi Iro no Melody~<br/>
Chikan Kizoku<br/> Chikan Kizoku<br/>
Narimono<br/> Narimono<br/>
Reconquista<br/> Reconquista<br/>
@ -546,6 +548,10 @@ Gakuen Saimin Reido<br/>
Hapymaher<br/> Hapymaher<br/>
</td></tr> </td></tr>
<tr><td>*.pb3</td><td><tt>PB3B</tt></td><td>No</td></tr> <tr><td>*.pb3</td><td><tt>PB3B</tt></td><td>No</td></tr>
<tr class="odd"><td>*.g2<br/>*.stx</td><td>-</td><td>No</td><td rowspan="2">GLib2</td><td rowspan="2">
Hitozuma Net Auction<br/>
</td></tr>
<tr class="odd"><td>*.pgx</td><td><tt>PGX</tt></td><td>No</td></tr>
</table> </table>
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p> <p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
</body> </body>