mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-07 06:08:47 +08:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e035b7c25 | ||
|
|
cb536f040e | ||
|
|
dcedc79678 | ||
|
|
1d59b50ea1 | ||
|
|
857f4c544b | ||
|
|
e783d26957 | ||
|
|
62ccf44288 | ||
|
|
84a94d1636 | ||
|
|
0bb857876b | ||
|
|
9d2eb27383 | ||
|
|
ac27791ea3 | ||
|
|
4517bd55e8 | ||
|
|
883cbb2954 | ||
|
|
da1762dc64 | ||
|
|
64ba3f8294 | ||
|
|
703b097de5 | ||
|
|
da61e5e3ba | ||
|
|
bfe38d3ef8 | ||
|
|
13f53affbb | ||
|
|
90941a0ba6 | ||
|
|
d54305d3ff | ||
|
|
d7d49c010b | ||
|
|
b713a01031 | ||
|
|
99ab258678 | ||
|
|
c29aee53b8 | ||
|
|
d4132c2cac | ||
|
|
09793a08d8 | ||
|
|
271ede6f07 | ||
|
|
81be6901a3 | ||
|
|
cb2b6e2f43 | ||
|
|
481b0995a7 | ||
|
|
afb13290f5 | ||
|
|
d6e441fe64 | ||
|
|
651aad92a2 | ||
|
|
0b80548a81 | ||
|
|
fd55204172 | ||
|
|
532f58625c | ||
|
|
4b0506c9d0 | ||
|
|
f40226866c | ||
|
|
8724e8131c | ||
|
|
c499d4c707 | ||
|
|
b574d2fb96 | ||
|
|
c00747a38b | ||
|
|
e1bfe9cb09 | ||
|
|
40575baacb | ||
|
|
ad16b754ae | ||
|
|
3ad1c60dde | ||
|
|
b4ec3d229e | ||
|
|
cbab8b200a | ||
|
|
37539cc083 | ||
|
|
eb53552bbc |
@@ -62,6 +62,7 @@
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -93,6 +94,8 @@
|
||||
<Compile Include="Aoi\ImageAGF.cs" />
|
||||
<Compile Include="ArcARCX.cs" />
|
||||
<Compile Include="Artemis\ArcMJA.cs" />
|
||||
<Compile Include="BeF\AudioVZY.cs" />
|
||||
<Compile Include="BeF\ImageALO.cs" />
|
||||
<Compile Include="BlackRainbow\ArcCCF.cs" />
|
||||
<Compile Include="BlackRainbow\ArcIMP.cs" />
|
||||
<Compile Include="BlackRainbow\ArcSPPAK.cs" />
|
||||
@@ -107,12 +110,16 @@
|
||||
<Compile Include="Foster\ImageC24.cs" />
|
||||
<Compile Include="GameSystem\ArcPureMail.cs" />
|
||||
<Compile Include="GameSystem\AudioADP4.cs" />
|
||||
<Compile Include="ImagePSM.cs" />
|
||||
<Compile Include="Kaas\ArcPB.cs" />
|
||||
<Compile Include="Kaas\AudioKAAS.cs" />
|
||||
<Compile Include="Kaguya\ArcAN21.cs" />
|
||||
<Compile Include="Kaguya\ArcUF.cs" />
|
||||
<Compile Include="KiriKiri\CzCrypt.cs" />
|
||||
<Compile Include="Kurumi\ImageGRA.cs" />
|
||||
<Compile Include="Leaf\ArcPX.cs" />
|
||||
<Compile Include="Littlewitch\ArcDAT.cs" />
|
||||
<Compile Include="LunaSoft\ArcPAC.cs" />
|
||||
<Compile Include="Malie\ArcLIBU.cs" />
|
||||
<Compile Include="Malie\LibScheme.cs" />
|
||||
<Compile Include="Malie\MalieEncryption.cs" />
|
||||
@@ -124,6 +131,8 @@
|
||||
<Compile Include="Qlie\Encryption.cs" />
|
||||
<Compile Include="RealLive\ArcKOE.cs" />
|
||||
<Compile Include="rUGP\AudioRHA.cs" />
|
||||
<Compile Include="Lilim\ArcFGA.cs" />
|
||||
<Compile Include="Slg\ArcSPD.cs" />
|
||||
<Compile Include="Software House Parsley\ArcCG.cs" />
|
||||
<Compile Include="Artemis\ArcPFS.cs" />
|
||||
<Compile Include="AudioWMA.cs" />
|
||||
|
||||
@@ -168,21 +168,9 @@ namespace GameRes.Formats.Neko
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = FormatCatalog.Instance.DataDirectory;
|
||||
var lst_file = Path.Combine (dir, "nekopack.lst");
|
||||
if (!File.Exists (lst_file))
|
||||
return new string[0];
|
||||
using (var input = new StreamReader (lst_file))
|
||||
{
|
||||
var names = new List<string>();
|
||||
string line;
|
||||
while ((line = input.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length > 0)
|
||||
names.Add (line);
|
||||
}
|
||||
return names.ToArray();
|
||||
}
|
||||
var names = new List<string>();
|
||||
FormatCatalog.Instance.ReadFileList ("nekopack.lst", name => names.Add (name));
|
||||
return names.ToArray();
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
|
||||
@@ -112,43 +112,25 @@ namespace GameRes.Formats
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom implementation of MediaFoundationReader.
|
||||
/// NAudio uses MFCreateMFByteStreamOnStreamEx API call which is not available in Windows 7.
|
||||
/// Custom implementation of StreamMediaFoundationReader.
|
||||
/// Extends it with Duration property.
|
||||
/// </summary>
|
||||
internal class CustomMediaFoundationReader : MediaFoundationReader
|
||||
internal class CustomMediaFoundationReader : StreamMediaFoundationReader
|
||||
{
|
||||
private readonly Stream m_stream;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the duration of a presentation, in 100-nanosecond units.
|
||||
/// </summary>
|
||||
public long Duration { get; private set; }
|
||||
|
||||
public CustomMediaFoundationReader (Stream stream, MediaFoundationReaderSettings settings = null)
|
||||
: base (stream, settings)
|
||||
{
|
||||
m_stream = stream;
|
||||
Init (settings);
|
||||
}
|
||||
|
||||
protected override IMFSourceReader CreateReader (MediaFoundationReaderSettings settings)
|
||||
{
|
||||
const int MF_SOURCE_READER_ALL_STREAMS = -2;
|
||||
const int MF_SOURCE_READER_FIRST_AUDIO_STREAM = -3;
|
||||
|
||||
IMFByteStream byteStream;
|
||||
MFCreateMFByteStreamOnStream (new ComStream (m_stream), out byteStream);
|
||||
var source_reader = MediaFoundationApi.CreateSourceReaderFromByteStream (byteStream);
|
||||
|
||||
source_reader.SetStreamSelection (MF_SOURCE_READER_ALL_STREAMS, false);
|
||||
source_reader.SetStreamSelection (MF_SOURCE_READER_FIRST_AUDIO_STREAM, true);
|
||||
source_reader.SetCurrentMediaType (MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, new MediaType
|
||||
{
|
||||
MajorType = MediaTypes.MFMediaType_Audio,
|
||||
SubType = settings.RequestFloatOutput ? AudioSubtypes.MFAudioFormat_Float : AudioSubtypes.MFAudioFormat_PCM
|
||||
}.MediaFoundationObject);
|
||||
|
||||
var source_reader = base.CreateReader (settings);
|
||||
Duration = GetDuration (source_reader);
|
||||
|
||||
return source_reader;
|
||||
}
|
||||
|
||||
@@ -173,96 +155,5 @@ namespace GameRes.Formats
|
||||
Marshal.FreeHGlobal (variantPtr);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
|
||||
static extern void MFCreateMFByteStreamOnStream (IStream pStream, out IMFByteStream ppByteStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of Com IStream.
|
||||
/// NAudio implementation is inaccessible (private).
|
||||
/// </summary>
|
||||
internal class ComStream : ProxyStream, IStream
|
||||
{
|
||||
public ComStream (Stream stream) : base (Synchronized (stream))
|
||||
{
|
||||
}
|
||||
|
||||
void IStream.Clone (out IStream ppstm)
|
||||
{
|
||||
ppstm = null;
|
||||
}
|
||||
|
||||
void IStream.Commit (int grfCommitFlags)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
|
||||
void IStream.CopyTo (IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
|
||||
{
|
||||
}
|
||||
|
||||
void IStream.LockRegion (long libOffset, long cb, int dwLockType)
|
||||
{
|
||||
}
|
||||
|
||||
void IStream.Read (byte[] pv, int cb, IntPtr pcbRead)
|
||||
{
|
||||
if (!CanRead)
|
||||
throw new InvalidOperationException ("Stream is not readable.");
|
||||
int read = Read (pv, 0, cb);
|
||||
if (pcbRead != IntPtr.Zero)
|
||||
Marshal.WriteInt32 (pcbRead, read);
|
||||
}
|
||||
|
||||
void IStream.Revert()
|
||||
{
|
||||
}
|
||||
|
||||
void IStream.Seek (long dlibMove, int dwOrigin, IntPtr plibNewPosition)
|
||||
{
|
||||
SeekOrigin origin = (SeekOrigin) dwOrigin;
|
||||
long val = Seek (dlibMove, origin);
|
||||
if (plibNewPosition != IntPtr.Zero)
|
||||
Marshal.WriteInt64 (plibNewPosition, val);
|
||||
}
|
||||
|
||||
void IStream.SetSize (long libNewSize)
|
||||
{
|
||||
SetLength (libNewSize);
|
||||
}
|
||||
|
||||
void IStream.Stat (out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
|
||||
{
|
||||
const int STGM_READ = 0x00000000;
|
||||
const int STGM_WRITE = 0x00000001;
|
||||
const int STGM_READWRITE = 0x00000002;
|
||||
|
||||
var tmp = new System.Runtime.InteropServices.ComTypes.STATSTG { type = 2, cbSize = Length, grfMode = 0 };
|
||||
|
||||
if (CanWrite && CanRead)
|
||||
tmp.grfMode |= STGM_READWRITE;
|
||||
else if (CanRead)
|
||||
tmp.grfMode |= STGM_READ;
|
||||
else if (CanWrite)
|
||||
tmp.grfMode |= STGM_WRITE;
|
||||
else
|
||||
throw new ObjectDisposedException ("Stream");
|
||||
|
||||
pstatstg = tmp;
|
||||
}
|
||||
|
||||
void IStream.UnlockRegion (long libOffset, long cb, int dwLockType)
|
||||
{
|
||||
}
|
||||
|
||||
void IStream.Write (byte[] pv, int cb, IntPtr pcbWritten)
|
||||
{
|
||||
if (!CanWrite)
|
||||
throw new InvalidOperationException ("Stream is not writeable.");
|
||||
Write (pv, 0, cb);
|
||||
if (pcbWritten != IntPtr.Zero)
|
||||
Marshal.WriteInt32 (pcbWritten, cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
ArcFormats/BeF/AudioVZY.cs
Normal file
73
ArcFormats/BeF/AudioVZY.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
//! \file AudioVZY.cs
|
||||
//! \date 2017 Oct 22
|
||||
//! \brief Obfuscated WAVE file.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.BeF
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class VzyAudio : AudioFormat
|
||||
{
|
||||
public override string Tag { get { return "VZY"; } }
|
||||
public override string Description { get { return "Obfuscated WAVE audio"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x12);
|
||||
if (!header.AsciiEqual ("\0\0\0\0"))
|
||||
return null;
|
||||
int riff_length = header.ToInt32 (4);
|
||||
if (file.Length != riff_length+8)
|
||||
return null;
|
||||
if (!header.AsciiEqual (8, "\0\0\0\0\0\0\0\0"))
|
||||
return null;
|
||||
int header_length = header.ToUInt16 (0x10);
|
||||
if (header_length < 0x10 || header_length > riff_length)
|
||||
return null;
|
||||
header = file.ReadHeader (0x18+header_length);
|
||||
if (!header.AsciiEqual (0x14+header_length, "data"))
|
||||
return null;
|
||||
var header_bytes = new byte[0x10] {
|
||||
(byte)'R', (byte)'I', (byte)'F', (byte)'F', header[4], header[5], header[6], header[7],
|
||||
(byte)'W', (byte)'A', (byte)'V', (byte)'E', (byte)'f', (byte)'m', (byte)'t', (byte)' '
|
||||
};
|
||||
Stream riff = new StreamRegion (file.AsStream, 0x10);
|
||||
riff = new PrefixStream (header_bytes, riff);
|
||||
var wav = new BinaryStream (riff, file.Name);
|
||||
try
|
||||
{
|
||||
return Wav.TryOpen (wav);
|
||||
}
|
||||
catch
|
||||
{
|
||||
wav.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
ArcFormats/BeF/ImageALO.cs
Normal file
76
ArcFormats/BeF/ImageALO.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
//! \file ImageALO.cs
|
||||
//! \date 2017 Oct 22
|
||||
//! \brief Ancient obfuscated BMP image.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.BeF
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class AloFormat : BmpFormat
|
||||
{
|
||||
public override string Tag { get { return "ALO"; } }
|
||||
public override string Description { get { return "Obfuscated bitmap"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
if (!stream.Name.HasExtension (".alo"))
|
||||
return null;
|
||||
var header = stream.ReadHeader (2);
|
||||
if (0 != header[0] || 0 != header[1])
|
||||
return null;
|
||||
using (var bmp = OpenAsBitmap (stream))
|
||||
return base.ReadMetaData (bmp);
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
||||
{
|
||||
using (var bmp = OpenAsBitmap (stream))
|
||||
return base.Read (bmp, info);
|
||||
}
|
||||
|
||||
IBinaryStream OpenAsBitmap (IBinaryStream input)
|
||||
{
|
||||
var header = new byte[2] { (byte)'B', (byte)'M' };
|
||||
Stream stream = new StreamRegion (input.AsStream, 2, true);
|
||||
stream = new PrefixStream (header, stream);
|
||||
return new BinaryStream (stream, input.Name);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
using (var bmp = new MemoryStream())
|
||||
{
|
||||
base.Write (bmp, image);
|
||||
file.WriteByte (0);
|
||||
file.WriteByte (0);
|
||||
bmp.Position = 2;
|
||||
bmp.CopyTo (file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace GameRes.Formats.CatSystem
|
||||
return null;
|
||||
if (file.View.AsciiEqual (8, "__key__.dat\x00"))
|
||||
{
|
||||
uint? key = QueryEncryptionInfo();
|
||||
uint? key = QueryEncryptionInfo (file.Name);
|
||||
if (null == key)
|
||||
throw new UnknownEncryptionScheme();
|
||||
return OpenEncrypted (file, entry_count, key.Value);
|
||||
@@ -274,8 +274,11 @@ namespace GameRes.Formats.CatSystem
|
||||
return new GUI.CreateINTWidget();
|
||||
}
|
||||
|
||||
uint? QueryEncryptionInfo ()
|
||||
uint? QueryEncryptionInfo (string arc_name)
|
||||
{
|
||||
var title = FormatCatalog.Instance.LookupGame (arc_name);
|
||||
if (!string.IsNullOrEmpty (title) && KnownSchemes.ContainsKey (title))
|
||||
return KnownSchemes[title].Key;
|
||||
var options = Query<IntOptions> (arcStrings.INTNotice);
|
||||
return options.EncryptionInfo.GetKey();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace GameRes.Formats.GUI
|
||||
CheckPathExists = true,
|
||||
Multiselect = false,
|
||||
Title = arcStrings.INTChooseExe,
|
||||
Filter = arcStrings.INTExeFiles+"|*.exe",
|
||||
Filter = arcStrings.INTExeFiles+"|*.exe;*.bin",
|
||||
FilterIndex = 1,
|
||||
InitialDirectory = Directory.GetCurrentDirectory(),
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace GameRes.Formats.Cyberworks
|
||||
{
|
||||
if (!index.Read())
|
||||
return null;
|
||||
return new BellArchive (file, this, index.Dir, null);
|
||||
return ArchiveFromDir (file, index.Dir, index.HasImages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,11 +167,19 @@ namespace GameRes.Formats.Cyberworks
|
||||
var header = ReadHeader();
|
||||
if (0 == Info.Width || Info.Width >= 0x8000 || 0 == Info.Height || Info.Height >= 0x8000)
|
||||
throw new InvalidFormatException();
|
||||
int unpacked_size = header[5];
|
||||
if (unpacked_size <= 0)
|
||||
throw new InvalidFormatException();
|
||||
int flags = header[0];
|
||||
int unpacked_size = header[5];
|
||||
int bits_size = header[7];
|
||||
if (unpacked_size <= 0)
|
||||
{
|
||||
if (0 == unpacked_size && 0 == header[6]
|
||||
&& (1 == (flags & 1) && Baseline != null))
|
||||
{
|
||||
UnpackV6NoAlpha (bits_size);
|
||||
return;
|
||||
}
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
int data_offset = bits_size * 2;
|
||||
if (0 == flags)
|
||||
CopyV0 (unpacked_size);
|
||||
@@ -399,6 +407,35 @@ namespace GameRes.Formats.Cyberworks
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV6NoAlpha (int bits_size)
|
||||
{
|
||||
var rgb_map = m_input.ReadBytes (bits_size);
|
||||
int plane_size = Math.Min (Baseline.Length, bits_size*8);
|
||||
m_output = Baseline;
|
||||
if (m_info.Width * m_info.Height * 3 == m_output.Length)
|
||||
Info.BPP = 24;
|
||||
else
|
||||
Info.BPP = 32;
|
||||
int bit = 1;
|
||||
int bit_src = 0;
|
||||
int dst = 0;
|
||||
int pixel_size = Info.BPP / 8;
|
||||
for (int i = 0; i < plane_size; ++i)
|
||||
{
|
||||
if ((bit & rgb_map[bit_src]) != 0)
|
||||
{
|
||||
m_input.Read (m_output, dst, 3);
|
||||
}
|
||||
dst += pixel_size;
|
||||
bit <<= 1;
|
||||
if (0x100 == bit)
|
||||
{
|
||||
++bit_src;
|
||||
bit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GetInt ()
|
||||
{
|
||||
byte a = m_input.ReadUInt8();
|
||||
|
||||
@@ -260,7 +260,6 @@ namespace GameRes.Formats.DxLib
|
||||
if (0x2C != header.Length)
|
||||
return null;
|
||||
Decrypt (header, 0, header.Length, 4, key);
|
||||
Dump.Write (header);
|
||||
return new DxHeader {
|
||||
IndexSize = LittleEndian.ToUInt32 (header, 0),
|
||||
BaseOffset = LittleEndian.ToInt64 (header, 4),
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace GameRes.Formats.Entis
|
||||
PtrProcedure[] m_pfnColorOperation;
|
||||
byte[] m_src_frame;
|
||||
|
||||
public EriMetaData Info { get { return m_info; } }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public PixelFormat Format { get; private set; }
|
||||
public int Stride { get { return Math.Abs (m_dwBytesPerLine); } }
|
||||
@@ -150,6 +151,35 @@ namespace GameRes.Formats.Entis
|
||||
};
|
||||
}
|
||||
|
||||
internal void AddImageBuffer (EriReader src_reader)
|
||||
{
|
||||
var dst_reader = this;
|
||||
var dst_img = dst_reader.Data;
|
||||
var src_img = src_reader.Data;
|
||||
int src_bpp = (src_reader.Info.BPP + 7) / 8;
|
||||
int dst_bpp = (dst_reader.Info.BPP + 7) / 8;
|
||||
bool has_alpha = src_bpp == 4 && src_bpp == dst_bpp;
|
||||
int src_stride = src_reader.Stride;
|
||||
int dst_stride = dst_reader.Stride;
|
||||
int height = (int)dst_reader.Info.Height;
|
||||
int width = (int)dst_reader.Info.Width;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
int src = src_stride * y;
|
||||
int dst = dst_stride * y;
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
dst_img[dst ] += src_img[src ];
|
||||
dst_img[dst+1] += src_img[src+1];
|
||||
dst_img[dst+2] += src_img[src+2];
|
||||
if (has_alpha)
|
||||
dst_img[dst+3] += src_img[src+3];
|
||||
dst += dst_bpp;
|
||||
src += src_bpp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeLossless ()
|
||||
{
|
||||
if (0 != m_info.BlockingDegree)
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace GameRes.Formats.Entis
|
||||
throw new FileNotFoundException ("Referenced image not found", ref_file);
|
||||
ref_info.FileName = ref_file;
|
||||
var ref_reader = ReadImageData (ref_src, ref_info);
|
||||
AddImageBuffer (meta, reader.Data, ref_info, ref_reader.Data);
|
||||
reader.AddImageBuffer (ref_reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,25 +304,6 @@ namespace GameRes.Formats.Entis
|
||||
return reader;
|
||||
}
|
||||
|
||||
void AddImageBuffer (EriMetaData dst_info, byte[] dst_img, EriMetaData src_info, byte[] src_img)
|
||||
{
|
||||
int src_bpp = (src_info.BPP + 7) / 8;
|
||||
int dst_bpp = (dst_info.BPP + 7) / 8;
|
||||
bool has_alpha = src_bpp == 4 && src_bpp == dst_bpp;
|
||||
int dst = 0;
|
||||
int src = 0;
|
||||
while (dst < dst_img.Length)
|
||||
{
|
||||
dst_img[dst ] += src_img[src ];
|
||||
dst_img[dst+1] += src_img[src+1];
|
||||
dst_img[dst+2] += src_img[src+2];
|
||||
if (has_alpha)
|
||||
dst_img[dst+3] += src_img[src+3];
|
||||
dst += dst_bpp;
|
||||
src += src_bpp;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Regex s_TagRe = new Regex (@"^\s*#\s*(\S+)");
|
||||
|
||||
Dictionary<string, string> ParseTagInfo (string desc)
|
||||
|
||||
@@ -85,7 +85,13 @@ namespace GameRes.Formats.ExHibit
|
||||
bool arc_found = false;
|
||||
for (int i = 0; i < res_count && index_offset < toc_file.MaxOffset; ++i)
|
||||
{
|
||||
if (toc_file.View.ReadInt32 (index_offset) == arc_index)
|
||||
int num = toc_file.View.ReadInt32 (index_offset);
|
||||
if (0x01000000 == num)
|
||||
{
|
||||
index_offset += 4;
|
||||
num = toc_file.View.ReadInt32 (index_offset);
|
||||
}
|
||||
if (num == arc_index)
|
||||
{
|
||||
arc_found = true;
|
||||
break;
|
||||
|
||||
63
ArcFormats/ImagePSM.cs
Normal file
63
ArcFormats/ImagePSM.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! \file ImagePSM.cs
|
||||
//! \date Fri Jul 07 19:16:57 2017
|
||||
//! \brief Obfuscated PNG image.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Misc
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PsmFormat : PngFormat
|
||||
{
|
||||
public override string Tag { get { return "PSM"; } }
|
||||
public override string Description { get { return "Obfuscated PNG image"; } }
|
||||
public override uint Signature { get { return 0x474E50ED; } }
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
using (var png = DeobfuscateStream (file))
|
||||
return base.Read (png, info);
|
||||
}
|
||||
|
||||
IBinaryStream DeobfuscateStream (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (4).ToArray();
|
||||
header[0] = 0x89;
|
||||
var body = new StreamRegion (file.AsStream, 4, true);
|
||||
var png = new PrefixStream (header, body);
|
||||
return new BinaryStream (png, file.Name);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
var start_pos = file.Position;
|
||||
base.Write (file, image);
|
||||
var end_pos = file.Position;
|
||||
file.Position = start_pos;
|
||||
file.WriteByte (0xED);
|
||||
file.Position = end_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Sep 12 01:39:49 2016
|
||||
//! \brief 'fPK' resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
// Copyright (C) 2016-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
|
||||
@@ -44,75 +44,95 @@ namespace GameRes.Formats.Ivory
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public PakOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x204B5066, 0x324B5066 };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (file.MaxOffset != file.View.ReadUInt32 (4))
|
||||
return null;
|
||||
|
||||
int version = '2' == file.View.ReadByte (3) ? 2 : 1;
|
||||
long base_offset = 0;
|
||||
List<Entry> dir = null;
|
||||
bool got_names = false;
|
||||
long offset = 8;
|
||||
while (offset < file.MaxOffset)
|
||||
byte[] names = null;
|
||||
using (var stream = file.CreateStream())
|
||||
{
|
||||
var id = new AsciiString (file.View.ReadBytes (offset, 4));
|
||||
uint section_size = file.View.ReadUInt32 (offset+4);
|
||||
if (0 == section_size)
|
||||
Func<IBinaryStream, long> read_long;
|
||||
if (2 == version)
|
||||
read_long = s => s.ReadInt64();
|
||||
else
|
||||
read_long = s => s.ReadUInt32();
|
||||
|
||||
stream.Position = 4;
|
||||
if (file.MaxOffset != read_long (stream))
|
||||
return null;
|
||||
uint header_size = file.View.ReadUInt32 (offset+8);
|
||||
if ("cLST" == id)
|
||||
for (;;)
|
||||
{
|
||||
int count = file.View.ReadInt32 (offset+0x10);
|
||||
if (!IsSaneCount (count))
|
||||
long section_start = stream.Position;
|
||||
var id_bytes = stream.ReadBytes (4);
|
||||
if (0 == id_bytes.Length)
|
||||
break;
|
||||
var id = new AsciiString (id_bytes);
|
||||
var section_size = read_long (stream);
|
||||
var header_size = read_long (stream);
|
||||
if (section_size < 4 || header_size > section_size)
|
||||
return null;
|
||||
uint key = file.View.ReadUInt32 (offset+0x14);
|
||||
var clst = file.View.ReadBytes (offset+header_size, section_size - header_size);
|
||||
Decrypt (clst, key);
|
||||
dir = new List<Entry> (count);
|
||||
int index_offset = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
var content_pos = section_start + header_size;
|
||||
if ("cLST" == id)
|
||||
{
|
||||
var entry = new PkEntry {
|
||||
NameOffset = LittleEndian.ToInt32 (clst, index_offset),
|
||||
Offset = LittleEndian.ToUInt32 (clst, index_offset+4),
|
||||
Size = LittleEndian.ToUInt32 (clst, index_offset+8),
|
||||
};
|
||||
dir.Add (entry);
|
||||
index_offset += 12;
|
||||
stream.ReadUInt32();
|
||||
int count = stream.ReadInt32();
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint key = stream.ReadUInt32();
|
||||
stream.Position = content_pos;
|
||||
var clst = stream.ReadBytes ((int)(section_size - header_size));
|
||||
Decrypt (clst, key);
|
||||
dir = new List<Entry> (count);
|
||||
using (var index = new BinMemoryStream (clst))
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new PkEntry {
|
||||
NameOffset = (int)read_long (index),
|
||||
Offset = (long)read_long (index),
|
||||
Size = (uint)read_long (index),
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("cNAM" == id)
|
||||
{
|
||||
if (null == dir)
|
||||
return null;
|
||||
uint key = file.View.ReadUInt32 (offset+0x10);
|
||||
var cnam = file.View.ReadBytes (offset+header_size, section_size - header_size);
|
||||
Decrypt (cnam, key);
|
||||
foreach (PkEntry entry in dir)
|
||||
else if ("cNAM" == id)
|
||||
{
|
||||
var name = Binary.GetCString (cnam, entry.NameOffset);
|
||||
entry.Name = name;
|
||||
if (name.HasExtension (".px"))
|
||||
entry.Type = "audio";
|
||||
else
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
|
||||
if (null == dir)
|
||||
return null;
|
||||
stream.ReadUInt32();
|
||||
uint key = stream.ReadUInt32();
|
||||
stream.Position = content_pos;
|
||||
names = stream.ReadBytes ((int)(section_size - header_size));
|
||||
Decrypt (names, key);
|
||||
}
|
||||
got_names = true;
|
||||
else if ("cDAT" == id)
|
||||
{
|
||||
base_offset = content_pos;
|
||||
}
|
||||
stream.Position = section_start + section_size;
|
||||
}
|
||||
else if ("cDAT" == id)
|
||||
{
|
||||
base_offset = offset + header_size;
|
||||
}
|
||||
offset += section_size;
|
||||
}
|
||||
if (null == dir || !got_names || 0 == base_offset)
|
||||
if (null == dir || null == names || 0 == base_offset)
|
||||
return null;
|
||||
|
||||
foreach (var entry in dir)
|
||||
foreach (PkEntry entry in dir)
|
||||
{
|
||||
entry.Offset += base_offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
var name = Binary.GetCString (names, entry.NameOffset);
|
||||
entry.Name = name;
|
||||
if (name.HasExtension (".px"))
|
||||
entry.Type = "audio";
|
||||
else
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
@@ -169,6 +169,7 @@ namespace GameRes.Formats.Ivory
|
||||
else
|
||||
UnpackV2();
|
||||
break;
|
||||
case 3: UnpackV3(); break;
|
||||
default:
|
||||
throw new NotImplementedException (string.Format ("sRGB image type {0} not implemented", m_info.RgbMode));
|
||||
}
|
||||
@@ -343,6 +344,45 @@ namespace GameRes.Formats.Ivory
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV3 ()
|
||||
{
|
||||
Format = 3 == m_channels ? PixelFormats.Bgr24 : PixelFormats.Bgra32;
|
||||
m_stride = m_width * m_channels;
|
||||
m_output = new byte[m_stride * m_height];
|
||||
var index = new int[m_height];
|
||||
for (int i = 0; i < m_height; ++i)
|
||||
index[i] = m_input.ReadInt32();
|
||||
var data_pos = m_input.Position;
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_height; ++y)
|
||||
{
|
||||
m_input.Position = data_pos + index[y];
|
||||
for (int x = 0; x < m_width; )
|
||||
{
|
||||
int ctl = m_input.ReadUInt8();
|
||||
int count = ctl & 0x3F;
|
||||
if (0 != (ctl & 0x40))
|
||||
{
|
||||
count <<= 8;
|
||||
count |= m_input.ReadUInt8();
|
||||
}
|
||||
x += count;
|
||||
count *= m_channels;
|
||||
if (0 != (ctl & 0x80))
|
||||
{
|
||||
m_input.Read (m_output, dst, m_channels);
|
||||
if (count > m_channels)
|
||||
Binary.CopyOverlapped (m_output, dst, dst+m_channels, count - m_channels);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (m_output, dst, count);
|
||||
}
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
public void Dispose ()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Wed Jul 16 13:58:17 2014
|
||||
//! \brief KiriKiri engine archive implementation.
|
||||
//
|
||||
// Copyright (C) 2014-2016 by morkt
|
||||
// Copyright (C) 2014-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
|
||||
@@ -344,90 +344,7 @@ NextEntry:
|
||||
else
|
||||
input = new Xp3Stream (arc.File, xp3_entry);
|
||||
|
||||
if (xp3_entry.UnpackedSize <= 5 || "audio" == entry.Type)
|
||||
return input;
|
||||
|
||||
var header = new byte[5];
|
||||
input.Read (header, 0, 5);
|
||||
if (0x184D2204 == header.ToInt32 (0)) // LZ4 magic
|
||||
{
|
||||
// assume no scripts are compressed using LZ4, return decompressed stream right away
|
||||
return DecompressLz4 (xp3_entry, header, input);
|
||||
}
|
||||
|
||||
if (0xFE == header[0] && 0xFE == header[1] && header[2] < 3 && 0xFF == header[3] && 0xFE == header[4])
|
||||
return DecryptScript (header[2], input, xp3_entry.UnpackedSize);
|
||||
|
||||
if (!input.CanSeek)
|
||||
return new PrefixStream (header, input);
|
||||
input.Position = 0;
|
||||
return input;
|
||||
}
|
||||
|
||||
Stream DecompressLz4 (Xp3Entry entry, byte[] header, Stream input)
|
||||
{
|
||||
if (header.Length != 5)
|
||||
throw new ArgumentException ("Invalid header length for DecompressLz4", "header");
|
||||
var info = new Lz4FrameInfo (header[4]);
|
||||
info.SetBlockSize (input.ReadByte());
|
||||
if (info.HasContentLength)
|
||||
{
|
||||
input.Read (header, 0, 4);
|
||||
long length = header.ToUInt32 (0);
|
||||
input.Read (header, 0, 4);
|
||||
length |= (long)header.ToUInt32 (0) << 32;
|
||||
info.OriginalLength = length;
|
||||
entry.UnpackedSize = (uint)length;
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
if (info.HasDictionary)
|
||||
{
|
||||
input.Read (header, 0, 4);
|
||||
info.DictionaryId = header.ToInt32 (0);
|
||||
}
|
||||
input.ReadByte(); // skip descriptor checksum
|
||||
return new Lz4Stream (input, info);
|
||||
}
|
||||
|
||||
Stream DecryptScript (int enc_type, Stream input, uint unpacked_size)
|
||||
{
|
||||
using (var reader = new BinaryReader (input, Encoding.Unicode, true))
|
||||
{
|
||||
if (2 == enc_type)
|
||||
{
|
||||
reader.ReadInt64(); // packed_size
|
||||
reader.ReadInt64(); // unpacked_size
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
var output = new MemoryStream ((int)unpacked_size+2);
|
||||
using (var writer = new BinaryWriter (output, Encoding.Unicode, true))
|
||||
{
|
||||
writer.Write ('\xFEFF'); // BOM
|
||||
int c;
|
||||
if (1 == enc_type)
|
||||
{
|
||||
while ((c = reader.Read()) != -1)
|
||||
{
|
||||
c = (c & 0xAAAA) >> 1 | (c & 0x5555) << 1;
|
||||
writer.Write ((char)c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((c = reader.Read()) != -1)
|
||||
{
|
||||
if (c >= 0x20)
|
||||
{
|
||||
c = c ^ (((c & 0xFE) << 8) ^ 1);
|
||||
writer.Write ((char)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Position = 0;
|
||||
input.Dispose();
|
||||
return output;
|
||||
}
|
||||
return xp3_entry.Cipher.EntryReadFilter (xp3_entry, input);
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions ()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Thu Feb 04 12:08:40 2016
|
||||
//! \brief KiriKiri engine encryption algorithms.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
// Copyright (C) 2016-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
|
||||
@@ -27,6 +27,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.KiriKiri
|
||||
@@ -83,6 +85,96 @@ namespace GameRes.Formats.KiriKiri
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post-process entry stream.
|
||||
/// </summary>
|
||||
public virtual Stream EntryReadFilter (Xp3Entry entry, Stream input)
|
||||
{
|
||||
if (entry.UnpackedSize <= 5 || "audio" == entry.Type)
|
||||
return input;
|
||||
|
||||
var header = new byte[5];
|
||||
input.Read (header, 0, 5);
|
||||
if (0x184D2204 == header.ToInt32 (0)) // LZ4 magic
|
||||
{
|
||||
// assume no scripts are compressed using LZ4, return decompressed stream right away
|
||||
return DecompressLz4 (entry, header, input);
|
||||
}
|
||||
if (0xFE == header[0] && 0xFE == header[1] && header[2] < 3 && 0xFF == header[3] && 0xFE == header[4])
|
||||
return DecryptScript (header[2], input, entry.UnpackedSize);
|
||||
|
||||
if (!input.CanSeek)
|
||||
return new PrefixStream (header, input);
|
||||
input.Position = 0;
|
||||
return input;
|
||||
}
|
||||
|
||||
internal Stream DecompressLz4 (Xp3Entry entry, byte[] header, Stream input)
|
||||
{
|
||||
if (header.Length != 5)
|
||||
throw new ArgumentException ("Invalid header length for DecompressLz4", "header");
|
||||
var info = new Lz4FrameInfo (header[4]);
|
||||
info.SetBlockSize (input.ReadByte());
|
||||
if (info.HasContentLength)
|
||||
{
|
||||
input.Read (header, 0, 4);
|
||||
long length = header.ToUInt32 (0);
|
||||
input.Read (header, 0, 4);
|
||||
length |= (long)header.ToUInt32 (0) << 32;
|
||||
info.OriginalLength = length;
|
||||
entry.UnpackedSize = (uint)length;
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
if (info.HasDictionary)
|
||||
{
|
||||
input.Read (header, 0, 4);
|
||||
info.DictionaryId = header.ToInt32 (0);
|
||||
}
|
||||
input.ReadByte(); // skip descriptor checksum
|
||||
return new Lz4Stream (input, info);
|
||||
}
|
||||
|
||||
internal Stream DecryptScript (int enc_type, Stream input, uint unpacked_size)
|
||||
{
|
||||
using (var reader = new BinaryReader (input, Encoding.Unicode, true))
|
||||
{
|
||||
if (2 == enc_type)
|
||||
{
|
||||
reader.ReadInt64(); // packed_size
|
||||
reader.ReadInt64(); // unpacked_size
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
var output = new MemoryStream ((int)unpacked_size+2);
|
||||
using (var writer = new BinaryWriter (output, Encoding.Unicode, true))
|
||||
{
|
||||
writer.Write ('\xFEFF'); // BOM
|
||||
int c;
|
||||
if (1 == enc_type)
|
||||
{
|
||||
while ((c = reader.Read()) != -1)
|
||||
{
|
||||
c = (c & 0xAAAA) >> 1 | (c & 0x5555) << 1;
|
||||
writer.Write ((char)c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((c = reader.Read()) != -1)
|
||||
{
|
||||
if (c >= 0x20)
|
||||
{
|
||||
c = c ^ (((c & 0xFE) << 8) ^ 1);
|
||||
writer.Write ((char)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Position = 0;
|
||||
input.Dispose();
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -744,7 +836,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
Decrypt (entry, offset, buffer, pos, count);
|
||||
}
|
||||
|
||||
IEnumerable<byte> GetKey (uint hash)
|
||||
internal IEnumerable<byte> GetKey (uint hash)
|
||||
{
|
||||
hash = (hash ^ m_seed) & 0x7FFFFFFF;
|
||||
hash = hash << 31 | hash;
|
||||
@@ -968,7 +1060,23 @@ namespace GameRes.Formats.KiriKiri
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KissCrypt : ICrypt
|
||||
public class NanaCxCrypt : SenrenCxCrypt
|
||||
{
|
||||
uint m_random_seed;
|
||||
|
||||
public NanaCxCrypt (CxScheme scheme, uint seed) : base (scheme)
|
||||
{
|
||||
m_random_seed = seed;
|
||||
}
|
||||
|
||||
internal override CxProgram NewProgram (uint seed)
|
||||
{
|
||||
return new CxProgramNana (seed, m_random_seed, ControlBlock);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KissCrypt : CzCrypt
|
||||
{
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] data, int pos, int count)
|
||||
{
|
||||
@@ -1100,21 +1208,12 @@ namespace GameRes.Formats.KiriKiri
|
||||
|
||||
void ReadNames ()
|
||||
{
|
||||
var dir = FormatCatalog.Instance.DataDirectory;
|
||||
var names_file = Path.Combine (dir, FileListName);
|
||||
var names = new Dictionary<uint, string>();
|
||||
try
|
||||
{
|
||||
using (var reader = new StreamReader (names_file))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
var name = reader.ReadLine();
|
||||
if (null == name)
|
||||
break;
|
||||
names[GetNameHash (name)] = name;
|
||||
}
|
||||
}
|
||||
FormatCatalog.Instance.ReadFileList (FileListName, name => {
|
||||
names[GetNameHash (name)] = name;
|
||||
});
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
@@ -1126,4 +1225,29 @@ namespace GameRes.Formats.KiriKiri
|
||||
[NonSerialized]
|
||||
Dictionary<uint, string> KnownNames = null;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MadoCrypt : AkabeiCrypt
|
||||
{
|
||||
public MadoCrypt (uint seed) : base (seed)
|
||||
{
|
||||
}
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
int key_pos = (int)offset % 0x1F;
|
||||
var key = GetKey (entry.Hash).ElementAt (key_pos);
|
||||
return (byte)(value ^ key);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||
{
|
||||
var key = GetKey (entry.Hash).ToArray();
|
||||
int key_pos = (int)offset;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
buffer[pos+i] ^= key[key_pos++ % 0x1F];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
133
ArcFormats/KiriKiri/CzCrypt.cs
Normal file
133
ArcFormats/KiriKiri/CzCrypt.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
//! \file CzCrypt.cs
|
||||
//! \date 2017 Sep 27
|
||||
//! \brief implementation of cZLIB extraction filter for KiriKiri engine.
|
||||
//
|
||||
// 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;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using GameRes.Compression;
|
||||
|
||||
namespace GameRes.Formats.KiriKiri
|
||||
{
|
||||
/// <summary>
|
||||
/// cZLIB entry filter.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class CzCrypt : ICrypt
|
||||
{
|
||||
const uint CzMagic = 0xA590D7FDu;
|
||||
const uint CzIvSeed = 0xBFBFBFBFu;
|
||||
|
||||
static readonly byte[] CzHeaderKey = { 0x9D, 0x1D, 0x9A, 0xF2 };
|
||||
static readonly byte[] CzDefaultKey = {
|
||||
0x91, 0x10, 0xFC, 0x75, 0x45, 0x8F, 0xB5, 0xE6, 0xFE, 0xAC, 0xBA, 0x44, 0x76, 0x58, 0xC2, 0x1A
|
||||
};
|
||||
|
||||
public override Stream EntryReadFilter (Xp3Entry entry, Stream input)
|
||||
{
|
||||
if (entry.UnpackedSize <= 15 || "audio" == entry.Type)
|
||||
return input;
|
||||
|
||||
var header = new byte[15];
|
||||
input.Read (header, 0, 15);
|
||||
if (CzMagic == header.ToUInt32 (0))
|
||||
{
|
||||
var type = new char[3] {
|
||||
(char)(header[4] ^ 0x11),
|
||||
(char)(header[5] ^ 0x7F),
|
||||
(char)(header[6] ^ 0x9A)
|
||||
};
|
||||
byte key = (byte)type[0];
|
||||
int unpacked_size = CzDecryptInt (header, 7, key);
|
||||
int packed_size = CzDecryptInt (header, 11, key);
|
||||
if (packed_size < entry.UnpackedSize && 0 == ((packed_size-5) & 0xF))
|
||||
{
|
||||
var data = new byte[packed_size];
|
||||
input.Read (data, 0, packed_size);
|
||||
input.Dispose();
|
||||
data = CzDecryptData (data);
|
||||
input = new BinMemoryStream (data);
|
||||
if ('C' == type[0])
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
if (!input.CanSeek)
|
||||
return new PrefixStream (header, input);
|
||||
input.Position = 0;
|
||||
return input;
|
||||
}
|
||||
|
||||
static int CzDecryptInt (byte[] data, int offset, byte key)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
data[offset+i] ^= (byte)(key ^ CzHeaderKey[i]);
|
||||
}
|
||||
return data.ToInt32 (offset);
|
||||
}
|
||||
|
||||
static byte[] CzDecryptData (byte[] data)
|
||||
{
|
||||
int padded_size = data.Length - 5;
|
||||
int original_size = padded_size - (data[padded_size+1] ^ data[padded_size]);
|
||||
uint iv_seed = data.ToUInt32 (padded_size+1) ^ CzIvSeed;
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.Zeros;
|
||||
aes.Key = CzDefaultKey;
|
||||
aes.IV = CzCreateIV (iv_seed);
|
||||
using (var enc = new MemoryStream (data, 0, padded_size))
|
||||
using (var dec = new InputCryptoStream (enc, aes.CreateDecryptor()))
|
||||
{
|
||||
var original = new byte[original_size];
|
||||
dec.Read (original, 0, original_size);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] CzCreateIV (uint seed)
|
||||
{
|
||||
var state = new uint[4];
|
||||
state[0] = 123456789; // field_0
|
||||
state[1] = 972436830; // field_4
|
||||
state[2] = 524018621; // field_8
|
||||
state[3] = seed; // field_C
|
||||
var iv = new byte[16];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
uint a = state[3];
|
||||
uint b = state[0] ^ (state[0] << 11);
|
||||
state[0] = state[1];
|
||||
state[1] = state[2];
|
||||
state[2] = a;
|
||||
state[3] = b ^ a ^ ((b ^ (a >> 11)) >> 8);
|
||||
iv[i] = (byte)state[3];
|
||||
}
|
||||
return iv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
|
||||
CxProgram GenerateProgram (uint seed)
|
||||
{
|
||||
var program = new CxProgram (seed, ControlBlock);
|
||||
var program = NewProgram (seed);
|
||||
for (int stage = 5; stage > 0; --stage)
|
||||
{
|
||||
if (EmitCode (program, stage))
|
||||
@@ -228,6 +228,11 @@ namespace GameRes.Formats.KiriKiri
|
||||
throw new CxProgramException ("Overly large CxEncryption bytecode");
|
||||
}
|
||||
|
||||
internal virtual CxProgram NewProgram (uint seed)
|
||||
{
|
||||
return new CxProgram (seed, ControlBlock);
|
||||
}
|
||||
|
||||
bool EmitCode (CxProgram program, int stage)
|
||||
{
|
||||
return program.EmitNop (5) // 0x57 0x56 0x53 0x51 0x52
|
||||
@@ -437,7 +442,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
private List<uint> m_code = new List<uint> (LengthLimit);
|
||||
private uint[] m_ControlBlock;
|
||||
private int m_length;
|
||||
private uint m_seed;
|
||||
protected uint m_seed;
|
||||
|
||||
class Context
|
||||
{
|
||||
@@ -562,7 +567,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
return EmitUInt32 (GetRandom());
|
||||
}
|
||||
|
||||
public uint GetRandom ()
|
||||
public virtual uint GetRandom ()
|
||||
{
|
||||
uint seed = m_seed;
|
||||
m_seed = 1103515245 * seed + 12345;
|
||||
@@ -570,6 +575,27 @@ namespace GameRes.Formats.KiriKiri
|
||||
}
|
||||
}
|
||||
|
||||
internal class CxProgramNana : CxProgram
|
||||
{
|
||||
protected uint m_random_seed;
|
||||
|
||||
public CxProgramNana (uint seed, uint random_seed, uint[] control_block) : base (seed, control_block)
|
||||
{
|
||||
m_random_seed = random_seed;
|
||||
}
|
||||
|
||||
public override uint GetRandom ()
|
||||
{
|
||||
uint s = m_seed ^ (m_seed << 17);
|
||||
s ^= (s << 18) | (s >> 15);
|
||||
m_seed = ~s;
|
||||
uint r = m_random_seed ^ (m_random_seed << 13);
|
||||
r ^= r >> 17;
|
||||
m_random_seed = r ^ (r << 5);
|
||||
return m_seed ^ m_random_seed;
|
||||
}
|
||||
}
|
||||
|
||||
/* CxEncryption base branch order
|
||||
OddBranchOrder
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Utility;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Lilim
|
||||
{
|
||||
@@ -155,14 +155,40 @@ namespace GameRes.Formats.Lilim
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
if (2 == m_info.Mode)
|
||||
switch (m_info.Mode)
|
||||
{
|
||||
m_bpp = 32;
|
||||
case 8:
|
||||
case -8:
|
||||
m_input.Position = 0x36;
|
||||
var palette = ImageFormat.ReadPalette (m_input.AsStream);
|
||||
var bitmap_size = m_info.Width * m_info.Height;
|
||||
m_output = new byte[bitmap_size];
|
||||
m_input.Position = m_info.BaseOffset;
|
||||
if (8 == m_info.Mode)
|
||||
{
|
||||
m_bpp = 8;
|
||||
if (m_output.Length != m_input.Read (m_output, 0, m_output.Length))
|
||||
throw new EndOfStreamException();
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, m_output);
|
||||
}
|
||||
else
|
||||
{
|
||||
var alpha = new byte[bitmap_size];
|
||||
UnpackStream8 (m_input, m_output, alpha);
|
||||
m_output = ApplyAlpha (m_output, alpha, palette);
|
||||
m_bpp = 32;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_bpp = 32;
|
||||
m_input.Position = m_info.FrameOffset;
|
||||
m_output = UnpackV2 (m_input);
|
||||
}
|
||||
else if (1 == m_info.Mode || 32 == m_info.Mode || 24 == m_info.Mode)
|
||||
{
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 24:
|
||||
case 32:
|
||||
if (1 == m_info.Mode)
|
||||
m_bpp = 24;
|
||||
else
|
||||
@@ -180,9 +206,11 @@ namespace GameRes.Formats.Lilim
|
||||
UnpackStream24 (m_input, m_output, total_length);
|
||||
else
|
||||
UnpackStream32 (m_input, m_output, total_length);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
if (0 != m_info.FrameOffset)
|
||||
m_output = Unpack();
|
||||
PixelFormat format = 24 == m_bpp ? PixelFormats.Bgr24 : PixelFormats.Bgra32;
|
||||
@@ -319,5 +347,50 @@ namespace GameRes.Formats.Lilim
|
||||
src += frame_w * pixel_size;
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackStream8 (IBinaryStream input, byte[] output, byte[] alpha)
|
||||
{
|
||||
int alpha_dst = 0;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
byte rle = input.ReadUInt8();
|
||||
if (0 == rle)
|
||||
{
|
||||
int skip = input.ReadUInt8();
|
||||
dst += skip;
|
||||
alpha_dst += skip;
|
||||
}
|
||||
else if (rle != 0xFF)
|
||||
{
|
||||
output[dst++] = input.ReadUInt8();
|
||||
alpha[alpha_dst++] = rle;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = input.ReadUInt8();
|
||||
input.Read (output, dst, count);
|
||||
dst += count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
alpha[alpha_dst++] = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] ApplyAlpha (byte[] input, byte[] alpha, BitmapPalette palette)
|
||||
{
|
||||
var colors = palette.Colors;
|
||||
var pixels = new byte[input.Length * 4];
|
||||
int dst = 0;
|
||||
for (int i = 0; i < input.Length; ++i)
|
||||
{
|
||||
var color = colors[input[i]];
|
||||
pixels[dst++] = color.B;
|
||||
pixels[dst++] = color.G;
|
||||
pixels[dst++] = color.R;
|
||||
pixels[dst++] = alpha[i];
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,9 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Lilim
|
||||
{
|
||||
internal class AosEntry : Entry
|
||||
{
|
||||
public bool IsCompressed;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class AosOpener : ArchiveFormat
|
||||
{
|
||||
@@ -56,9 +50,7 @@ namespace GameRes.Formats.Lilim
|
||||
uint first_offset = file.View.ReadUInt32 (0x10);
|
||||
if (first_offset >= file.MaxOffset || 0 != (first_offset & 0x1F))
|
||||
return null;
|
||||
var name_buf = new byte[0x10];
|
||||
if (0x10 != file.View.Read (first_offset, name_buf, 0, 0x10))
|
||||
return null;
|
||||
var name_buf = file.View.ReadBytes (first_offset, 0x10);
|
||||
if (!name_buf.SequenceEqual (IndexLink) && !name_buf.SequenceEqual (IndexEnd))
|
||||
return null;
|
||||
|
||||
@@ -81,15 +73,14 @@ namespace GameRes.Formats.Lilim
|
||||
if (-1 == name_length)
|
||||
name_length = name_buf.Length;
|
||||
var name = Encodings.cp932.GetString (name_buf, 0, name_length);
|
||||
var entry = FormatCatalog.Instance.Create<AosEntry> (name);
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (current_offset+0x10);
|
||||
entry.Size = file.View.ReadUInt32 (current_offset+0x14);
|
||||
current_offset += 0x20;
|
||||
entry.Offset += current_offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
if (name.HasExtension (".scr"))
|
||||
entry.IsCompressed = true;
|
||||
entry.IsPacked = name.HasExtension (".scr");
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
@@ -98,14 +89,13 @@ namespace GameRes.Formats.Lilim
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var aent = entry as AosEntry;
|
||||
if (null == aent || !aent.IsCompressed)
|
||||
var aent = entry as PackedEntry;
|
||||
if (null == aent || !aent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
|
||||
uint unpacked_size = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var packed = new byte[entry.Size-4];
|
||||
arc.File.View.Read (entry.Offset+4, packed, 0, (uint)packed.Length);
|
||||
var unpacked = new byte[unpacked_size];
|
||||
aent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var packed = arc.File.View.ReadBytes (entry.Offset+4, entry.Size-4);
|
||||
var unpacked = new byte[aent.UnpackedSize];
|
||||
|
||||
var decoder = new HuffmanDecoder (packed, unpacked);
|
||||
decoder.Unpack();
|
||||
@@ -148,16 +138,16 @@ namespace GameRes.Formats.Lilim
|
||||
var name = file.View.ReadString (index_offset, 0x20);
|
||||
if (0 == name.Length)
|
||||
return null;
|
||||
var entry = FormatCatalog.Instance.Create<AosEntry> (name);
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = base_offset + file.View.ReadUInt32 (index_offset+0x20);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x24);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
if (name.HasExtension (".scr"))
|
||||
entry.IsCompressed = true;
|
||||
entry.IsPacked = true;
|
||||
else if (name.HasExtension (".cmp"))
|
||||
{
|
||||
entry.IsCompressed = true;
|
||||
entry.IsPacked = true;
|
||||
entry.Name = Path.ChangeExtension (entry.Name, ".abm");
|
||||
entry.Type = "image";
|
||||
}
|
||||
|
||||
83
ArcFormats/Lilim/ArcFGA.cs
Normal file
83
ArcFormats/Lilim/ArcFGA.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
//! \file ArcFGA.cs
|
||||
//! \date 2017 Sep 16
|
||||
//! \brief SFA engine resource archive.
|
||||
//
|
||||
// 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.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Lilim
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class FgaOpener : AosOpener
|
||||
{
|
||||
public override string Tag { get { return "FGA"; } }
|
||||
public override string Description { get { return "SFA engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".fga"))
|
||||
return null;
|
||||
uint index_offset = 0;
|
||||
var index_buffer = file.View.ReadBytes (index_offset, 0x318);
|
||||
if (0x318 != index_buffer.Length)
|
||||
return null;
|
||||
var dir = new List<Entry> (0x20);
|
||||
int pos = 0;
|
||||
while (pos < index_buffer.Length)
|
||||
{
|
||||
if (0 == index_buffer[pos])
|
||||
break;
|
||||
if (0xFF == index_buffer[pos])
|
||||
{
|
||||
uint next_offset = index_buffer.ToUInt32 (pos+0xC);
|
||||
if (next_offset <= index_offset || next_offset >= file.MaxOffset)
|
||||
return null;
|
||||
index_offset = next_offset;
|
||||
if (0x318 != file.View.Read (index_offset, index_buffer, 0, 0x318))
|
||||
return null;
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = Binary.GetCString (index_buffer, pos, 0xC);
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = index_buffer.ToUInt32 (pos+0xC);
|
||||
entry.Size = index_buffer.ToUInt32 (pos+0x10);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.IsPacked = name.HasExtension (".scr");
|
||||
dir.Add (entry);
|
||||
pos += 0x18;
|
||||
}
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,9 @@ namespace GameRes.Formats.Lilim
|
||||
var header = stream.ReadHeader (0x46);
|
||||
if ('B' != header[0] || 'M' != header[1])
|
||||
return null;
|
||||
int type = (sbyte)header[0x1C];
|
||||
int type = header.ToInt16 (0x1C);
|
||||
uint frame_offset;
|
||||
int bpp = 24;
|
||||
if (1 == type || 2 == type)
|
||||
{
|
||||
int count = header.ToUInt16 (0x3A);
|
||||
@@ -52,12 +53,14 @@ namespace GameRes.Formats.Lilim
|
||||
return null;
|
||||
frame_offset = header.ToUInt32 (0x42);
|
||||
}
|
||||
else if (32 == type || 24 == type)
|
||||
else if (32 == type || 24 == type || 8 == type || -8 == type)
|
||||
{
|
||||
uint unpacked_size = header.ToUInt32 (2);
|
||||
if (0 == unpacked_size || unpacked_size == stream.Length) // probably an ordinary bmp file
|
||||
return null;
|
||||
frame_offset = header.ToUInt32 (0xA);
|
||||
if (8 == type)
|
||||
bpp = 8;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
@@ -67,7 +70,7 @@ namespace GameRes.Formats.Lilim
|
||||
{
|
||||
Width = header.ToUInt32 (0x12),
|
||||
Height = header.ToUInt32 (0x16),
|
||||
BPP = 24,
|
||||
BPP = bpp,
|
||||
Mode = type,
|
||||
BaseOffset = frame_offset,
|
||||
};
|
||||
|
||||
267
ArcFormats/Littlewitch/ArcDAT.cs
Normal file
267
ArcFormats/Littlewitch/ArcDAT.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
//! \file ArcDAT.cs
|
||||
//! \date 2017 Oct 27
|
||||
//! \brief RepiPack resource archive.
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Cryptography;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Littlewitch
|
||||
{
|
||||
[Serializable]
|
||||
public class RepiScheme : ResourceScheme
|
||||
{
|
||||
public IDictionary<string, uint[]> KnownSchemes;
|
||||
}
|
||||
|
||||
internal class RepiEntry : PackedEntry
|
||||
{
|
||||
public bool HasEncryptionKey;
|
||||
|
||||
public byte[] CreateKey ()
|
||||
{
|
||||
var name_bytes = DatOpener.ToLowerAscii (Name);
|
||||
int name_length = name_bytes.Length;
|
||||
var md5 = new MD5();
|
||||
Array.Reverse (name_bytes);
|
||||
var key = new byte[1024];
|
||||
int key_pos = 0;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
int name_pos = i % name_length;
|
||||
md5.Update (name_bytes, name_pos, name_length - name_pos);
|
||||
md5.Final();
|
||||
Buffer.BlockCopy (md5.State, 0, key, key_pos, 16);
|
||||
key_pos += 16;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "DAT/RepiPack"; } }
|
||||
public override string Description { get { return "Littlewitch engine resource archive"; } }
|
||||
public override uint Signature { get { return 0x69706552; } } // 'Repi'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
static readonly string ListFileName = "littlewitch.lst";
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "Pack"))
|
||||
return null;
|
||||
int version = file.View.ReadInt32 (8);
|
||||
if (version != 5)
|
||||
return null;
|
||||
uint name_length = file.View.ReadUInt32 (0xC);
|
||||
if (name_length < 4)
|
||||
return null;
|
||||
uint name_key = file.View.ReadUInt32 (0x10);
|
||||
var key = FindKey (file.Name, name_key);
|
||||
if (null == key)
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (0x10 + name_length);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var index = file.View.ReadBytes (0x14 + name_length, (uint)count * 0x20);
|
||||
int pos = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
var name_builder = new StringBuilder();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
DecryptData (index, pos, 0x20, key[2], key[1]);
|
||||
var entry = EntryFromMd5 (index, pos, name_builder);
|
||||
entry.Offset = index.ToUInt32 (pos+0x10);
|
||||
entry.Size = index.ToUInt32 (pos+0x14);
|
||||
entry.UnpackedSize = index.ToUInt32 (pos+0x18);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||
dir.Add (entry);
|
||||
pos += 0x20;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var rent = entry as RepiEntry;
|
||||
if (null == rent || !rent.HasEncryptionKey)
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var key = rent.CreateKey();
|
||||
uint enc_length = Math.Min ((uint)key.Length, entry.Size);
|
||||
byte[] encrypted = arc.File.View.ReadBytes (entry.Offset, enc_length);
|
||||
DecryptEntry (encrypted, key);
|
||||
Stream input;
|
||||
if (enc_length == entry.Size)
|
||||
{
|
||||
input = new BinMemoryStream (encrypted, entry.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
input = arc.File.CreateStream (entry.Offset + enc_length, entry.Size - enc_length);
|
||||
input = new PrefixStream (encrypted, input);
|
||||
}
|
||||
if (rent.IsPacked)
|
||||
{
|
||||
input = new LzssStream (input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
static void DecryptEntry (byte[] data, byte[] key)
|
||||
{
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] ^= key[i];
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe void DecryptData (byte[] data, int pos, int length, uint key, uint seed)
|
||||
{
|
||||
if (pos < 0 || pos + length > data.Length)
|
||||
throw new ArgumentOutOfRangeException ("pos", "Invalid byte array index.");
|
||||
fixed (byte* data8 = &data[pos])
|
||||
{
|
||||
uint* data32 = (uint*)data8;
|
||||
for (int count = length / 4; count > 0; --count)
|
||||
{
|
||||
*data32 ^= key;
|
||||
key += Binary.RotL (*data32, 16) ^ seed;
|
||||
data32++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RepiEntry EntryFromMd5 (byte[] data, int pos, StringBuilder builder)
|
||||
{
|
||||
var key = new CowArray<byte> (data, pos, 16).ToArray();
|
||||
string name;
|
||||
if (KnownNames.TryGetValue (key, out name))
|
||||
{
|
||||
var entry = FormatCatalog.Instance.Create<RepiEntry> (name);
|
||||
entry.HasEncryptionKey = true;
|
||||
return entry;
|
||||
}
|
||||
builder.Clear();
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
builder.AppendFormat ("{0:x2}", key[i]);
|
||||
}
|
||||
return new RepiEntry { Name = builder.ToString() };
|
||||
}
|
||||
|
||||
static uint[] FindKey (string arc_name, uint arc_key)
|
||||
{
|
||||
arc_name = Path.GetFileName (arc_name);
|
||||
var name_bytes = Encodings.cp932.GetBytes (arc_name);
|
||||
arc_key ^= name_bytes.ToUInt32 (0);
|
||||
return DatScheme.KnownSchemes.Values.FirstOrDefault (k => k[0] == arc_key);
|
||||
}
|
||||
|
||||
static RepiScheme DatScheme = new RepiScheme { KnownSchemes = new Dictionary<string, uint[]>() };
|
||||
|
||||
public override ResourceScheme Scheme
|
||||
{
|
||||
get { return DatScheme; }
|
||||
set { DatScheme = (RepiScheme)value; }
|
||||
}
|
||||
|
||||
internal Dictionary<byte[], string> KnownNames { get { return s_known_file_names.Value; } }
|
||||
|
||||
static Lazy<Dictionary<byte[], string>> s_known_file_names = new Lazy<Dictionary<byte[], string>> (ReadFileList);
|
||||
|
||||
static Dictionary<byte[], string> ReadFileList ()
|
||||
{
|
||||
var dict = new Dictionary<byte[], string> (new Md5Comparer());
|
||||
try
|
||||
{
|
||||
var md5 = new MD5();
|
||||
FormatCatalog.Instance.ReadFileList (ListFileName, name => {
|
||||
var name_bytes = ToLowerAscii (name);
|
||||
var hash = md5.ComputeHash (name_bytes);
|
||||
dict[hash] = name;
|
||||
});
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine (X.Message, "[RepiPack]");
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
internal static byte[] ToLowerAscii (string text)
|
||||
{
|
||||
var text_bytes = Encodings.cp932.GetBytes (text);
|
||||
for (int i = 0; i < text_bytes.Length; ++i)
|
||||
{
|
||||
byte c = text_bytes[i];
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
text_bytes[i] += 0x20;
|
||||
else if (c > 0x7F && c < 0xA1 || c > 0xDF)
|
||||
++i;
|
||||
}
|
||||
return text_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Md5Comparer : IEqualityComparer<byte[]>
|
||||
{
|
||||
public bool Equals (byte[] left, byte[] right)
|
||||
{
|
||||
if (left == null || right == null)
|
||||
return left == right;
|
||||
if (left.Length != right.Length)
|
||||
return false;
|
||||
for (int i = 0; i < left.Length; ++i)
|
||||
{
|
||||
if (left[i] != right[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetHashCode (byte[] key)
|
||||
{
|
||||
if (null == key)
|
||||
throw new ArgumentNullException ("key");
|
||||
if (key.Length < 16)
|
||||
throw new ArgumentException ("Invalid key length.", "key");
|
||||
var hash = key.ToInt32 (0);
|
||||
hash ^= key.ToInt32 (4);
|
||||
hash ^= key.ToInt32 (8);
|
||||
hash ^= key.ToInt32 (12);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
ArcFormats/LunaSoft/ArcPAC.cs
Normal file
67
ArcFormats/LunaSoft/ArcPAC.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
//! \file ArcPAC.cs
|
||||
//! \date 2017 Aug 17
|
||||
//! \brief LunaSoft resource archive.
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.LunaSoft
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PacOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAC/LUNA"; } }
|
||||
public override string Description { get { return "LunaSoft resource archive"; } }
|
||||
public override uint Signature { get { return 0xAD82CF82; } } // ぱく
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
long base_offset = file.View.ReadUInt32 (8);
|
||||
|
||||
uint current_offset = 0x10;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint name_length = file.View.ReadUInt32 (current_offset+0x108);
|
||||
var name = file.View.ReadString (current_offset, name_length);
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
current_offset += 0x100;
|
||||
entry.Offset = base_offset + file.View.ReadUInt32 (current_offset);
|
||||
entry.Size = file.View.ReadUInt32 (current_offset+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
current_offset += 0xC;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,7 @@ namespace GameRes.Cryptography
|
||||
public class MD5 : MD5Base
|
||||
{
|
||||
long m_bit_count;
|
||||
int m_buf_pos;
|
||||
|
||||
public uint[] State { get { return m_state; } }
|
||||
|
||||
@@ -118,25 +119,42 @@ namespace GameRes.Cryptography
|
||||
m_state[2] = 0x98BADCFE;
|
||||
m_state[3] = 0x10325476;
|
||||
m_bit_count = 0;
|
||||
m_buf_pos = 0;
|
||||
}
|
||||
|
||||
public byte[] ComputeHash (byte[] data)
|
||||
{
|
||||
return ComputeHash (data, 0, data.Length);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash (byte[] data, int pos, int count)
|
||||
{
|
||||
Initialize();
|
||||
Update (data, pos, count);
|
||||
Final();
|
||||
var hash = new byte[16];
|
||||
Buffer.BlockCopy (m_state, 0, hash, 0, 16);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void Update (byte[] data, int pos, int count)
|
||||
{
|
||||
int buf_pos = (int)(m_bit_count >> 3) & 0x3F;
|
||||
m_bit_count += (long)count << 3;
|
||||
|
||||
if (buf_pos != 0)
|
||||
if (m_buf_pos != 0)
|
||||
{
|
||||
int buf_count = 64 - buf_pos;
|
||||
int buf_count = 64 - m_buf_pos;
|
||||
if (count < buf_count)
|
||||
{
|
||||
Buffer.BlockCopy (data, pos, m_buffer, buf_pos, count);
|
||||
Buffer.BlockCopy (data, pos, m_buffer, m_buf_pos, count);
|
||||
m_buf_pos += count;
|
||||
return;
|
||||
}
|
||||
Buffer.BlockCopy (data, pos, m_buffer, buf_pos, buf_count);
|
||||
Buffer.BlockCopy (data, pos, m_buffer, m_buf_pos, buf_count);
|
||||
Transform();
|
||||
pos += buf_count;
|
||||
count -= buf_count;
|
||||
m_buf_pos = 0;
|
||||
}
|
||||
// data is processed in 64-byte chunks
|
||||
while (count >= 64)
|
||||
@@ -149,7 +167,31 @@ namespace GameRes.Cryptography
|
||||
if (count > 0)
|
||||
{
|
||||
Buffer.BlockCopy (data, pos, m_buffer, 0, count);
|
||||
m_buf_pos += count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Final ()
|
||||
{
|
||||
Buffer.BlockCopy (Terminator, 0, m_buffer, m_buf_pos++, 1);
|
||||
int buf_count = 64 - m_buf_pos;
|
||||
|
||||
if (buf_count < 8)
|
||||
{
|
||||
Buffer.BlockCopy (ZeroBytes, 0, m_buffer, m_buf_pos, buf_count);
|
||||
Transform();
|
||||
m_buf_pos = 0;
|
||||
buf_count = 64;
|
||||
}
|
||||
Buffer.BlockCopy (ZeroBytes, 0, m_buffer, m_buf_pos, buf_count-8);
|
||||
m_buffer[14] = (uint)m_bit_count;
|
||||
m_buffer[15] = (uint)(m_bit_count >> 32);
|
||||
Transform();
|
||||
m_buf_pos = 0;
|
||||
// XXX m_bit_count is not reset -- this is intentional.
|
||||
}
|
||||
|
||||
static readonly byte[] Terminator = new byte[1] { 0x80 };
|
||||
static readonly byte[] ZeroBytes = new byte[56];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,15 +80,17 @@ namespace GameRes.Formats.Malie
|
||||
public class LibCfiScheme : LibScheme
|
||||
{
|
||||
public byte[] Key { get; set; }
|
||||
public uint[] RotateKey { get; set; }
|
||||
|
||||
public LibCfiScheme (uint align, byte[] key) : base (align)
|
||||
public LibCfiScheme (uint align, byte[] key, uint[] rot_key) : base (align)
|
||||
{
|
||||
Key = key;
|
||||
RotateKey = rot_key;
|
||||
}
|
||||
|
||||
public override IMalieDecryptor CreateDecryptor ()
|
||||
{
|
||||
return new CfiDecryptor (Key);
|
||||
return new CfiDecryptor (Key, RotateKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,12 @@ namespace GameRes.Formats.Malie
|
||||
public class CfiDecryptor : IMalieDecryptor
|
||||
{
|
||||
byte[] m_key;
|
||||
uint[] m_rotate_key;
|
||||
|
||||
public CfiDecryptor (byte[] key)
|
||||
public CfiDecryptor (byte[] key, uint[] rotate_key)
|
||||
{
|
||||
m_key = key;
|
||||
m_rotate_key = rotate_key;
|
||||
}
|
||||
|
||||
public void DecryptBlock (long block_offset, byte[] data, int index)
|
||||
@@ -79,13 +81,13 @@ namespace GameRes.Formats.Malie
|
||||
fixed (byte* data8 = &data[index])
|
||||
{
|
||||
uint* data32 = (uint*)data8;
|
||||
uint k = Binary.RotR (0x39653542, m_key[offset & 0x1F] ^ 0xA5);
|
||||
uint k = Binary.RotR (m_rotate_key[0], m_key[offset & 0x1F] ^ 0xA5);
|
||||
data32[0] = Binary.RotR (data32[0] ^ k, m_key[(offset + 12) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotL (0x76706367, m_key[(offset + 3) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotL (m_rotate_key[1], m_key[(offset + 3) & 0x1F] ^ 0xA5);
|
||||
data32[1] = Binary.RotL (data32[1] ^ k, m_key[(offset + 15) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotR (0x69454462, m_key[(offset + 6) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotR (m_rotate_key[2], m_key[(offset + 6) & 0x1F] ^ 0xA5);
|
||||
data32[2] = Binary.RotR (data32[2] ^ k, m_key[(offset - 14) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotL (0x71334334, m_key[(offset + 9) & 0x1F] ^ 0xA5);
|
||||
k = Binary.RotL (m_rotate_key[3], m_key[(offset + 9) & 0x1F] ^ 0xA5);
|
||||
data32[3] = Binary.RotL (data32[3] ^ k, m_key[(offset - 11) & 0x1F] ^ 0xA5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace GameRes.Formats.Pajamas
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (EpaMetaData)info as EpaMetaData;
|
||||
var meta = (EpaMetaData)info;
|
||||
file.Position = 2 == meta.Mode ? 0x18 : 0x10;
|
||||
var reader = new Reader (file.AsStream, meta);
|
||||
reader.Unpack();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Fri Sep 30 10:37:28 2016
|
||||
//! \brief Primel the Adventure System resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
// Copyright (C) 2016-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
|
||||
@@ -35,8 +35,19 @@ namespace GameRes.Formats.Primel
|
||||
{
|
||||
internal class PcfEntry : PackedEntry
|
||||
{
|
||||
public uint Flags;
|
||||
public IEnumerable<byte> Key;
|
||||
public uint Flags;
|
||||
public byte[] Key;
|
||||
}
|
||||
|
||||
internal class PcfArchive : ArcFile
|
||||
{
|
||||
public readonly PrimelScheme Scheme;
|
||||
|
||||
public PcfArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, PrimelScheme scheme)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Scheme = scheme;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
@@ -55,49 +66,23 @@ namespace GameRes.Formats.Primel
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
long data_size = file.View.ReadInt64 (0x10);
|
||||
long index_offset = file.View.ReadInt64 (0x28);
|
||||
if (data_size >= file.MaxOffset || index_offset >= file.MaxOffset)
|
||||
var reader = new PcfIndexReader (file, count);
|
||||
var dir = reader.Read();
|
||||
if (null == dir)
|
||||
return null;
|
||||
uint index_size = file.View.ReadUInt32 (0x30);
|
||||
uint flags = file.View.ReadUInt32 (0x38);
|
||||
var key = file.View.ReadBytes (0x58, 8);
|
||||
long base_offset = file.MaxOffset - data_size;
|
||||
|
||||
using (var stream = file.CreateStream (base_offset + index_offset, index_size))
|
||||
using (var index = ReadFile (stream, key, flags))
|
||||
{
|
||||
var buffer = new byte[0x80];
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (buffer.Length != index.Read (buffer, 0, buffer.Length))
|
||||
break;
|
||||
var name = Binary.GetCString (buffer, 0, 0x50);
|
||||
var entry = FormatCatalog.Instance.Create<PcfEntry> (name);
|
||||
entry.Offset = LittleEndian.ToInt64 (buffer, 0x50) + base_offset;
|
||||
entry.UnpackedSize = LittleEndian.ToUInt32 (buffer, 0x58);
|
||||
entry.Size = LittleEndian.ToUInt32 (buffer, 0x60);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.Flags = LittleEndian.ToUInt32 (buffer, 0x68);
|
||||
entry.Key = new ArraySegment<byte> (buffer, 0x78, 8).ToArray();
|
||||
entry.IsPacked = entry.UnpackedSize != entry.Size;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
return new PcfArchive (file, this, dir, reader.Scheme);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var parc = arc as PcfArchive;
|
||||
var pent = entry as PcfEntry;
|
||||
if (null == pent)
|
||||
if (null == pent || null == parc)
|
||||
return base.OpenEntry (arc, entry);
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
try
|
||||
{
|
||||
input = ReadFile (input, pent.Key, pent.Flags);
|
||||
input = parc.Scheme.TransformStream (input, pent.Key, pent.Flags);
|
||||
if (pent.IsPacked)
|
||||
input = new LimitStream (input, pent.UnpackedSize);
|
||||
return input;
|
||||
@@ -108,8 +93,85 @@ namespace GameRes.Formats.Primel
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream ReadFile (Stream input, IEnumerable<byte> key, uint flags)
|
||||
internal sealed class PcfIndexReader
|
||||
{
|
||||
ArcView m_file;
|
||||
int m_count;
|
||||
long m_base_offset;
|
||||
List<Entry> m_dir;
|
||||
|
||||
public PrimelScheme Scheme { get; set; }
|
||||
|
||||
public PcfIndexReader (ArcView file, int count)
|
||||
{
|
||||
m_file = file;
|
||||
m_count = count;
|
||||
m_dir = new List<Entry> (m_count);
|
||||
}
|
||||
|
||||
static readonly PrimelScheme[] KnownSchemes = {
|
||||
new PrimelScheme(), new PrimelSchemeV2()
|
||||
};
|
||||
|
||||
public List<Entry> Read ()
|
||||
{
|
||||
long data_size = m_file.View.ReadInt64 (0x10);
|
||||
long index_offset = m_file.View.ReadInt64 (0x28);
|
||||
if (data_size >= m_file.MaxOffset || index_offset >= m_file.MaxOffset)
|
||||
return null;
|
||||
uint index_size = m_file.View.ReadUInt32 (0x30);
|
||||
uint flags = m_file.View.ReadUInt32 (0x38);
|
||||
var key = m_file.View.ReadBytes (0x58, 8);
|
||||
m_base_offset = m_file.MaxOffset - data_size;
|
||||
foreach (var scheme in KnownSchemes)
|
||||
{
|
||||
m_dir.Clear();
|
||||
try
|
||||
{
|
||||
using (var stream = m_file.CreateStream (m_base_offset + index_offset, index_size))
|
||||
using (var index = scheme.TransformStream (stream, key, flags))
|
||||
{
|
||||
if (ReadIndex (index))
|
||||
{
|
||||
this.Scheme = scheme;
|
||||
return m_dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* invalid scheme, retry */ }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] m_buffer = new byte[0x80];
|
||||
|
||||
bool ReadIndex (Stream index)
|
||||
{
|
||||
for (int i = 0; i < m_count; ++i)
|
||||
{
|
||||
if (m_buffer.Length != index.Read (m_buffer, 0, m_buffer.Length))
|
||||
break;
|
||||
var name = Binary.GetCString (m_buffer, 0, 0x50);
|
||||
var entry = FormatCatalog.Instance.Create<PcfEntry> (name);
|
||||
entry.Offset = LittleEndian.ToInt64 (m_buffer, 0x50) + m_base_offset;
|
||||
entry.UnpackedSize = LittleEndian.ToUInt32 (m_buffer, 0x58);
|
||||
entry.Size = LittleEndian.ToUInt32 (m_buffer, 0x60);
|
||||
if (!entry.CheckPlacement (m_file.MaxOffset))
|
||||
return false;
|
||||
entry.Flags = LittleEndian.ToUInt32 (m_buffer, 0x68);
|
||||
entry.Key = new ArraySegment<byte> (m_buffer, 0x78, 8).ToArray();
|
||||
entry.IsPacked = entry.UnpackedSize != entry.Size;
|
||||
m_dir.Add (entry);
|
||||
}
|
||||
return m_dir.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrimelScheme
|
||||
{
|
||||
public Stream TransformStream (Stream input, byte[] key, uint flags)
|
||||
{
|
||||
var key1 = GenerateKey (key);
|
||||
var iv = GenerateKey (key1);
|
||||
@@ -168,10 +230,9 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
}
|
||||
|
||||
byte[] GenerateKey (IEnumerable<byte> seed)
|
||||
byte[] GenerateKey (byte[] seed)
|
||||
{
|
||||
var sha = new Primel.SHA256();
|
||||
var hash = sha.ComputeHash (seed.ToArray());
|
||||
var hash = ComputeHash (seed);
|
||||
var key = new byte[0x10];
|
||||
for (int i = 0; i < hash.Length; ++i)
|
||||
{
|
||||
@@ -179,5 +240,20 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
protected virtual byte[] ComputeHash (byte[] seed)
|
||||
{
|
||||
var sha = new Primel.SHA256();
|
||||
return sha.ComputeHash (seed);
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrimelSchemeV2 : PrimelScheme
|
||||
{
|
||||
protected override byte[] ComputeHash (byte[] seed)
|
||||
{
|
||||
using (var sha = System.Security.Cryptography.SHA256.Create())
|
||||
return sha.ComputeHash (seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Oct 03 04:16:11 2016
|
||||
//! \brief Primel the Adventure System resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
// Copyright (C) 2016-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
|
||||
@@ -97,6 +97,14 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
if (0x800 == (m_info.Flags & 0xFF00))
|
||||
UnpackV2();
|
||||
else
|
||||
UnpackV1();
|
||||
}
|
||||
|
||||
void UnpackV1 ()
|
||||
{
|
||||
m_input.Input.Position = 0x30;
|
||||
int pixel_size = m_info.BPP / 8;
|
||||
@@ -148,6 +156,63 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV2 ()
|
||||
{
|
||||
m_input.Input.Position = 0x30;
|
||||
int pixel_size = m_info.BPP / 8;
|
||||
int blocks_w = (int)(m_info.Width + 7) / 8;
|
||||
int blocks_h = (int)(m_info.Height + 7) / 8;
|
||||
short[,] block = new short[pixel_size, 64];
|
||||
|
||||
for (int by = 0; by < blocks_h; ++by)
|
||||
{
|
||||
int dst_line = by * 8 * m_stride;
|
||||
for (int bx = 0; bx < blocks_w; ++bx)
|
||||
{
|
||||
int dst_block = dst_line + bx * 8 * pixel_size;
|
||||
|
||||
for (int i = 0; i < pixel_size; ++i)
|
||||
{
|
||||
for (int j = 0; j < 64; ++j)
|
||||
block[i,j] = 0;
|
||||
RestoreBlockV2 (block, i);
|
||||
}
|
||||
|
||||
for (int y = 0; y < 8; ++y)
|
||||
{
|
||||
if (by + 1 == blocks_h && (by * 8 + y) >= m_info.Height)
|
||||
break;
|
||||
|
||||
int src = y * 8;
|
||||
int dst = dst_block + y * m_stride;
|
||||
for (int x = 0; x < 8; ++x)
|
||||
{
|
||||
if (bx + 1 == blocks_w && (bx * 8 + x) >= m_info.Width)
|
||||
break;
|
||||
if (4 == pixel_size)
|
||||
{
|
||||
m_output[dst + x * 4 + 2] = (byte)block[0, src+x];
|
||||
m_output[dst + x * 4 + 1] = (byte)block[1, src+x];
|
||||
m_output[dst + x * 4] = (byte)block[2, src+x];
|
||||
m_output[dst + x * 4 + 3] = (byte)block[3, src+x];
|
||||
}
|
||||
else if (3 == pixel_size)
|
||||
{
|
||||
m_output[dst + x * 3 + 2] = (byte)block[0, src+x];
|
||||
m_output[dst + x * 3 + 1] = (byte)block[1, src+x];
|
||||
m_output[dst + x * 3] = (byte)block[2, src+x];
|
||||
}
|
||||
else
|
||||
{
|
||||
var val = block[0, src+x];
|
||||
m_output[dst + x] = (byte)val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreBlock (short[,] block, int n)
|
||||
{
|
||||
int row = 8;
|
||||
@@ -168,6 +233,31 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreBlockV2 (short[,] block, int plane)
|
||||
{
|
||||
int skip;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
int n = GetIntV2 (out skip);
|
||||
if (n != 0)
|
||||
block[plane, ZigzagOrder[i]] = (short)n;
|
||||
else if (0 == skip)
|
||||
break;
|
||||
else
|
||||
i += skip - 1;
|
||||
}
|
||||
for (int row = 0; row < 64; row += 8)
|
||||
for (int x = 1; x < 8; ++x)
|
||||
{
|
||||
block[plane, row+x] += block[plane, row+x-1];
|
||||
}
|
||||
for (int row = 8; row < 64; row += 8)
|
||||
for (int x = 0; x < 8; ++x)
|
||||
{
|
||||
block[plane, row+x] += block[plane, row-8+x];
|
||||
}
|
||||
}
|
||||
|
||||
int GetInt ()
|
||||
{
|
||||
int count = m_input.GetBits (4);
|
||||
@@ -194,6 +284,40 @@ namespace GameRes.Formats.Primel
|
||||
}
|
||||
}
|
||||
|
||||
int GetIntV2 (out int repeat)
|
||||
{
|
||||
int count = m_input.GetBits (4);
|
||||
repeat = 0;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
repeat = 1;
|
||||
while (repeat < 16 && 1 == m_input.GetNextBit())
|
||||
++repeat;
|
||||
if (16 == repeat)
|
||||
repeat = 0;
|
||||
return 0;
|
||||
|
||||
case 1: return 1;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return m_input.GetBits (count - 1) + (1 << (count - 1));
|
||||
|
||||
case 8: return -1;
|
||||
case 9: return -2;
|
||||
|
||||
default:
|
||||
return m_input.GetBits (count - 9) - (2 << (count - 9));
|
||||
|
||||
case -1: throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
|
||||
bool _disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.2.34.1396")]
|
||||
[assembly: AssemblyFileVersion ("1.2.34.1396")]
|
||||
[assembly: AssemblyVersion ("1.2.36.1443")]
|
||||
[assembly: AssemblyFileVersion ("1.2.36.1443")]
|
||||
|
||||
@@ -38,10 +38,12 @@ namespace GameRes.Formats.Qlie
|
||||
{
|
||||
internal class QlieEntry : PackedEntry
|
||||
{
|
||||
public bool IsEncrypted;
|
||||
public int EncryptionMethod;
|
||||
public uint Hash;
|
||||
public byte[] RawName;
|
||||
|
||||
public bool IsEncrypted { get { return EncryptionMethod != 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// Data from a separate key file "key.fkey" that comes with installed game.
|
||||
/// null if not used.
|
||||
@@ -153,7 +155,7 @@ namespace GameRes.Formats.Qlie
|
||||
return null;
|
||||
entry.UnpackedSize = index.ReadUInt32(); // [+0C]
|
||||
entry.IsPacked = 0 != index.ReadInt32(); // [+10]
|
||||
entry.IsEncrypted = 0 != index.ReadInt32(); // [+14]
|
||||
entry.EncryptionMethod = index.ReadInt32(); // [+14]
|
||||
entry.Hash = index.ReadUInt32(); // [+18]
|
||||
entry.KeyFile = key_file;
|
||||
if (3 == pack_version.Major && use_pack_keyfile && entry.Name.Contains ("pack_keyfile"))
|
||||
@@ -330,6 +332,16 @@ namespace GameRes.Formats.Qlie
|
||||
return File.ReadAllBytes (name);
|
||||
}
|
||||
}
|
||||
var pattern = VFS.CombinePath (dir_name, @"..\*.exe");
|
||||
foreach (var exe_file in VFS.GetFiles (pattern))
|
||||
{
|
||||
using (var exe = new ExeFile.ResourceAccessor (exe_file.Name))
|
||||
{
|
||||
var reskey = exe.GetResource ("RESKEY", "#10");
|
||||
if (reskey != null)
|
||||
return reskey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Qlie
|
||||
{
|
||||
@@ -285,13 +286,28 @@ namespace GameRes.Formats.Qlie
|
||||
|
||||
public override void DecryptEntry (byte[] data, int offset, int length, QlieEntry entry)
|
||||
{
|
||||
if (0 == entry.EncryptionMethod)
|
||||
return;
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset");
|
||||
if (length > data.Length || offset > data.Length - length)
|
||||
throw new ArgumentOutOfRangeException ("length");
|
||||
if (length < 8)
|
||||
return;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* raw_data = &data[offset])
|
||||
{
|
||||
if (1 == entry.EncryptionMethod)
|
||||
DecryptV1 (raw_data, length, entry);
|
||||
else
|
||||
DecryptV2 (raw_data, length, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void DecryptV1 (byte* data, int length, QlieEntry entry)
|
||||
{
|
||||
var file_name = entry.Name;
|
||||
uint hash = 0x85F532;
|
||||
uint seed = 0x33F641;
|
||||
@@ -306,34 +322,66 @@ namespace GameRes.Formats.Qlie
|
||||
+ hash + (hash ^ (uint)length ^ 0x8F32DCu));
|
||||
seed = 9 * (seed & 0xFFFFFF);
|
||||
var table = GenerateTable (0x20, seed); // originally 0x2C, 12 dwords not used
|
||||
unsafe
|
||||
ulong* data64 = (ulong*)data;
|
||||
int qword_length = length / 8;
|
||||
uint k = 2 * (table[0xD] & 0xF);
|
||||
ulong hash64 = table[6] | (ulong)table[7] << 32;
|
||||
for (int i = 0; i < qword_length; ++i)
|
||||
{
|
||||
fixed (byte* raw_data = &data[offset])
|
||||
{
|
||||
ulong* data64 = (ulong*)raw_data;
|
||||
int qword_length = length / 8;
|
||||
uint k = 2 * (table[0xD] & 0xF);
|
||||
ulong hash64 = table[6] | (ulong)table[7] << 32;
|
||||
for (int i = 0; i < qword_length; ++i)
|
||||
{
|
||||
ulong t = table[k] | (ulong)table[k+1] << 32;
|
||||
hash64 = MMX.PAddD (hash64 ^ t, t);
|
||||
ulong t = table[k] | (ulong)table[k+1] << 32;
|
||||
hash64 = MMX.PAddD (hash64 ^ t, t);
|
||||
|
||||
ulong d = data64[i] ^ hash64;
|
||||
data64[i] = d;
|
||||
ulong d = data64[i] ^ hash64;
|
||||
data64[i] = d;
|
||||
|
||||
hash64 = MMX.PAddB (hash64, d) ^ d;
|
||||
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
|
||||
hash64 = MMX.PAddB (hash64, d) ^ d;
|
||||
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
|
||||
|
||||
k = (k + 2) & 0x1F;
|
||||
}
|
||||
}
|
||||
k = (k + 2) & 0x1F;
|
||||
}
|
||||
}
|
||||
|
||||
static uint[] GenerateTable (int length, uint seed)
|
||||
unsafe void DecryptV2 (byte* data, int length, QlieEntry entry)
|
||||
{
|
||||
var file_name = entry.Name;
|
||||
uint hash = 0x86F7E2;
|
||||
uint seed = 0x4437F1;
|
||||
|
||||
for (int i = 0; i < file_name.Length; i++)
|
||||
{
|
||||
hash += (uint)(file_name[i] << (i & 7));
|
||||
seed ^= hash;
|
||||
}
|
||||
|
||||
seed += ArcKey ^ (13 * ((uint)length & 0xFFFFFF) + (uint)length
|
||||
+ hash + (hash ^ (uint)length ^ 0x56E213u));
|
||||
seed = 13 * (seed & 0xFFFFFF);
|
||||
var table = GenerateTable (0x20, seed, 0x8A77F473u); // originally 0x40
|
||||
var key_data = GenerateKeyData (entry.KeyFile);
|
||||
|
||||
ulong* data64 = (ulong*)data;
|
||||
int qword_length = length / 8;
|
||||
int k = (8 * ((int)table[8] & 0xD)) & 0x7F;
|
||||
ulong hash64 = table[6] | (ulong)table[7] << 32;
|
||||
for (int i = 0; i < qword_length; ++i)
|
||||
{
|
||||
int t_index = 2 * (k & 0xF);
|
||||
ulong t = table[t_index] | (ulong)table[t_index + 1] << 32;
|
||||
t ^= LittleEndian.ToUInt64 (key_data, 8 * k);
|
||||
hash64 = MMX.PAddD (hash64 ^ t, t);
|
||||
|
||||
ulong d = data64[i] ^ hash64;
|
||||
data64[i] = d;
|
||||
|
||||
hash64 = MMX.PAddB (hash64, d) ^ d;
|
||||
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
|
||||
|
||||
k = (k + 1) & 0x7F;
|
||||
}
|
||||
}
|
||||
|
||||
static uint[] GenerateTable (int length, uint seed, uint key = 0x8DF21431u)
|
||||
{
|
||||
const uint key = 0x8DF21431u;
|
||||
var table = new uint[length];
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
@@ -343,5 +391,30 @@ namespace GameRes.Formats.Qlie
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
byte[] GenerateKeyData (byte[] key_file)
|
||||
{
|
||||
var key_data = new byte[0x400];
|
||||
for (int i = 0; i < 0x100; ++i)
|
||||
{
|
||||
int hash;
|
||||
if (0 != (i % 3))
|
||||
hash = (i + 7) * -(i + 3);
|
||||
else
|
||||
hash = (i + 7) * (i + 3);
|
||||
LittleEndian.Pack (hash, key_data, i * 4);
|
||||
}
|
||||
if (key_file != null && key_file.Length >= 128)
|
||||
{
|
||||
int k = key_file[49] % 73 + 128;
|
||||
int l = key_file[79] % 7 + 7;
|
||||
for (int i = 0; i < key_data.Length; ++i)
|
||||
{
|
||||
k = (k + l) % key_file.Length;
|
||||
key_data[i] ^= key_file[k];
|
||||
}
|
||||
}
|
||||
return key_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
@@ -99,14 +100,17 @@ namespace GameRes.Formats.RenPy
|
||||
return null;
|
||||
}
|
||||
var entry = FormatCatalog.Instance.Create<RpaEntry> (name);
|
||||
entry.Offset = (uint)((int)tuple[0] ^ key);
|
||||
entry.UnpackedSize = (uint)((int)tuple[1] ^ key);
|
||||
entry.Offset = (long)(Convert.ToInt64 (tuple[0]) ^ key);
|
||||
entry.UnpackedSize = (uint)(Convert.ToInt32 (tuple[1]) ^ key);
|
||||
entry.Size = entry.UnpackedSize;
|
||||
if (tuple.Count > 2)
|
||||
{
|
||||
entry.Header = tuple[2] as byte[];
|
||||
if (null != entry.Header)
|
||||
if (null != entry.Header && entry.Header.Length > 0)
|
||||
{
|
||||
entry.Size -= (uint)entry.Header.Length;
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
}
|
||||
dir.Add (entry);
|
||||
}
|
||||
@@ -123,7 +127,7 @@ namespace GameRes.Formats.RenPy
|
||||
else
|
||||
input = Stream.Null;
|
||||
var rpa_entry = entry as RpaEntry;
|
||||
if (null == rpa_entry || null == rpa_entry.Header)
|
||||
if (null == rpa_entry || null == rpa_entry.Header || 0 == rpa_entry.Header.Length)
|
||||
return input;
|
||||
return new PrefixStream (rpa_entry.Header, input);
|
||||
}
|
||||
@@ -205,6 +209,8 @@ namespace GameRes.Formats.RenPy
|
||||
const byte PROTO = 0x80; /* identify pickle protocol */
|
||||
const byte TUPLE2 = 0x86; /* build 2-tuple from two topmost stack items */
|
||||
const byte TUPLE3 = 0x87; /* build 3-tuple from three topmost stack items */
|
||||
const byte LONG1 = 0x8A; /* push long from < 256 bytes */
|
||||
const byte LONG4 = 0x8B; /* push really big long */
|
||||
const byte MARK = (byte)'(';
|
||||
const byte STOP = (byte)'.';
|
||||
const byte INT = (byte)'I';
|
||||
@@ -527,6 +533,16 @@ namespace GameRes.Formats.RenPy
|
||||
break;
|
||||
continue;
|
||||
|
||||
case LONG1:
|
||||
if (!LoadLong())
|
||||
break;
|
||||
continue;
|
||||
|
||||
case LONG4:
|
||||
if (!LoadLong4())
|
||||
break;
|
||||
continue;
|
||||
|
||||
case APPEND:
|
||||
if (!LoadAppend())
|
||||
break;
|
||||
@@ -675,6 +691,47 @@ namespace GameRes.Formats.RenPy
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadLong ()
|
||||
{
|
||||
int count = m_stream.ReadByte();
|
||||
if (-1 == count)
|
||||
return false;
|
||||
m_stack.Push (DecodeLong (count));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadLong4 ()
|
||||
{
|
||||
int count = 0;
|
||||
if (!ReadInt (4, out count) || count < 0)
|
||||
return false;
|
||||
m_stack.Push (DecodeLong (count));
|
||||
return true;
|
||||
}
|
||||
|
||||
object DecodeLong (int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
return 0L;
|
||||
else if (count > 8)
|
||||
{
|
||||
var bytes = new byte[count];
|
||||
m_stream.Read (bytes, 0, count);
|
||||
return new BigInteger (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bytes = new byte[8];
|
||||
m_stream.Read (bytes, 0, count);
|
||||
if (0 != (bytes[count-1] & 0x80)) // sign bit is set
|
||||
{
|
||||
for (int i = count; i < bytes.Length; ++i)
|
||||
bytes[i] = 0xFF;
|
||||
}
|
||||
return bytes.ToInt64 (0);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadCountedTuple (int count)
|
||||
{
|
||||
if (m_stack.Count < count)
|
||||
|
||||
Binary file not shown.
@@ -84,6 +84,15 @@ namespace GameRes.Formats.Riddle
|
||||
stream.Position = 12;
|
||||
var reader = new CmpReader (stream.AsStream, meta.PackedSize, meta.DataSize);
|
||||
reader.Unpack();
|
||||
// 24bpp bitmaps have non-standard stride
|
||||
if (24 == meta.BPP && 0 != (meta.Width & 3)
|
||||
&& (meta.Height * meta.Width * 3 + 54) == meta.DataSize)
|
||||
{
|
||||
int stride = (int)meta.Width * 3;
|
||||
var pixels = new byte[stride * (int)meta.Height];
|
||||
Buffer.BlockCopy (reader.Data, 54, pixels, 0, pixels.Length);
|
||||
return ImageData.CreateFlipped (meta, PixelFormats.Bgr24, null, pixels, stride);
|
||||
}
|
||||
using (var bmp = new MemoryStream (reader.Data))
|
||||
{
|
||||
var decoder = new BmpBitmapDecoder (bmp,
|
||||
|
||||
80
ArcFormats/Slg/ArcSPD.cs
Normal file
80
ArcFormats/Slg/ArcSPD.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
//! \file ArcSPD.cs
|
||||
//! \date 2017 Aug 13
|
||||
//! \brief SLG system audio archive.
|
||||
//
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Slg
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class SpdOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "SPD/SLG"; } }
|
||||
public override string Description { get { return "SLG system audio archive"; } }
|
||||
public override uint Signature { get { return 0x504653; } } // 'SFP'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (file.Name.HasExtension (".SPL"))
|
||||
return null;
|
||||
var index_name = Path.ChangeExtension (file.Name, ".SPL");
|
||||
if (!VFS.FileExists (index_name))
|
||||
return null;
|
||||
using (var idx = VFS.OpenView (index_name))
|
||||
{
|
||||
if (!idx.View.AsciiEqual (0, "SFP\0"))
|
||||
return null;
|
||||
uint align = idx.View.ReadUInt32 (0xC);
|
||||
uint index_offset = 0x20;
|
||||
uint names_offset = idx.View.ReadUInt32 (index_offset);
|
||||
if (names_offset > idx.MaxOffset || names_offset <= index_offset)
|
||||
return null;
|
||||
int count = (int)(names_offset - index_offset) / 0x10;
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint name_offset = idx.View.ReadUInt32 (index_offset);
|
||||
var name = idx.View.ReadString (name_offset, (uint)(idx.MaxOffset - name_offset));
|
||||
if (string.IsNullOrWhiteSpace (name))
|
||||
return null;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Size = idx.View.ReadUInt32 (index_offset+4);
|
||||
entry.Offset = (long)idx.View.ReadUInt32 (index_offset+8) * align;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x10;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,21 +210,9 @@ namespace GameRes.Formats.Tanuki
|
||||
{
|
||||
try
|
||||
{
|
||||
var dir = FormatCatalog.Instance.DataDirectory;
|
||||
var lst_file = Path.Combine (dir, ListFileName);
|
||||
if (!File.Exists (lst_file))
|
||||
return new string[0];
|
||||
using (var input = new StreamReader (lst_file))
|
||||
{
|
||||
var names = new List<string>();
|
||||
string line;
|
||||
while ((line = input.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length > 0)
|
||||
names.Add (line);
|
||||
}
|
||||
return names.ToArray();
|
||||
}
|
||||
var names = new List<string>();
|
||||
FormatCatalog.Instance.ReadFileList (ListFileName, name => names.Add (name));
|
||||
return names.ToArray();
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
|
||||
@@ -320,6 +320,8 @@ namespace GameRes.Formats.TopCat
|
||||
name = Path.Combine (dir_name, name);
|
||||
name = Path.ChangeExtension (name, section.Extension);
|
||||
var entry = FormatCatalog.Instance.Create<TcdEntry> (name);
|
||||
if (name.HasExtension (".SPD"))
|
||||
entry.Type = "image";
|
||||
entry.Offset = offsets[index];
|
||||
entry.Size = offsets[index+1] - offsets[index];
|
||||
entry.Index = index;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
@@ -32,8 +33,15 @@ namespace GameRes.Formats.Valkyria
|
||||
{
|
||||
internal class Mg2MetaData : ImageMetaData
|
||||
{
|
||||
public int ImageLength;
|
||||
public int AlphaLength;
|
||||
public int ImageLength;
|
||||
public int AlphaLength;
|
||||
public IMg2Scheme Scheme;
|
||||
}
|
||||
|
||||
internal interface IMg2Scheme
|
||||
{
|
||||
Mg2EncryptedStream CreateStream (Stream main, int offset, int length);
|
||||
ImageData CreateImage (BitmapSource bitmap, ImageMetaData info);
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@@ -43,44 +51,54 @@ namespace GameRes.Formats.Valkyria
|
||||
public override string Description { get { return "Valkyria image format"; } }
|
||||
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
|
||||
|
||||
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2() };
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
if (!header.AsciiEqual (4, "CG01"))
|
||||
return null;
|
||||
int length = header.ToInt32 (8);
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, length))
|
||||
using (var png = new BinaryStream (input, file.Name))
|
||||
foreach (var scheme in KnownSchemes)
|
||||
{
|
||||
var info = Png.ReadMetaData (png);
|
||||
if (null == info)
|
||||
return null;
|
||||
return new Mg2MetaData
|
||||
using (var input = scheme.CreateStream (file.AsStream, 0x10, length))
|
||||
using (var png = new BinaryStream (input, file.Name))
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
OffsetX = info.OffsetX,
|
||||
OffsetY = info.OffsetY,
|
||||
BPP = info.BPP,
|
||||
ImageLength = length,
|
||||
AlphaLength = header.ToInt32 (12)
|
||||
};
|
||||
var info = Png.ReadMetaData (png);
|
||||
if (null == info)
|
||||
continue;
|
||||
return new Mg2MetaData
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
OffsetX = info.OffsetX,
|
||||
OffsetY = info.OffsetY,
|
||||
BPP = info.BPP,
|
||||
ImageLength = length,
|
||||
AlphaLength = header.ToInt32 (12),
|
||||
Scheme = scheme,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (Mg2MetaData)info;
|
||||
var frame = ReadBitmapSource (file.AsStream, meta);
|
||||
return meta.Scheme.CreateImage (frame, meta);
|
||||
}
|
||||
|
||||
BitmapSource ReadBitmapSource (Stream file, Mg2MetaData meta)
|
||||
{
|
||||
BitmapSource frame;
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, meta.ImageLength))
|
||||
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength))
|
||||
{
|
||||
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
frame = decoder.Frames[0];
|
||||
if (0 == meta.AlphaLength)
|
||||
{
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
if (frame.Format.BitsPerPixel != 32)
|
||||
frame = new FormatConvertedBitmap (frame, PixelFormats.Bgr32, null, 0);
|
||||
@@ -88,12 +106,14 @@ namespace GameRes.Formats.Valkyria
|
||||
var pixels = new byte[stride * (int)meta.Height];
|
||||
frame.CopyPixels (pixels, stride, 0);
|
||||
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10+meta.ImageLength, meta.AlphaLength))
|
||||
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength))
|
||||
{
|
||||
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
BitmapSource alpha_frame = decoder.Frames[0];
|
||||
if (alpha_frame.PixelWidth != frame.PixelWidth || alpha_frame.PixelHeight != frame.PixelHeight)
|
||||
return ImageData.Create (info, PixelFormats.Bgr32, null, pixels, stride);
|
||||
return BitmapSource.Create ((int)meta.Width, (int)meta.Height,
|
||||
ImageData.DefaultDpiX, ImageData.DefaultDpiY,
|
||||
PixelFormats.Bgr32, null, pixels, stride);
|
||||
|
||||
alpha_frame = new FormatConvertedBitmap (alpha_frame, PixelFormats.Gray8, null, 0);
|
||||
var alpha = new byte[alpha_frame.PixelWidth * alpha_frame.PixelHeight];
|
||||
@@ -104,7 +124,9 @@ namespace GameRes.Formats.Valkyria
|
||||
{
|
||||
pixels[dst] = alpha[src++];
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels, stride);
|
||||
return BitmapSource.Create ((int)meta.Width, (int)meta.Height,
|
||||
ImageData.DefaultDpiX, ImageData.DefaultDpiY,
|
||||
PixelFormats.Bgra32, null, pixels, stride);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,11 +139,23 @@ namespace GameRes.Formats.Valkyria
|
||||
internal class Mg2EncryptedStream : StreamRegion
|
||||
{
|
||||
readonly int m_threshold;
|
||||
readonly byte m_key;
|
||||
|
||||
public Mg2EncryptedStream (Stream main, int offset, int length)
|
||||
protected Mg2EncryptedStream (Stream main, int offset, int length, int threshold, byte key)
|
||||
: base (main, offset, length, true)
|
||||
{
|
||||
m_threshold = length / 5;
|
||||
m_threshold = threshold;
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
public static Mg2EncryptedStream CreateV1 (Stream main, int offset, int length)
|
||||
{
|
||||
return new Mg2EncryptedStream (main, offset, length, length / 5, 0);
|
||||
}
|
||||
|
||||
public static Mg2EncryptedStream CreateV2 (Stream main, int offset, int length)
|
||||
{
|
||||
return new Mg2EncryptedStream (main, offset, length, Math.Min (25, length), (byte)length);
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
@@ -129,7 +163,7 @@ namespace GameRes.Formats.Valkyria
|
||||
int pos = (int)Position;
|
||||
int read = base.Read (buffer, offset, count);
|
||||
for (int i = 0; i < read && pos < m_threshold; ++i)
|
||||
buffer[offset+i] ^= (byte)pos++;
|
||||
buffer[offset+i] ^= (byte)(m_key + pos++);
|
||||
return read;
|
||||
}
|
||||
|
||||
@@ -138,8 +172,37 @@ namespace GameRes.Formats.Valkyria
|
||||
long pos = Position;
|
||||
int b = base.ReadByte();
|
||||
if (b != -1 && pos < m_threshold)
|
||||
b ^= (byte)pos;
|
||||
b ^= (byte)(m_key + pos);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mg2SchemeV1 : IMg2Scheme
|
||||
{
|
||||
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
|
||||
{
|
||||
return Mg2EncryptedStream.CreateV1 (main, offset, length);
|
||||
}
|
||||
|
||||
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
|
||||
{
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mg2SchemeV2 : IMg2Scheme
|
||||
{
|
||||
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
|
||||
{
|
||||
return Mg2EncryptedStream.CreateV2 (main, offset, length);
|
||||
}
|
||||
|
||||
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
|
||||
{
|
||||
frame = new TransformedBitmap (frame, new ScaleTransform { ScaleY = -1 });
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace GameRes.Formats.YuRis
|
||||
return null;
|
||||
var parser = new Parser (file, version, count, dir_size);
|
||||
|
||||
var scheme = QueryEncryptionScheme (version);
|
||||
var scheme = QueryEncryptionScheme (file.Name, version);
|
||||
var dir = parser.ScanDir (scheme);
|
||||
if (null == dir || 0 == dir.Count)
|
||||
return null;
|
||||
@@ -175,10 +175,13 @@ namespace GameRes.Formats.YuRis
|
||||
return new GUI.CreateYPFWidget();
|
||||
}
|
||||
|
||||
YpfScheme QueryEncryptionScheme (uint version)
|
||||
YpfScheme QueryEncryptionScheme (string arc_name, uint version)
|
||||
{
|
||||
var options = Query<YpfOptions> (arcStrings.YPFNotice);
|
||||
var title = FormatCatalog.Instance.LookupGame (arc_name);
|
||||
YpfScheme scheme;
|
||||
if (!string.IsNullOrEmpty (title) && KnownSchemes.TryGetValue (title, out scheme))
|
||||
return scheme;
|
||||
var options = Query<YpfOptions> (arcStrings.YPFNotice);
|
||||
if (!KnownSchemes.TryGetValue (options.Scheme, out scheme) || null == scheme)
|
||||
scheme = new YpfScheme {
|
||||
SwapTable = GuessSwapTable (version),
|
||||
@@ -354,7 +357,7 @@ namespace GameRes.Formats.YuRis
|
||||
uint* header = (uint*)raw;
|
||||
uint version = header[1];
|
||||
int first_item, last_item;
|
||||
if (version >= 0x1CE || 0x12C == version)
|
||||
if (version >= 0x1CE || 0x12C == version || 0x19A == version)
|
||||
{
|
||||
first_item = 3;
|
||||
last_item = 7;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<AssemblyName>ArcExtra</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>9af0b529</NuGetPackageImportStamp>
|
||||
<NuGetPackageImportStamp>97deb393</NuGetPackageImportStamp>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
@@ -46,8 +46,8 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Concentus, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Concentus.1.1.3\lib\portable-net45+win+wpa81+wp80\Concentus.dll</HintPath>
|
||||
<Reference Include="Concentus, Version=1.1.6.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Concentus.Oggfile, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -66,8 +66,8 @@
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SQLite, Version=1.0.105.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Data.SQLite.Core.1.0.105.0\lib\net45\System.Data.SQLite.dll</HintPath>
|
||||
<Reference Include="System.Data.SQLite, Version=1.0.106.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Data.SQLite.Core.1.0.106.0\lib\net45\System.Data.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@@ -111,6 +111,15 @@
|
||||
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)
|
||||
exit 0</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.1\build\NETStandard.Library.targets')" />
|
||||
<Import Project="..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.106.0\build\net45\System.Data.SQLite.Core.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.0.5.23")]
|
||||
[assembly: AssemblyFileVersion ("1.0.5.23")]
|
||||
[assembly: AssemblyVersion ("1.0.6.26")]
|
||||
[assembly: AssemblyFileVersion ("1.0.6.26")]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Concentus" version="1.1.3" targetFramework="net45" />
|
||||
<package id="Concentus" version="1.1.7" targetFramework="net45" />
|
||||
<package id="Concentus.OggFile" version="1.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="2.0.0" targetFramework="net45" />
|
||||
<package id="MSFTCompressionCab" version="1.0.0" targetFramework="net45" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.105.0" targetFramework="net45" />
|
||||
<package id="NETStandard.Library" version="2.0.1" targetFramework="net45" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.106.0" targetFramework="net45" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -92,8 +92,8 @@
|
||||
<Reference Include="Microsoft.WindowsAPICodePack.Shell">
|
||||
<HintPath>..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NAudio.1.8.0\lib\net35\NAudio.dll</HintPath>
|
||||
<Reference Include="NAudio, Version=1.8.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NAudio.1.8.3\lib\net35\NAudio.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace GARbro.GUI
|
||||
var oem = CultureInfo.CurrentCulture.TextInfo.OEMCodePage;
|
||||
list.Add (Encoding.GetEncoding (oem));
|
||||
list.Add (Encoding.GetEncoding (932));
|
||||
list.Add (Encoding.GetEncoding (936));
|
||||
list.Add (Encoding.UTF8);
|
||||
list.Add (Encoding.Unicode);
|
||||
list.Add (Encoding.BigEndianUnicode);
|
||||
|
||||
@@ -51,5 +51,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.4.30.1888")]
|
||||
[assembly: AssemblyFileVersion ("1.4.30.1888")]
|
||||
[assembly: AssemblyVersion ("1.4.32.1939")]
|
||||
[assembly: AssemblyFileVersion ("1.4.32.1939")]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NAudio" version="1.8.0" targetFramework="net45" />
|
||||
<package id="NAudio" version="1.8.3" targetFramework="net45" />
|
||||
<package id="WindowsAPICodePack-Core" version="1.1.2" targetFramework="net45" />
|
||||
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net45" />
|
||||
<package id="WPFToolkit" version="3.5.50211.1" targetFramework="net45" />
|
||||
|
||||
@@ -313,6 +313,27 @@ namespace GameRes
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read text file <paramref name="filename"/> from data directory, performing <paramref name="process_line"/> action on each line.
|
||||
/// </summary>
|
||||
public void ReadFileList (string filename, Action<string> process_line)
|
||||
{
|
||||
var lst_file = Path.Combine (DataDirectory, filename);
|
||||
if (!File.Exists (lst_file))
|
||||
return;
|
||||
using (var input = new StreamReader (lst_file))
|
||||
{
|
||||
string line;
|
||||
while ((line = input.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length > 0)
|
||||
{
|
||||
process_line (line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NAudio, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NAudio.1.8.0\lib\net35\NAudio.dll</HintPath>
|
||||
<Reference Include="NAudio, Version=1.8.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NAudio.1.8.3\lib\net35\NAudio.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
|
||||
@@ -129,6 +129,8 @@ namespace GameRes
|
||||
meta.Width = Binary.BigEndian (file.ReadUInt32());
|
||||
meta.Height = Binary.BigEndian (file.ReadUInt32());
|
||||
int bpp = file.ReadByte();
|
||||
if (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8 && bpp != 16)
|
||||
return null;
|
||||
int color_type = file.ReadByte();
|
||||
switch (color_type)
|
||||
{
|
||||
@@ -136,7 +138,8 @@ namespace GameRes
|
||||
case 3: meta.BPP = 24; break;
|
||||
case 4: meta.BPP = bpp*2; break;
|
||||
case 6: meta.BPP = bpp*4; break;
|
||||
default: meta.BPP = bpp; break;
|
||||
case 0: meta.BPP = bpp; break;
|
||||
default: return null;
|
||||
}
|
||||
SkipBytes (file, 7);
|
||||
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.4.31.255")]
|
||||
[assembly: AssemblyFileVersion ("1.4.31.255")]
|
||||
[assembly: AssemblyVersion ("1.4.33.260")]
|
||||
[assembly: AssemblyFileVersion ("1.4.33.260")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NAudio" version="1.8.0" targetFramework="net45" />
|
||||
<package id="NAudio" version="1.8.3" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -98,6 +98,7 @@ Grisaia no Kajitsu<br/>
|
||||
Happiness! Re:Lucks<br/>
|
||||
Kamikaze ☆ Explorer!<br/>
|
||||
Koko kara Natsu no Innocence!<br/>
|
||||
Labyrinth of Grisaia<br/>
|
||||
Makai Tenshi Djibril -Episode 4-<br/>
|
||||
Sakigake ⇒ Generation!<br/>
|
||||
Sengoku Tenshi Djibril<br/>
|
||||
@@ -229,6 +230,7 @@ Iinari<br/>
|
||||
Kyrie ~Blood Royal 3~<br/>
|
||||
Shiosai no Himei<br/>
|
||||
Tender Breeze<br/>
|
||||
Zoku Hitou Meguri<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.gcp</td><td><tt>CMP1</tt></td><td></td></tr>
|
||||
<tr><td>*.pd</td><td><tt>PackOnly</tt><br/><tt>PackPlus</tt><br/><tt>FlyingShinePDFile</tt></td><td><span class="supported"></span></td><td>Flying Shine</td><td>
|
||||
@@ -236,7 +238,10 @@ Akarui Mirai ~Wet And Messy 2nd time~<br/>
|
||||
Cross†Channel<br/>
|
||||
Crime Rhyme<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.rpa</td><td><tt>RPA-3.0</tt></td><td><span class="supported"></span></td><td>Ren'Py</td><td>Katawa Shoujo</td></tr>
|
||||
<tr class="odd"><td>*.rpa</td><td><tt>RPA-3.0</tt></td><td><span class="supported"></span></td><td>Ren'Py</td><td>
|
||||
Katawa Shoujo<br/>
|
||||
Sakura Halloween<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.arc</td><td>-</td><td><span class="supported"></span></td><td rowspan="3">Will</td><td rowspan="3">
|
||||
Chaste ☆ Chase!<br/>
|
||||
Cynthia ~Sincerely to You~<br/>
|
||||
@@ -244,6 +249,7 @@ Folklore Jam<br/>
|
||||
Hapihosu! ~Osewasaremasu Nyuuin Seikatsu~<br/>
|
||||
I/O<br/>
|
||||
Kenseiki Alpha Ride<br/>
|
||||
Mirai Kanojo<br/>
|
||||
Natsuiro Kouen ~Denpatou no Shita de Ai wo Kataru~<br/>
|
||||
Omoi o Sasageru Otome no Melody<br/>
|
||||
Onegai O-Hoshi-sama<br/>
|
||||
@@ -303,6 +309,7 @@ Grisaia: Phantom Trigger Vol.2<br/>
|
||||
Hachukano<br/>
|
||||
Hanafubuki ~Sennen no Koi o Shimashita~<br/>
|
||||
Haruiro ☆ Communication ♪<br/>
|
||||
Haruoto Alice*Gram<br/>
|
||||
Heliotrope -Sore wa Shi ni Itaru Kami no Ai-<br/>
|
||||
Hime to Majin to Koi Suru Tamashii<br/>
|
||||
Imouto Style<br/>
|
||||
@@ -312,6 +319,7 @@ Kagachi-sama Onagusame Tatematsurimasu<br/>
|
||||
Karakara<br/>
|
||||
Karenai Sekai to Owaru Hana<br/>
|
||||
Kicking Horse Rhapsody<br/>
|
||||
Koi ga Saku Koro Sakura Doki<br/>
|
||||
Kourin no Machi, Lavender no Shoujo<br/>
|
||||
Kozukuri Onsen ~Ippai Tsukutte Ichizoku Hanei~<br/>
|
||||
Kurenai no Tsuki<br/>
|
||||
@@ -322,6 +330,7 @@ Maitetsu<br/>
|
||||
Mizukoi<br/>
|
||||
Mizu no Miyako no Patisserie<br/>
|
||||
Nakadashi Hara Maid series<br/>
|
||||
Namaiki Delation<br/>
|
||||
Natsupochi<br/>
|
||||
Natsuzora Kanata<br/>
|
||||
Nidaime wa ☆ Mahou Shoujo<br/>
|
||||
@@ -359,6 +368,8 @@ Teakamamire no Tenshi<br/>
|
||||
Toaru Jukujo no Hentai Choukyou<br/>
|
||||
Towazugatari ~Shoujo Ryoujoku Hishou~<br/>
|
||||
Ushinawareta Mirai o Motomete<br/>
|
||||
Wagamama High Spec<br/>
|
||||
Walpurgis<br/>
|
||||
With Ribbon<br/>
|
||||
Yome Sagashi ga Hakadori Sugite Yabai.<br/>
|
||||
Yomibito Shirazu ~Amai Meshibe no Seichoushi~<br/>
|
||||
@@ -371,10 +382,13 @@ Zettai Karen! Ojou-sama<br/>
|
||||
<tr class="last"><td>*.tlg</td><td><tt>TLG0.0</tt><br/><tt>TLG5.0</tt><br/><tt>TLG6.0</tt></td><td></td></tr>
|
||||
<tr class="odd"><td>*.ypf</td><td><tt>YPF</tt></td><td><span class="supported"></span></td><td rowspan="2">YU-RIS</td><td rowspan="2">
|
||||
77 (Sevens) ~And, Two Stars Meet Again~<br/>
|
||||
Aikagi<br/>
|
||||
Eroge! ~H mo Game mo Kaihatsu Zanmai~<br/>
|
||||
Haramase Taiken Gakushuu ~Shizen no Naka de Seikyouiku!?~<br/>
|
||||
Koi Mekuri Clover<br/>
|
||||
Mamono Musume-tachi to no Rakuen ~Slime & Scylla~<br/>
|
||||
Mashou no Nie 3 ~Hakudaku no Umi ni Shizumu Injoku no Reiki~<br/>
|
||||
M.C. Saimin Kenkyuu<br/>
|
||||
Ryuuyoku no Melodia -Diva with the blessed dragonol-<br/>
|
||||
Sei Monmusu Festival!!<br/>
|
||||
Shin Chikan Ou<br/>
|
||||
@@ -531,6 +545,7 @@ Jukubo Gui ~Moto Guradoru no Okaa-san~ <span class="footnote">ShiinaRio v2.37</s
|
||||
Kateinai Choukyou <span class="footnote">ShiinaRio v2.31</span><br/>
|
||||
Kichiku Nakadashi Suieibu<span class="footnote">ShiinaRio v2.41</span><br/>
|
||||
Last Waltz ~Hakudaku Mamire no Natsu Gasshuku~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Libra of the Vampire Princess <span class="footnote">ShiinaRio v2.50</span><br/>
|
||||
Mahou Shoujo no Taisetsu na Koto <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Maki Fes! <span class="footnote">ShiinaRio v2.50</span><br/>
|
||||
Mikoko <span class="footnote">ShiinaRio v2.46</span><br/>
|
||||
@@ -549,6 +564,7 @@ Ren'ai Saimin ~Tsun na Kanojo ga dereru Saimin~ <span class="footnote">ShiinaRio
|
||||
Rin×Sen <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Ryoumaden ~Houkago no Rakuen~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Sabae no Ou <span class="footnote">ShiinaRio v2.36</span><br/>
|
||||
Saimin Seikatsu ~Kousoku Dakara Shikatanai!?~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Shinigami no Testament <span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
Shinseki no Oba-san ~Hanare no Netsujo, Honke no Gosai~ <span class="footnote">ShiinaRio v2.36</span><br/>
|
||||
Shojo Mama<span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
@@ -630,6 +646,7 @@ WW&F ~Taishou Teito Denkitan~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.dat</td><td><tt>GAMEDAT PACK</tt><br/><tt>GAMEDAT PAC2</tt></td><td></td><td rowspan="2"> bootUP!<br/>Pajamas Soft<br/>Aries</td><td rowspan="2">
|
||||
Aneimo 2 ~Second Stage~<br/>
|
||||
Lilycle Rainbow Stage!!!<br/>
|
||||
Momichupa Teacher!<br/>
|
||||
Natsu no Owari no Nirvana<br/>
|
||||
Patissier na Nyanko<br/>
|
||||
@@ -683,6 +700,7 @@ Koishiki Manual<br/>
|
||||
Nanairo Kouro<br/>
|
||||
Natsu no Ame<br/>
|
||||
Nerawareta Megami Tenshi Angeltia<br/>
|
||||
Ouchi ni Kaeru made ga Mashimaro Desu<br/>
|
||||
Parallel Harmony<br/>
|
||||
re-Laive<br/>
|
||||
Sakura Musubi<br/>
|
||||
@@ -741,6 +759,7 @@ Zetsuboushi<br/>
|
||||
<tr><td>*.pack</td><td><tt>FilePackVer1.0</tt><br/><tt>FilePackVer2.0</tt><br/><tt>FilePackVer3.0</tt><br/><tt>FilePackVer3.1</tt></td><td></td><td rowspan="3">QLIE</td><td rowspan="3">
|
||||
Amanatsu Adolesence Trial 2<br/>
|
||||
Bishoujo Mangekyou -Kami ga Tsukuritamouta Shoujo-tachi-<br/>
|
||||
Bishoujo Mangekyou -Tsumi to Batsu no Shoujo-<br/>
|
||||
Harem Hospital<br/>
|
||||
Hidamari Basket<br/>
|
||||
Kikan Bakumatsu Ibun Last Cavalier<br/>
|
||||
@@ -773,6 +792,7 @@ Double<br/>
|
||||
Exile ~Blood Royal 2~<br/>
|
||||
Gakuen ~Nerawareta Chitai~<br/>
|
||||
Kimon Youitan<br/>
|
||||
Onna Kyoushi Saeko ~Biniku Jugyou~<br/>
|
||||
Unbalance<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.lib<br/>*.dat</td><td><tt>LIB</tt><br/><tt>LIBP</tt><br/><tt>LIBU</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="3">Malie</td><td rowspan="3">
|
||||
@@ -782,6 +802,7 @@ Deep Love Diary<br/>
|
||||
Dies irae<br/>
|
||||
Dies irae ~Amantes amentes~<br/>
|
||||
Dokidoki Sister Paradise 2<br/>
|
||||
Fuyu Uso -Snow World End-<br/>
|
||||
Kajiri Kamui Kagura<br/>
|
||||
Paradise Lost<br/>
|
||||
Sacrifice ~Seifuku Gari~<br/>
|
||||
@@ -827,6 +848,7 @@ Yuukyou Gangu 2<br/>
|
||||
<tr class="odd last"><td>*.wwa</td><td><tt>WPX\x1AWAV</tt></td><td></td></tr>
|
||||
<tr><td>*.mrg</td><td><tt>MRG</tt></td><td></td><td rowspan="3">F&C</td><td rowspan="3">
|
||||
Asa no Konai Yoru ni Dakarete -Eternal Night-<br/>
|
||||
Beside ~Shiawase wa Katawara ni~<br/>
|
||||
Guren ni Somaru Gin no Rozario<br/>
|
||||
Konata yori Kanata made<br/>
|
||||
Natural ~Mi mo Kokoro mo~<br/>
|
||||
@@ -867,7 +889,7 @@ Vampire Crusaders<br/>
|
||||
<tr class="last"><td>*.cgf</td><td>-</td><td></td></tr>
|
||||
<tr class="odd"><td>*.bin+*.pak</td><td><tt>hed</tt></td><td></td><td rowspan="2">elf</td><td rowspan="2">Adult Video King</td></tr>
|
||||
<tr class="odd last"><td>*.hip<br/>*.hiz<br/></td><td><tt>hip</tt><br/><tt>hiz</tt></td><td></td></tr>
|
||||
<tr><td>*.gpk+*.gtb<br/>*.vpk+*.vtb</td><td>-</td><td></td><td rowspan="3">Black Cyc</td><td rowspan="3">
|
||||
<tr><td>*.gpk+*.gtb<br/>*.vpk+*.vtb</td><td>-</td><td></td><td rowspan="3">System-NNN</td><td rowspan="3">
|
||||
Before Dawn Daybreak ~Shinen no Utahime~<br/>
|
||||
Extravaganza ~Mushi Mederu Shoujo~<br/>
|
||||
Gokuin no Sentou<br/>
|
||||
@@ -876,6 +898,7 @@ Hana Goyomi<br/>
|
||||
Jishou Seirei Majutsushi vs Shinsei Daiikkyuu Akuma<br/>
|
||||
Kurogane no Tsubasa ~The Alchemist's Story~<br/>
|
||||
Mushitsukai<br/>
|
||||
Ningyou-hime no Shiro ~Anata wa Mou, Atashi no Omocha~<br/>
|
||||
Yami no Koe series<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.dwq</td><td><tt>BMP</tt><br/><tt>JPEG</tt><br/><tt>PNG</tt><br/><tt>JPEG+MASK</tt><br/><tt>PACKBMP+MASK</tt><br/></td><td></td></tr>
|
||||
@@ -895,6 +918,7 @@ Kikouyoku Senki Gin no Toki no Corona<br/>
|
||||
Kikouyoku Senki Tenkuu no Yumina<br/>
|
||||
Nega0<br/>
|
||||
Seinarukana -The Spirit of Eternity Sword 2-<br/>
|
||||
Ten no Hikari wa Koi no Hoshi<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.wag<br/>*.4ag<br/>*.004</td><td><tt>WAG@</tt><br/><tt>GAF4</tt></td><td></td></tr>
|
||||
<tr class="odd"><td>*.ykc</td><td><tt>YKC001</tt></td><td><span class="supported"></span></td><td rowspan="2">Yuka</td><td rowspan="2">
|
||||
@@ -991,6 +1015,7 @@ Moshimo Ashita ga Harenaraba<br/>
|
||||
<tr class="odd"><td>*.pga</td><td><tt>PGAPGAH</tt></td><td><span class="supported"></span></td></tr>
|
||||
<tr class="odd last"><td>*.chr</td><td><tt>char</tt></td><td></td></tr>
|
||||
<tr><td>*.gyu</td><td><tt>GYU\x1a</tt></td><td></td><td rowspan="2">ExHIBIT</td><td rowspan="2">
|
||||
Ane Ane Double Saimin 2<br/>
|
||||
Eve ~New Generation X~<br/>
|
||||
Fuyu no Rondo<br/>
|
||||
Imouto Paradise! 2<br/>
|
||||
@@ -1072,6 +1097,7 @@ Haruiro Ouse<br/>
|
||||
Memoria<br/>
|
||||
Miharu - Alto Another Story<br/>
|
||||
Natsu ni Kanaderu Bokura no Uta<br/>
|
||||
Shiawase Kazoku-bu<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.pb2</td><td><tt>PB2A</tt></td><td></td></tr>
|
||||
<tr><td>*.pb3</td><td><tt>PB3B</tt></td><td></td></tr>
|
||||
@@ -1087,6 +1113,7 @@ AstralAir no Shiroki Towa<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.hzc</td><td><tt>hzc1</tt></td><td></td></tr>
|
||||
<tr class="odd"><td>*.bin</td><td><tt>ESC-ARC1</tt><br/><tt>ESC-ARC2</tt></td><td></td><td>Escu:de</td><td>
|
||||
Hyakki Ryouran no Yakata<br/>
|
||||
Otome Renshin Prister<br/>
|
||||
Suisei Tenshi Primaveil Zwei<br/>
|
||||
Verdia Gensoukyoku<br/>
|
||||
@@ -1101,15 +1128,17 @@ Paimega<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.snn+*.inx</td><td>-</td><td></td><td rowspan="3">BlueGale</td><td rowspan="3">
|
||||
Bifronte ~Kugaitou Kitan~<br/>
|
||||
Bikini Junkie<br/>
|
||||
Cafe Junkie<br/>
|
||||
Immoral<br/>
|
||||
Joi Okkusu<br/>
|
||||
Majidashi! Royale ~Finish wa Watashi no Naka de~<br/>
|
||||
MILK Junkies<br/>
|
||||
Oppai Life<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.zbm</td><td><tt>amp_</tt></td><td></td></tr>
|
||||
<tr class="odd last"><td>*.amv</td><td><tt>ampV</tt></td><td></td></tr>
|
||||
<tr><td>*.vfs</td><td><tt>VF</tt></td><td></td><td rowspan="5">Aoi</td><td rowspan="5">
|
||||
<tr><td>*.vfs</td><td><tt>VF</tt></td><td></td><td rowspan="6">Aoi</td><td rowspan="6">
|
||||
Alfred Gakuen Mamono Daitai<br/>
|
||||
Brown Doori Sanbanme<br/>
|
||||
Daisounan<br/>
|
||||
@@ -1174,10 +1203,12 @@ Tokyo Necro<br/>
|
||||
Thanatos no Koi ~In Ane Otouto Soukan~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.gps</td><td><tt>GPS</tt></td><td></td></tr>
|
||||
<tr><td>*.szs</td><td><tt>SZS100__</tt></td><td></td><td rowspan="4">SLG system</td><td rowspan="4">
|
||||
<tr><td>*.szs</td><td><tt>SZS100__</tt></td><td></td><td rowspan="5">SLG system</td><td rowspan="5">
|
||||
Sangoku Hime 2<br/>
|
||||
Sengoku Hime<br/>
|
||||
Shihen 69 ~Shinen no Messiah~<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.spd+*.spl</td><td><tt>SFP</tt></td><td></td></tr>
|
||||
<tr><td>*.tig</td><td><tt>\x7C\xF3\xC2\x8B</tt></td><td></td></tr>
|
||||
<tr><td>*.alb</td><td><tt>ALB1.21</tt></td><td></td></tr>
|
||||
<tr class="last"><td>*.voi</td><td>-</td><td></td></tr>
|
||||
@@ -1189,6 +1220,7 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-<br/>
|
||||
<tr><td>*.eme</td><td><tt>RREDATA</tt></td><td></td><td>Emon Engine</td><td>
|
||||
Ase Nure Shoujo Misaki "Anata no Nioi de Icchau!"<br/>
|
||||
D-spray Biyaku de Motemote Kachou Dairi Hosa<br/>
|
||||
D-spray 2 Biyaku de Motemote Kachou Dairi Yukemuri Ryojou Hen<br/>
|
||||
Hitomi no Rakuin ~Inbaku no Mesu Dorei~<br/>
|
||||
Jokujima<br/>
|
||||
Saiin Haramase Keyword<br/>
|
||||
@@ -1240,6 +1272,7 @@ Uso x Mote ~Usonko Motemote-shon~<br/>
|
||||
Chijoku Yuugi<br/>
|
||||
Kakure Ecchi<br/>
|
||||
OL Gari ~Nureta Office~<br/>
|
||||
Machina no Kiseki ~Ogeretsu Daihyakka Gaiden~<br/>
|
||||
Taiiku Souko ~Shoujotachi no Sange~<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.cmp</td><td><tt>SBI</tt></td><td></td><td rowspan="2">Vitamin</td><td rowspan="2">
|
||||
@@ -1295,6 +1328,7 @@ Etsuraku no Tane<br/>
|
||||
Fit Shichao! ~Toshiue Josei to Asedaku Lesson Hatsutaiken~<br/>
|
||||
Fuka no Jugoku<br/>
|
||||
Gakkou Yarashii Kaidan<br/>
|
||||
Gibo no Shizuku ~Shimetta Hada kara Kaori Tatsu Amai Iroka~<br/>
|
||||
Gibo no Toiki ~Haitoku Kokoro ni tadayou Haha no Iroka~<br/>
|
||||
Gohoushi★Princess<br/>
|
||||
Gokkun♪ Vampire☆Princess<br/>
|
||||
@@ -1422,6 +1456,7 @@ Stray Sheep ~Chijoku no Zangeshitsu~<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.i</td><td><tt>IMG2</tt></td><td></td></tr>
|
||||
<tr class="odd"><td>*.pk</td><td><tt>fPK</tt></td><td></td><td rowspan="3">Ivory</td><td rowspan="3">
|
||||
JK Free Hall<br/>
|
||||
Triangle Heart 1-2-3<br/>
|
||||
</td></tr>
|
||||
<tr class ="odd"><td>*.sg</td><td><tt>fSG</tt></td><td></td></tr>
|
||||
@@ -1442,8 +1477,10 @@ Eden's Ritter - Inetsu no Seima Kishi Lucifer Hen<br/>
|
||||
In Vitro Shoujo -Aragaishi Junshin Naru Shoujo-<br/>
|
||||
Kyonyuu Fantasy - Digital Novel<br/>
|
||||
Shirudaku Settai Okawari Ippaime<br/>
|
||||
Zen'aku<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.pcf</td><td><tt>PackCode</tt></td><td></td><td rowspan="2">Symphony</td><td rowspan="2">
|
||||
Fortuna Rhapsody<br/>
|
||||
Tiara<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.gbc</td><td><tt>GBCF</tt></td><td></td></tr>
|
||||
@@ -1552,6 +1589,7 @@ Sutadoru!<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.pcd</td><td>-</td><td></td></tr>
|
||||
<tr class="odd"><td>*.tac<br/>*.stx</td><td><tt>TArc1.10</tt><br/><tt>TArc1.00</tt></td><td></td><td>TanukiSoft</td><td>
|
||||
Onii-chan Daisuki!<br/>
|
||||
Shoujo Kyouiku<br/>
|
||||
Shoujo Ramune<br/>
|
||||
Shoukoujo<br/>
|
||||
@@ -1589,6 +1627,7 @@ Kannagi<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.pak</td><td><tt>CCf"</tt></td><td></td></tr>
|
||||
<tr><td>*.dat</td><td>-</td><td></td><td rowspan="3">Valkyria</td><td rowspan="3">
|
||||
Gedou Mahou Shoujo Rinne ~Akuin Akka~<br/>
|
||||
Kangokujou ~Ingyaku no Shihai~<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.mg2</td><td><tt>MICOCG01</tt></td><td></td></tr>
|
||||
@@ -1604,6 +1643,25 @@ Sengoku Hime 7<br/>
|
||||
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>YaneSDK?</td><td>
|
||||
Niizuma Lovely x Cation<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.pac</td><td><tt>ぱく</tt></td><td></td><td>LunaSoft</td><td>
|
||||
Mahou Shoujo Lyiene<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.fga</td><td>-</td><td></td><td rowspan="2">SFA</td><td rowspan="2">
|
||||
Maokko Crisis<br/>
|
||||
<tr class="odd last"><td>*.abm</td><td><tt>BM</tt></td><td></td></tr>
|
||||
</td></tr>
|
||||
<tr><td>*.alo</td><td>-</td><td><span class="supported"></span></td><td rowspan="2">BeF</td><td rowspan="2">
|
||||
Manatsu no Yoru no Yume<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.vzy</td><td>-</td><td></td></tr>
|
||||
<tr class="odd"><td>*.dat</td><td><tt>RepiPack</tt></td><td></td><td>Littlewitch</td><td>
|
||||
Eiyu*Senki - The World Conquest<br/>
|
||||
</td></tr>
|
||||
<tr class="last"><td>arc*.dat</td><td>-</td><td></td><td>non color</td><td>
|
||||
Hana Hime * Absolute!<br/>
|
||||
Yuuki Yuuna wa Yuusha de Aru<br/>
|
||||
Yuuki Yuuna wa Yuusha de Aru S<br/>
|
||||
</td></tr>
|
||||
</table>
|
||||
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
||||
</body>
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<GARbro>
|
||||
<Release>
|
||||
<Version>1.4.30</Version>
|
||||
<Version>1.4.32</Version>
|
||||
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
|
||||
<Notes>New formats:
|
||||
- 'pf6' archives
|
||||
- DxLib version 6 archives (Detective Seven)
|
||||
- updated encryption schemes for malie LIBP archives
|
||||
- added more KiriKiri and ShiinaRio encryption schemes
|
||||
- LunaSoft PAC archives
|
||||
- FGA archives
|
||||
- 'RepiPack' archives
|
||||
- ALO images and VZY audio
|
||||
- 'fPK2' archives
|
||||
- updated RPA archives support
|
||||
- more KiriKiri and Csystem encryption schemes
|
||||
</Notes>
|
||||
</Release>
|
||||
<FormatsData>
|
||||
<FileVersion>72</FileVersion>
|
||||
<FileVersion>86</FileVersion>
|
||||
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
|
||||
<Requires>
|
||||
<Assembly Name="ArcFormats" Version="1.2.33.1392"/>
|
||||
<Assembly Name="ArcFormats" Version="1.2.35.1417"/>
|
||||
<Assembly Name="GameRes" Version="1.4.26.238"/>
|
||||
</Requires>
|
||||
</FormatsData>
|
||||
|
||||
Reference in New Issue
Block a user