mirror of
https://github.com/crskycode/GARbro.git
synced 2025-01-11 12:39:16 +08:00
(EAGLS): auto-detect CG archive encryption scheme.
This commit is contained in:
parent
10e3846f51
commit
dbd95d62e3
@ -2,7 +2,7 @@
|
||||
//! \date Fri May 15 02:52:04 2015
|
||||
//! \brief EAGLS system resource archives.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
// Copyright (C) 2015-2016 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
|
||||
@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Eagls
|
||||
@ -45,8 +46,11 @@ namespace GameRes.Formats.Eagls
|
||||
Extensions = new string[] { "pak" };
|
||||
}
|
||||
|
||||
static readonly string IndexKey = "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-@:^[]";
|
||||
static readonly string Key = "EAGLS_SYSTEM";
|
||||
internal static readonly string IndexKey = "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-@:^[]";
|
||||
internal static readonly string Key = "EAGLS_SYSTEM";
|
||||
|
||||
// FIXME not thread-safe
|
||||
CRuntimeRandomGenerator m_rng = new CRuntimeRandomGenerator();
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
@ -94,45 +98,31 @@ namespace GameRes.Formats.Eagls
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
if (dir[0].Name.EndsWith (".gr", StringComparison.InvariantCultureIgnoreCase)) // CG archive
|
||||
return new CgArchive (file, this, dir);
|
||||
else
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Name.EndsWith (".gr", StringComparison.InvariantCultureIgnoreCase))
|
||||
return DecryptGr (arc, entry);
|
||||
var cg_arc = arc as CgArchive;
|
||||
if (null != cg_arc)
|
||||
return cg_arc.DecryptEntry (entry);
|
||||
if (entry.Name.EndsWith (".dat", StringComparison.InvariantCultureIgnoreCase))
|
||||
return DecryptDat (arc, entry);
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
}
|
||||
|
||||
Stream DecryptGr (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset, input, 0, entry.Size);
|
||||
int seed = 0x75bd924 ^ input[input.Length-1];
|
||||
int limit = Math.Min (input.Length-1, 0x174b);
|
||||
for (int i = 0; i < limit; ++i)
|
||||
{
|
||||
seed = LRand (seed);
|
||||
int index = (int)(seed * 4.656612875245797e-10 * 256);
|
||||
input[i] ^= (byte)Key[index % Key.Length];
|
||||
}
|
||||
return new MemoryStream (input);
|
||||
}
|
||||
|
||||
Stream DecryptDat (ArcFile arc, Entry entry)
|
||||
{
|
||||
byte[] input = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset, input, 0, entry.Size);
|
||||
byte[] input = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
int text_offset = 3600;
|
||||
int text_length = (int)(entry.Size - text_offset - 2);
|
||||
int seed = (sbyte)input[input.Length-1];
|
||||
m_rng.SRand ((sbyte)input[input.Length-1]);
|
||||
for (int i = 0; i < text_length; i += 2)
|
||||
{
|
||||
seed = seed * 0x343FD + 0x269EC3;
|
||||
int index = (int)(((uint)seed >> 16) & 0x7fff);
|
||||
input[text_offset + i] ^= (byte)Key[index % Key.Length];
|
||||
input[text_offset + i] ^= (byte)Key[m_rng.Rand() % Key.Length];
|
||||
}
|
||||
return new MemoryStream (input);
|
||||
}
|
||||
@ -144,15 +134,13 @@ namespace GameRes.Formats.Eagls
|
||||
using (var view = idx.CreateViewAccessor (0, (uint)idx.MaxOffset))
|
||||
unsafe
|
||||
{
|
||||
uint seed = view.ReadUInt32 (idx_size);
|
||||
m_rng.SRand (view.ReadInt32 (idx_size));
|
||||
byte* ptr = view.GetPointer (0);
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < idx_size; ++i)
|
||||
{
|
||||
seed = seed * 0x343FD + 0x269EC3;
|
||||
int index = (int)(seed >> 16) & 0x7FFF;
|
||||
output[i] = (byte)(ptr[i] ^ IndexKey[index % IndexKey.Length]);
|
||||
output[i] = (byte)(ptr[i] ^ IndexKey[m_rng.Rand() % IndexKey.Length]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@ -162,17 +150,97 @@ namespace GameRes.Formats.Eagls
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int LRand (int seed)
|
||||
internal interface IRandomGenerator
|
||||
{
|
||||
void SRand (int seed);
|
||||
int Rand ();
|
||||
}
|
||||
|
||||
internal class CRuntimeRandomGenerator : IRandomGenerator
|
||||
{
|
||||
uint m_seed;
|
||||
|
||||
public void SRand (int seed)
|
||||
{
|
||||
const int A = 48271;
|
||||
const int Q = 44488;
|
||||
const int R = 3399;
|
||||
const int M = 2147483647;
|
||||
seed = A * (seed % Q) - R * (seed / Q);
|
||||
if (seed < 0)
|
||||
seed += M;
|
||||
return seed;
|
||||
m_seed = (uint)seed;
|
||||
}
|
||||
|
||||
public int Rand ()
|
||||
{
|
||||
m_seed = m_seed * 214013u + 2531011u;
|
||||
return (int)(m_seed >> 16) & 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
internal class LehmerRandomGenerator : IRandomGenerator
|
||||
{
|
||||
int m_seed;
|
||||
|
||||
const int A = 48271;
|
||||
const int Q = 44488;
|
||||
const int R = 3399;
|
||||
const int M = 2147483647;
|
||||
|
||||
public void SRand (int seed)
|
||||
{
|
||||
m_seed = seed ^ 123459876;
|
||||
}
|
||||
|
||||
public int Rand ()
|
||||
{
|
||||
m_seed = A * (m_seed % Q) - R * (m_seed / Q);
|
||||
if (m_seed < 0)
|
||||
m_seed += M;
|
||||
return (int)(m_seed * 4.656612875245797e-10 * 256);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CgArchive : ArcFile
|
||||
{
|
||||
IRandomGenerator m_rng;
|
||||
|
||||
public CgArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
m_rng = DetectEncryptionScheme();
|
||||
}
|
||||
|
||||
IRandomGenerator DetectEncryptionScheme ()
|
||||
{
|
||||
var first_entry = Dir.First();
|
||||
int signature = (File.View.ReadInt32 (first_entry.Offset) >> 8) & 0xFFFF;
|
||||
byte seed = File.View.ReadByte (first_entry.Offset+first_entry.Size-1);
|
||||
IRandomGenerator[] rng_list = {
|
||||
new LehmerRandomGenerator(),
|
||||
new CRuntimeRandomGenerator(),
|
||||
};
|
||||
foreach (var rng in rng_list)
|
||||
{
|
||||
rng.SRand (seed);
|
||||
rng.Rand(); // skip LZSS control byte
|
||||
int test = signature;
|
||||
test ^= PakOpener.Key[rng.Rand() % PakOpener.Key.Length];
|
||||
test ^= PakOpener.Key[rng.Rand() % PakOpener.Key.Length] << 8;
|
||||
// FIXME
|
||||
// as key is rather short, this detection could produce false results sometimes
|
||||
if (0x4D42 == test) // 'BM'
|
||||
return rng;
|
||||
}
|
||||
throw new UnknownEncryptionScheme();
|
||||
}
|
||||
|
||||
public Stream DecryptEntry (Entry entry)
|
||||
{
|
||||
var input = File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
m_rng.SRand (input[input.Length-1]);
|
||||
int limit = Math.Min (input.Length-1, 0x174b);
|
||||
for (int i = 0; i < limit; ++i)
|
||||
{
|
||||
input[i] ^= (byte)PakOpener.Key[m_rng.Rand() % PakOpener.Key.Length];
|
||||
}
|
||||
return new MemoryStream (input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -328,6 +328,7 @@ Warusa<br/>
|
||||
<tr class="odd"><td>*.mgd<br/>*.mgs</td><td><tt>MGD</tt><br/><tt>MGS</tt></td><td>No</td><td rowspan="2">MEGU</td><td rowspan="2">Seduce</tr>
|
||||
<tr class="odd"><td>*.agc</td><td><tt>AGd</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pak+*.idx</td><td>-</td><td>No</td><td rowspan="2">EAGLS</td><td rowspan="2">
|
||||
Futago Hinyuu x 3<br/>
|
||||
Oppai Baka<br/>
|
||||
Mainichi Shabutte Ii Desu ka?<br/>
|
||||
Senpai - Oppai - Kako ni Modori Pai<br/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user