mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
implemented QLIE engine archives version 2.
This commit is contained in:
@@ -110,6 +110,7 @@
|
|||||||
<Compile Include="ArcPAC.cs" />
|
<Compile Include="ArcPAC.cs" />
|
||||||
<Compile Include="ArcPCK.cs" />
|
<Compile Include="ArcPCK.cs" />
|
||||||
<Compile Include="ArcPD.cs" />
|
<Compile Include="ArcPD.cs" />
|
||||||
|
<Compile Include="ArcQLIE.cs" />
|
||||||
<Compile Include="ArcRPA.cs" />
|
<Compile Include="ArcRPA.cs" />
|
||||||
<Compile Include="ArcS25.cs" />
|
<Compile Include="ArcS25.cs" />
|
||||||
<Compile Include="ArcSAF.cs" />
|
<Compile Include="ArcSAF.cs" />
|
||||||
|
|||||||
251
ArcFormats/ArcQLIE.cs
Normal file
251
ArcFormats/ArcQLIE.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
//! \file ArcQLIE.cs
|
||||||
|
//! \date Mon Jun 15 04:03:18 2015
|
||||||
|
//! \brief QLIE engine archives implementation.
|
||||||
|
//
|
||||||
|
// 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.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System;
|
||||||
|
using GameRes.Utility;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Qlie
|
||||||
|
{
|
||||||
|
internal class QlieEntry : PackedEntry
|
||||||
|
{
|
||||||
|
public bool IsEncrypted;
|
||||||
|
public uint Hash;
|
||||||
|
public uint Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public class PackOpener : ArchiveFormat
|
||||||
|
{
|
||||||
|
public override string Tag { get { return "QLIE/PACK"; } }
|
||||||
|
public override string Description { get { return "QLIE engine resource archive"; } }
|
||||||
|
public override uint Signature { get { return 0; } }
|
||||||
|
public override bool IsHierarchic { get { return true; } }
|
||||||
|
public override bool CanCreate { get { return false; } }
|
||||||
|
|
||||||
|
public PackOpener ()
|
||||||
|
{
|
||||||
|
Extensions = new string [] { "pack" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
if (file.MaxOffset <= 0x1c)
|
||||||
|
return null;
|
||||||
|
long index_offset = file.MaxOffset - 0x1c;
|
||||||
|
if (!file.View.AsciiEqual (index_offset, "FilePackVer")
|
||||||
|
|| !file.View.AsciiEqual (index_offset+0xC, ".0"))
|
||||||
|
return null;
|
||||||
|
int pack_version = file.View.ReadByte (index_offset+0xB) - '0';
|
||||||
|
if (pack_version != 2)
|
||||||
|
throw new NotSupportedException ("Not supported QLIE archive version");
|
||||||
|
int count = file.View.ReadInt32 (index_offset+0x10);
|
||||||
|
if (count < 0 || count > 0xfffff)
|
||||||
|
return null;
|
||||||
|
index_offset = file.View.ReadInt64 (index_offset+0x14);
|
||||||
|
if (index_offset < 0 || index_offset >= file.MaxOffset)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int pack_key = 0xC4;
|
||||||
|
var name_buffer = new byte[0x100];
|
||||||
|
var dir = new List<Entry> (count);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
int name_length = file.View.ReadUInt16 (index_offset);
|
||||||
|
if (name_length > name_buffer.Length)
|
||||||
|
name_buffer = new byte[name_length];
|
||||||
|
if (name_length != file.View.Read (index_offset+2, name_buffer, 0, (uint)name_length))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int key = name_length + (pack_key ^ 0x3e);
|
||||||
|
for (int k = 0; k < name_length; ++k)
|
||||||
|
name_buffer[k] ^= (byte)(((k + 1) ^ key) + k + 1);
|
||||||
|
|
||||||
|
string name = Encodings.cp932.GetString (name_buffer, 0, name_length);
|
||||||
|
var entry = new QlieEntry { Name = name };
|
||||||
|
entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
|
||||||
|
|
||||||
|
index_offset += 2 + name_length;
|
||||||
|
entry.Offset = file.View.ReadInt64 (index_offset);
|
||||||
|
entry.Size = file.View.ReadUInt32 (index_offset+8);
|
||||||
|
if (!entry.CheckPlacement (file.MaxOffset))
|
||||||
|
return null;
|
||||||
|
entry.UnpackedSize = file.View.ReadUInt32 (index_offset+12);
|
||||||
|
entry.IsPacked = 0 != file.View.ReadInt32 (index_offset+0x10);
|
||||||
|
entry.IsEncrypted = 0 != file.View.ReadInt32 (index_offset+0x14);
|
||||||
|
entry.Hash = file.View.ReadUInt32 (index_offset+0x18);
|
||||||
|
if (3 == pack_version)
|
||||||
|
entry.Key = (uint)pack_key;
|
||||||
|
else
|
||||||
|
entry.Key = 0;
|
||||||
|
dir.Add (entry);
|
||||||
|
index_offset += 0x1c;
|
||||||
|
}
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var qent = entry as QlieEntry;
|
||||||
|
if (null == qent || (!qent.IsEncrypted && !qent.IsPacked))
|
||||||
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
var data = new byte[entry.Size];
|
||||||
|
if (entry.Size != arc.File.View.Read (entry.Offset, data, 0, entry.Size))
|
||||||
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
|
||||||
|
if (qent.IsEncrypted)
|
||||||
|
Decrypt (data, 0, data.Length, qent.Key);
|
||||||
|
if (qent.IsPacked)
|
||||||
|
{
|
||||||
|
data = Decompress (data);
|
||||||
|
if (null == data) // probably wrong decryption
|
||||||
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
}
|
||||||
|
return new MemoryStream (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Decrypt (byte[] buffer, int offset, int length, uint key)
|
||||||
|
{
|
||||||
|
if (offset < 0)
|
||||||
|
throw new ArgumentOutOfRangeException ("offset");
|
||||||
|
if (length > buffer.Length || offset > buffer.Length - length)
|
||||||
|
throw new ArgumentOutOfRangeException ("length");
|
||||||
|
|
||||||
|
var table = new uint[2,2];
|
||||||
|
var xor = new uint[2];
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
table[0,i] = 0xA73C5F9Du;
|
||||||
|
table[1,i] = 0xCE24F523u;
|
||||||
|
xor[i] = ((uint)length + key) ^ 0xFEC9753Eu;
|
||||||
|
}
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* raw = buffer)
|
||||||
|
{
|
||||||
|
uint* encoded = (uint*)(raw + offset);
|
||||||
|
for (int i = 0; i < length / 8; ++i)
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 2; ++k)
|
||||||
|
{
|
||||||
|
table[0,k] = (table[0,k] + table[1,k]) ^ xor[k];
|
||||||
|
*encoded ^= table[0,k];
|
||||||
|
xor[k] = *encoded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] Decompress (byte[] input)
|
||||||
|
{
|
||||||
|
if (LittleEndian.ToUInt32 (input, 0) != 0xFF435031)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
bool is_16bit = 0 != (input[4] & 1);
|
||||||
|
|
||||||
|
var node = new byte[2,256];
|
||||||
|
var child_node = new byte[256];
|
||||||
|
|
||||||
|
int output_length = LittleEndian.ToInt32 (input, 8);
|
||||||
|
var output = new byte[output_length];
|
||||||
|
|
||||||
|
int src = 12;
|
||||||
|
int dst = 0;
|
||||||
|
while (src < input.Length)
|
||||||
|
{
|
||||||
|
int i, k, count, index;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
node[0,i] = (byte)i;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; )
|
||||||
|
{
|
||||||
|
count = input[src++];
|
||||||
|
|
||||||
|
if (count > 127)
|
||||||
|
{
|
||||||
|
int step = count - 127;
|
||||||
|
i += step;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 255)
|
||||||
|
break;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
for (k = 0; k < count; k++)
|
||||||
|
{
|
||||||
|
node[0,i] = input[src++];
|
||||||
|
if (node[0,i] != i)
|
||||||
|
node[1,i] = input[src++];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_16bit)
|
||||||
|
{
|
||||||
|
count = LittleEndian.ToUInt16 (input, src);
|
||||||
|
src += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = LittleEndian.ToInt32 (input, src);
|
||||||
|
src += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (k > 0)
|
||||||
|
index = child_node[--k];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (0 == count)
|
||||||
|
break;
|
||||||
|
count--;
|
||||||
|
index = input[src++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node[0,index] == index)
|
||||||
|
output[dst++] = (byte)index;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child_node[k++] = node[1,index];
|
||||||
|
child_node[k++] = node[0,index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dst != output.Length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.0.6.63")]
|
[assembly: AssemblyVersion ("1.0.6.64")]
|
||||||
[assembly: AssemblyFileVersion ("1.0.6.63")]
|
[assembly: AssemblyFileVersion ("1.0.6.64")]
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ Kana ~Imouto~<br/>
|
|||||||
Natsu no Hitoshizuku<br/>
|
Natsu no Hitoshizuku<br/>
|
||||||
Private Nurse<br/>
|
Private Nurse<br/>
|
||||||
Sensei 2<br/>
|
Sensei 2<br/>
|
||||||
|
Shoujo Settai<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.ggd</td><td><tt>\xB9\xAA\xB3\xB3</tt><br/><tt>\xAB\xAD\xAA\xBA</tt><br/><tt>\xB7\xB6\xB8\xB7</tt><br/><tt>\xCD\xCA\xC9\xB8</tt></td><td>Yes</td></tr>
|
<tr class="odd"><td>*.ggd</td><td><tt>\xB9\xAA\xB3\xB3</tt><br/><tt>\xAB\xAD\xAA\xBA</tt><br/><tt>\xB7\xB6\xB8\xB7</tt><br/><tt>\xCD\xCA\xC9\xB8</tt></td><td>Yes</td></tr>
|
||||||
<tr class="odd"><td>*.gg1<br/>*.gg2<br/>*.gg3<br/>*.gg0</td><td><tt>GGA00000</tt></td><td>No</td></tr>
|
<tr class="odd"><td>*.gg1<br/>*.gg2<br/>*.gg3<br/>*.gg0</td><td><tt>GGA00000</tt></td><td>No</td></tr>
|
||||||
@@ -190,8 +191,8 @@ Yatohime Zankikou<br/>
|
|||||||
Onna Kyoushi
|
Onna Kyoushi
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.bg_<br/>*.cg_</td><td><tt>AP</tt></td><td>Yes</td></tr>
|
<tr class="odd"><td>*.bg_<br/>*.cg_</td><td><tt>AP</tt></td><td>Yes</td></tr>
|
||||||
<tr><td>*.dpk</td><td>DPK</td><td>No</td><td rowspan="2">DAC</td><td rowspan="2">Yumemiru Tsuki no Lunalutia</td></tr>
|
<tr><td>*.dpk</td><td><tt>DPK</tt></td><td>No</td><td rowspan="2">DAC</td><td rowspan="2">Yumemiru Tsuki no Lunalutia</td></tr>
|
||||||
<tr><td>*.dgc</td><td>DGC</td><td>No</td></tr>
|
<tr><td>*.dgc</td><td><tt>DGC</tt></td><td>No</td></tr>
|
||||||
<tr class="odd"><td>*.pck</td><td>-</td><td>No</td><td rowspan="5">Crowd</td><td rowspan="5">
|
<tr class="odd"><td>*.pck</td><td>-</td><td>No</td><td rowspan="5">Crowd</td><td rowspan="5">
|
||||||
X Change R<br/>
|
X Change R<br/>
|
||||||
X Change<br/>
|
X Change<br/>
|
||||||
@@ -201,6 +202,9 @@ X Change 2<br/>
|
|||||||
<tr class="odd"><td>*.cwd</td><td><tt>cwd</tt></td><td>No</td></tr>
|
<tr class="odd"><td>*.cwd</td><td><tt>cwd</tt></td><td>No</td></tr>
|
||||||
<tr class="odd"><td>*.eog</td><td><tt>CRM</tt></td><td>No</td></tr>
|
<tr class="odd"><td>*.eog</td><td><tt>CRM</tt></td><td>No</td></tr>
|
||||||
<tr class="odd"><td>*.zbm<br/>*.cwl</td><td><tt>SZDD</tt></td><td>No</td></tr>
|
<tr class="odd"><td>*.zbm<br/>*.cwl</td><td><tt>SZDD</tt></td><td>No</td></tr>
|
||||||
|
<tr><td>*.pack</td><td><tt>FilePackVer2.0</tt></td><td>No</td><td>QLIE</td><td>
|
||||||
|
Mehime no Toriko<br/>
|
||||||
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p><a name="note-1">[1]</a> Non-encrypted only</p>
|
<p><a name="note-1">[1]</a> Non-encrypted only</p>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user