mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-06 21:58:53 +08:00
Compare commits
31 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 |
@@ -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" />
|
||||
@@ -112,8 +115,11 @@
|
||||
<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" />
|
||||
@@ -125,6 +131,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -984,7 +1076,7 @@ namespace GameRes.Formats.KiriKiri
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KissCrypt : ICrypt
|
||||
public class KissCrypt : CzCrypt
|
||||
{
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] data, int pos, int count)
|
||||
{
|
||||
@@ -1116,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)
|
||||
{
|
||||
@@ -1142,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.35.1414")]
|
||||
[assembly: AssemblyFileVersion ("1.2.35.1414")]
|
||||
[assembly: AssemblyVersion ("1.2.36.1443")]
|
||||
[assembly: AssemblyFileVersion ("1.2.36.1443")]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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.31.1908")]
|
||||
[assembly: AssemblyFileVersion ("1.4.31.1908")]
|
||||
[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" />
|
||||
|
||||
@@ -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.32.257")]
|
||||
[assembly: AssemblyFileVersion ("1.4.32.257")]
|
||||
[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>
|
||||
@@ -230,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>
|
||||
@@ -237,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/>
|
||||
@@ -245,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/>
|
||||
@@ -314,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/>
|
||||
@@ -324,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/>
|
||||
@@ -361,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/>
|
||||
@@ -379,6 +388,7 @@ 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/>
|
||||
@@ -690,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/>
|
||||
@@ -781,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">
|
||||
@@ -836,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/>
|
||||
@@ -876,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/>
|
||||
@@ -885,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>
|
||||
@@ -904,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">
|
||||
@@ -1000,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/>
|
||||
@@ -1081,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>
|
||||
@@ -1111,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/>
|
||||
@@ -1253,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">
|
||||
@@ -1308,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/>
|
||||
@@ -1435,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>
|
||||
@@ -1621,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,19 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<GARbro>
|
||||
<Release>
|
||||
<Version>1.4.31</Version>
|
||||
<Version>1.4.32</Version>
|
||||
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
|
||||
<Notes>Changes:
|
||||
- updated INT, QLIE, YPF, PCF, GBC, MG2 formats support
|
||||
- added more KiriKiri and Malie encryption schemes
|
||||
- added CP936 encoding option to text viewer
|
||||
<Notes>New formats:
|
||||
- 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>78</FileVersion>
|
||||
<FileVersion>86</FileVersion>
|
||||
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
|
||||
<Requires>
|
||||
<Assembly Name="ArcFormats" Version="1.2.35.1414"/>
|
||||
<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