mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-24 03:44:13 +08:00
added some XP3 encryption schemes.
This commit is contained in:
parent
f377b0088c
commit
9beff31fee
@ -66,12 +66,16 @@
|
||||
<Compile Include="Abel\ImageGPS.cs" />
|
||||
<Compile Include="Actgs\ArcDAT.cs" />
|
||||
<Compile Include="ArcCG.cs" />
|
||||
<Compile Include="ArcZIP.cs" />
|
||||
<Compile Include="Cri\ArcCPK.cs" />
|
||||
<Compile Include="Cri\AudioHCA.cs" />
|
||||
<Compile Include="Cri\BigEndianReader.cs" />
|
||||
<Compile Include="Cri\ImageXTX.cs" />
|
||||
<Compile Include="Entis\ErisaMatrix.cs" />
|
||||
<Compile Include="Hexenhaus\ArcARCC.cs" />
|
||||
<Compile Include="ImageLZ.cs" />
|
||||
<Compile Include="KiriKiri\ChainReactionCrypt.cs" />
|
||||
<Compile Include="MnoViolet\ImageDIF.cs" />
|
||||
<Compile Include="NitroPlus\ArcNPK.cs" />
|
||||
<Compile Include="SimpleEncryption.cs" />
|
||||
<Compile Include="Softpal\ArcPAC.cs" />
|
||||
|
@ -259,7 +259,10 @@ NextEntry:
|
||||
header.BaseStream.Position = dir_offset;
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
var arc = new ArcFile (file, this, dir);
|
||||
if (crypt_algorithm.IsValueCreated)
|
||||
crypt_algorithm.Value.Init (arc);
|
||||
return arc;
|
||||
}
|
||||
|
||||
static readonly Regex ObfuscatedPathRe = new Regex (@"[^\\/]+[\\/]\.\.[\\/]");
|
||||
|
206
ArcFormats/KiriKiri/ChainReactionCrypt.cs
Normal file
206
ArcFormats/KiriKiri/ChainReactionCrypt.cs
Normal file
@ -0,0 +1,206 @@
|
||||
//! \file ChainReactionCrypt.cs
|
||||
//! \date Mon Mar 07 15:59:47 2016
|
||||
//! \brief KiriKiri XP3 ecryption filter used in some games.
|
||||
//
|
||||
// Copyright (C) 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
|
||||
// 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;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.KiriKiri
|
||||
{
|
||||
// this encryption scheme encrypts first N bytes of file, where N varies depending on file's hash, and
|
||||
// those variations are stored within "plugin/list.bin" file. by default N=512 (used when hash is not
|
||||
// found within "list.bin").
|
||||
//
|
||||
// this implementation looks for "list.bin" upon archive open, parses it and remembers encryption
|
||||
// threshold values in a dictionary.
|
||||
//
|
||||
// such implementation has some flaws, for one, it would fail if "list.bin" is stored within archive other
|
||||
// than one being opened.
|
||||
|
||||
[Serializable]
|
||||
public class ChainReactionCrypt : ICrypt
|
||||
{
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
uint limit = GetEncryptionLimit (entry);
|
||||
uint key = entry.Hash;
|
||||
for (int i = 0; i < count && offset < limit; ++i, ++offset)
|
||||
{
|
||||
values[pos+i] ^= (byte)(offset ^ (key >> (((int)offset & 3) << 3)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
throw new NotImplementedException (Strings.arcStrings.MsgEncNotImplemented);
|
||||
// despite the fact that algorithm is symmetric, creating an archive without updating "list.bin"
|
||||
// wouldn't make much sense
|
||||
// Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
|
||||
uint GetEncryptionLimit (Xp3Entry entry)
|
||||
{
|
||||
uint limit;
|
||||
if (EncryptionThresholdMap != null && EncryptionThresholdMap.TryGetValue (entry.Hash, out limit))
|
||||
return limit;
|
||||
else
|
||||
return 0x200;
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
Dictionary<uint, uint> EncryptionThresholdMap;
|
||||
|
||||
public override void Init (ArcFile arc)
|
||||
{
|
||||
var list_bin = arc.Dir.Where (e => e.Name == "plugin/list.bin").FirstOrDefault() as Xp3Entry;
|
||||
if (null == list_bin || list_bin.UnpackedSize <= 0x30)
|
||||
return;
|
||||
var bin = new byte[list_bin.UnpackedSize];
|
||||
using (var input = arc.OpenEntry (list_bin))
|
||||
input.Read (bin, 0, bin.Length);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
bin = DecodeListBin (bin);
|
||||
if (null == bin)
|
||||
return;
|
||||
}
|
||||
if (null == EncryptionThresholdMap)
|
||||
EncryptionThresholdMap = new Dictionary<uint, uint>();
|
||||
else
|
||||
EncryptionThresholdMap.Clear();
|
||||
|
||||
ParseListBin (bin);
|
||||
}
|
||||
|
||||
void ParseListBin (byte[] data)
|
||||
{
|
||||
using (var mem = new MemoryStream (data))
|
||||
using (var input = new StreamReader (mem))
|
||||
{
|
||||
var converter = new UInt32Converter();
|
||||
string line;
|
||||
while ((line = input.ReadLine()) != null)
|
||||
{
|
||||
if (0 == line.Length || '0' != line[0])
|
||||
continue;
|
||||
var pair = line.Split (',');
|
||||
if (pair.Length > 1)
|
||||
{
|
||||
uint hash = (uint)converter.ConvertFromString (pair[0]);
|
||||
uint threshold = (uint)converter.ConvertFromString (pair[1]);
|
||||
EncryptionThresholdMap[hash] = threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] DecodeListBin (byte[] data)
|
||||
{
|
||||
var header = new byte[0x30];
|
||||
DecodeDPD (data, 0, 0x30, header);
|
||||
int packed_size = LittleEndian.ToInt32 (header, 0x0C);
|
||||
int unpacked_size = LittleEndian.ToInt32 (header, 0x10);
|
||||
if (packed_size <= 0 || packed_size > data.Length-0x30)
|
||||
return null;
|
||||
if (Binary.AsciiEqual (header, 0, "DPDC"))
|
||||
{
|
||||
var decrypted = new byte[packed_size];
|
||||
DecodeDPD (data, 0x30, packed_size, decrypted);
|
||||
return decrypted;
|
||||
}
|
||||
if (Binary.AsciiEqual (header, 0, "SZLC")) // LZSS
|
||||
{
|
||||
using (var input = new MemoryStream (data, 0x30, packed_size))
|
||||
using (var lzss = new LzssReader (input, packed_size, unpacked_size))
|
||||
{
|
||||
lzss.Unpack();
|
||||
return lzss.Data;
|
||||
}
|
||||
}
|
||||
if (Binary.AsciiEqual (header, 0, "ELRC")) // RLE
|
||||
{
|
||||
var unpacked = new byte[unpacked_size];
|
||||
int min_repeat = LittleEndian.ToInt32 (header, 0x1C);
|
||||
DecodeRLE (data, 0x30, packed_size, unpacked, min_repeat);
|
||||
return unpacked;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void DecodeRLE (byte[] input, int offset, int length, byte[] output, int min_repeat)
|
||||
{
|
||||
int src = offset;
|
||||
int src_end = offset+length;
|
||||
int dst = 0;
|
||||
while (src < src_end)
|
||||
{
|
||||
byte b = input[src++];
|
||||
int repeat = 1;
|
||||
while (repeat < min_repeat && src < src_end && input[src] == b)
|
||||
{
|
||||
++repeat;
|
||||
++src;
|
||||
}
|
||||
if (repeat == min_repeat)
|
||||
{
|
||||
byte ctl = input[src++];
|
||||
if (ctl > 0x7F)
|
||||
repeat += input[src++] + ((ctl & 0x7F) << 8) + 0x80;
|
||||
else
|
||||
repeat += ctl;
|
||||
}
|
||||
for (int i = 0; i < repeat; ++i)
|
||||
output[dst++] = b;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe static void DecodeDPD (byte[] src, int offset, int length, byte[] dst)
|
||||
{
|
||||
if (offset > src.Length || length > dst.Length || length > src.Length - offset)
|
||||
throw new IndexOutOfRangeException();
|
||||
if (length < 8)
|
||||
return;
|
||||
int tail = length & 3;
|
||||
if (tail != 0)
|
||||
Buffer.BlockCopy (src, offset+length-tail, dst, length-tail, tail);
|
||||
length /= 4;
|
||||
fixed (byte* src8 = &src[offset], dst8 = dst)
|
||||
{
|
||||
uint* src32 = (uint*)src8;
|
||||
uint* dst32 = (uint*)dst8;
|
||||
for (int i = 0; i < length-1; ++i)
|
||||
{
|
||||
dst32[i] = src32[i] ^ src32[i+1];
|
||||
}
|
||||
dst32[length-1] = dst32[0] ^ src32[length-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -56,6 +56,13 @@ namespace GameRes.Formats.KiriKiri
|
||||
{
|
||||
throw new NotImplementedException (Strings.arcStrings.MsgEncNotImplemented);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform necessary initialization specific to an archive being opened.
|
||||
/// </summary>
|
||||
public virtual void Init (ArcFile arc)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@ -112,6 +119,44 @@ namespace GameRes.Formats.KiriKiri
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MizukakeCrypt : ICrypt
|
||||
{
|
||||
public override bool HashAfterCrypt { get { return true; } }
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
if (offset <= 0x103 && offset + count > 0x103)
|
||||
values[pos+0x103-offset]--;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= 0xB6;
|
||||
}
|
||||
if (offset > 0x3F82)
|
||||
return;
|
||||
if (offset + count > 0x3F82)
|
||||
values[pos+0x3F82-offset] ^= 1;
|
||||
if (offset > 0x83)
|
||||
return;
|
||||
if (offset + count > 0x83)
|
||||
values[pos+0x83-offset] ^= 3;
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= 0xB6;
|
||||
}
|
||||
if (offset <= 0x3F82 && offset + count > 0x3F82)
|
||||
values[pos+0x3F82-offset] ^= 1;
|
||||
if (offset <= 0x83 && offset + count > 0x83)
|
||||
values[pos+0x83-offset] ^= 3;
|
||||
if (offset <= 0x103 && offset + count > 0x103)
|
||||
values[pos+0x103-offset]++;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HashCrypt : ICrypt
|
||||
{
|
||||
@ -504,7 +549,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class IncubusCrypt : ICrypt
|
||||
public class PoringSoftCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
@ -578,7 +623,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
var ext_bin = new byte[16];
|
||||
Encodings.cp932.GetBytes (ext, 0, Math.Min (4, ext.Length), ext_bin, 0);
|
||||
key = ~LittleEndian.ToUInt32 (ext_bin, 0);
|
||||
if (".asd.ks.tjs".Contains (ext))
|
||||
if (".asd\0.ks\0.tjs\0".Contains (ext+'\0'))
|
||||
return entry.Size;
|
||||
}
|
||||
else
|
||||
|
Binary file not shown.
@ -44,6 +44,7 @@ Salmon Pink<br/>
|
||||
Shisho-san to Issho<br/>
|
||||
Sensei 2<br/>
|
||||
Shoujo Settai<br/>
|
||||
Suzuri-sensei to 26-ko no Ecchi na Oppai<br/>
|
||||
Tsukushite Agechau series<br/>
|
||||
</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>
|
||||
@ -199,6 +200,7 @@ Imouto Style<br/>
|
||||
Inaho no Mirai</br>
|
||||
Mayoeru Futari to Sekai no Subete<br/>
|
||||
Mahoutsukai no Yoru<br/>
|
||||
Nakadashi Hara Maid series<br/>
|
||||
Natsupochi<br/>
|
||||
Nidaime wa ☆ Mahou Shoujo<br/>
|
||||
Nuki Doki!<br/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user