mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
(QLIE): attempt to extract archive key from game EXE resources.
This commit is contained in:
@@ -97,6 +97,7 @@
|
|||||||
<Compile Include="Foster\ArcFA2.cs" />
|
<Compile Include="Foster\ArcFA2.cs" />
|
||||||
<Compile Include="Foster\ImageC24.cs" />
|
<Compile Include="Foster\ImageC24.cs" />
|
||||||
<Compile Include="GameSystem\ArcPureMail.cs" />
|
<Compile Include="GameSystem\ArcPureMail.cs" />
|
||||||
|
<Compile Include="Qlie\DelphiDeserializer.cs" />
|
||||||
<Compile Include="RealLive\ArcKOE.cs" />
|
<Compile Include="RealLive\ArcKOE.cs" />
|
||||||
<Compile Include="Software House Parsley\ArcCG.cs" />
|
<Compile Include="Software House Parsley\ArcCG.cs" />
|
||||||
<Compile Include="Artemis\ArcPFS.cs" />
|
<Compile Include="Artemis\ArcPFS.cs" />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//! \date Mon Jun 15 04:03:18 2015
|
//! \date Mon Jun 15 04:03:18 2015
|
||||||
//! \brief QLIE engine archives implementation.
|
//! \brief QLIE engine archives implementation.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2015 by morkt
|
// Copyright (C) 2015-2017 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -32,6 +32,7 @@ using System.Linq;
|
|||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
using GameRes.Formats.Properties;
|
using GameRes.Formats.Properties;
|
||||||
using GameRes.Formats.Strings;
|
using GameRes.Formats.Strings;
|
||||||
|
using GameRes.Formats.Borland;
|
||||||
|
|
||||||
namespace GameRes.Formats.Qlie
|
namespace GameRes.Formats.Qlie
|
||||||
{
|
{
|
||||||
@@ -136,7 +137,7 @@ namespace GameRes.Formats.Qlie
|
|||||||
use_pack_keyfile = key_file != null;
|
use_pack_keyfile = key_file != null;
|
||||||
// currently, user is prompted to choose encryption scheme only if there's 'key.fkey' file found.
|
// currently, user is prompted to choose encryption scheme only if there's 'key.fkey' file found.
|
||||||
if (use_pack_keyfile)
|
if (use_pack_keyfile)
|
||||||
arc_key = QueryEncryption();
|
arc_key = QueryEncryption (file);
|
||||||
// use_pack_keyfile = null != arc_key;
|
// use_pack_keyfile = null != arc_key;
|
||||||
|
|
||||||
var key_data = file.View.ReadBytes (file.MaxOffset-0x41C, 0x100);
|
var key_data = file.View.ReadBytes (file.MaxOffset-0x41C, 0x100);
|
||||||
@@ -423,10 +424,20 @@ namespace GameRes.Formats.Qlie
|
|||||||
return new GUI.WidgetQLIE();
|
return new GUI.WidgetQLIE();
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] QueryEncryption ()
|
byte[] QueryEncryption (ArcView file)
|
||||||
{
|
{
|
||||||
var options = Query<QlieOptions> (arcStrings.ArcEncryptedNotice);
|
var title = FormatCatalog.Instance.LookupGame (file.Name, @"..\*.exe");
|
||||||
return options.GameKeyData;
|
byte[] key = null;
|
||||||
|
if (!string.IsNullOrEmpty (title))
|
||||||
|
key = GetKeyData (title);
|
||||||
|
if (null == key)
|
||||||
|
key = GuessKeyData (file.Name);
|
||||||
|
if (null == key)
|
||||||
|
{
|
||||||
|
var options = Query<QlieOptions> (arcStrings.ArcEncryptedNotice);
|
||||||
|
key = options.GameKeyData;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] GetKeyData (string scheme)
|
static byte[] GetKeyData (string scheme)
|
||||||
@@ -457,5 +468,46 @@ namespace GameRes.Formats.Qlie
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] GuessKeyData (string arc_name)
|
||||||
|
{
|
||||||
|
if (VFS.IsVirtual)
|
||||||
|
return null;
|
||||||
|
// XXX add button to query dialog like with CatSystem?
|
||||||
|
var pattern = VFS.CombinePath (VFS.GetDirectoryName (arc_name), @"..\*.exe");
|
||||||
|
foreach (var file in VFS.GetFiles (pattern))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var key = GetKeyDataFromExe (file.Name);
|
||||||
|
if (key != null)
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
catch { /* ignore errors */ }
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetKeyDataFromExe (string filename)
|
||||||
|
{
|
||||||
|
using (var exe = new ExeFile.ResourceAccessor (filename))
|
||||||
|
{
|
||||||
|
var tform = exe.GetResource ("TFORM1", "#10");
|
||||||
|
if (null == tform || !tform.AsciiEqual (0, "TPF0"))
|
||||||
|
return null;
|
||||||
|
using (var input = new BinMemoryStream (tform))
|
||||||
|
{
|
||||||
|
var deserializer = new DelphiDeserializer (input);
|
||||||
|
var form = deserializer.Deserialize();
|
||||||
|
var image = form.Contents.FirstOrDefault (n => n.Name == "IconKeyImage");
|
||||||
|
if (null == image)
|
||||||
|
return null;
|
||||||
|
var icon = image.Props["Picture.Data"] as byte[];
|
||||||
|
if (null == icon || icon.Length < 0x106 || !icon.AsciiEqual (0, "\x05TIcon"))
|
||||||
|
return null;
|
||||||
|
return new CowArray<byte> (icon, 6, 0x100).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
147
ArcFormats/Qlie/DelphiDeserializer.cs
Normal file
147
ArcFormats/Qlie/DelphiDeserializer.cs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
//! \file DelphiDeserializer.cs
|
||||||
|
//! \date Wed Feb 22 15:40:33 2017
|
||||||
|
//! \brief Borland Delphi binary data deserializer.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2017 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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Borland
|
||||||
|
{
|
||||||
|
public sealed class DelphiDeserializer
|
||||||
|
{
|
||||||
|
IBinaryStream m_input;
|
||||||
|
|
||||||
|
public Encoding Encoding { get; set; }
|
||||||
|
|
||||||
|
public DelphiDeserializer (IBinaryStream input)
|
||||||
|
{
|
||||||
|
m_input = input;
|
||||||
|
Encoding = Encodings.cp932;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DelphiObject Deserialize ()
|
||||||
|
{
|
||||||
|
if (m_input.ReadUInt32() != 0x30465054) // 'TPF0'
|
||||||
|
return null;
|
||||||
|
return DeserializeNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
DelphiObject DeserializeNode ()
|
||||||
|
{
|
||||||
|
int type_len = m_input.ReadByte();
|
||||||
|
if (type_len <= 0)
|
||||||
|
return null;
|
||||||
|
var node = new DelphiObject();
|
||||||
|
node.Type = ReadString (type_len);
|
||||||
|
node.Name = ReadString();
|
||||||
|
int key_length;
|
||||||
|
while ((key_length = m_input.ReadUInt8()) > 0)
|
||||||
|
{
|
||||||
|
var key = ReadString (key_length);
|
||||||
|
node.Props[key] = ReadValue();
|
||||||
|
}
|
||||||
|
DelphiObject child;
|
||||||
|
while ((child = DeserializeNode()) != null)
|
||||||
|
{
|
||||||
|
node.Contents.Add (child);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
object ReadValue ()
|
||||||
|
{
|
||||||
|
int type = m_input.ReadUInt8();
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 2: return (int)m_input.ReadUInt8();
|
||||||
|
case 3: return (int)m_input.ReadUInt16();
|
||||||
|
case 5: return ReadLongDouble();
|
||||||
|
case 6:
|
||||||
|
case 7: return ReadString();
|
||||||
|
case 8:
|
||||||
|
case 9: return true;
|
||||||
|
case 10: return ReadByteString();
|
||||||
|
case 11: return ReadStringArray();
|
||||||
|
case 18: return ReadUnicodeString();
|
||||||
|
default: throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReadString ()
|
||||||
|
{
|
||||||
|
return ReadString (m_input.ReadUInt8());
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReadString (int length)
|
||||||
|
{
|
||||||
|
return m_input.ReadCString (length, Encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
string ReadUnicodeString ()
|
||||||
|
{
|
||||||
|
int length = m_input.ReadInt32();
|
||||||
|
if (length < 0)
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
if (0 == length)
|
||||||
|
return "";
|
||||||
|
var bytes = m_input.ReadBytes (length * 2);
|
||||||
|
return Encoding.Unicode.GetString (bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] ReadByteString ()
|
||||||
|
{
|
||||||
|
int length = m_input.ReadInt32();
|
||||||
|
if (length < 0)
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
if (0 == length)
|
||||||
|
return new byte[0];
|
||||||
|
return m_input.ReadBytes (length);
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<string> ReadStringArray ()
|
||||||
|
{
|
||||||
|
var list = new List<string>();
|
||||||
|
int length;
|
||||||
|
while ((length = m_input.ReadUInt8()) > 0)
|
||||||
|
{
|
||||||
|
list.Add (ReadString (length));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
object ReadLongDouble ()
|
||||||
|
{
|
||||||
|
return m_input.ReadBytes (10); // long double deserialization not implemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DelphiObject
|
||||||
|
{
|
||||||
|
public string Type;
|
||||||
|
public string Name;
|
||||||
|
public IDictionary Props = new Hashtable();
|
||||||
|
public IList<DelphiObject> Contents = new List<DelphiObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user