mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-06 21:58:53 +08:00
Compare commits
2 Commits
v1.2.13
...
game-title
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ea25af83d | ||
|
|
1603b45415 |
@@ -57,11 +57,12 @@ namespace GameRes.Formats
|
||||
|
||||
public static AutoEntry Create (ArcView file, long offset, string base_name)
|
||||
{
|
||||
return new AutoEntry (base_name, () => DetectFileType (file.View.ReadUInt32 (offset))) { Offset = offset };
|
||||
return new AutoEntry (base_name, () => DetectFileType (file, offset)) { Offset = offset };
|
||||
}
|
||||
|
||||
public static IResource DetectFileType (uint signature)
|
||||
public static IResource DetectFileType (ArcView file, long offset)
|
||||
{
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
if (0 == signature) return null;
|
||||
// resolve some special cases first
|
||||
if (s_OggFormat.Value.Signature == signature)
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
<Compile Include="AZSys\ImageTYP1.cs" />
|
||||
<Compile Include="BlueGale\ArcSNN.cs" />
|
||||
<Compile Include="BlueGale\ImageZBM.cs" />
|
||||
<Compile Include="Circus\ArcPCK.cs" />
|
||||
<Compile Include="Cmvs\ArcCPZ.cs" />
|
||||
<Compile Include="ArcPBX.cs" />
|
||||
<Compile Include="Banana\ArcPK.cs" />
|
||||
@@ -83,10 +82,8 @@
|
||||
<Compile Include="CatSystem\ImageHG2.cs" />
|
||||
<Compile Include="Cmvs\CmvsMD5.cs" />
|
||||
<Compile Include="Cmvs\ImagePB3.cs" />
|
||||
<Compile Include="CsWare\ArcPCS.cs" />
|
||||
<Compile Include="DenSDK\ArcDAF.cs" />
|
||||
<Compile Include="elf\ArcVSD.cs" />
|
||||
<Compile Include="Entis\ErisaNemesis.cs" />
|
||||
<Compile Include="Escude\ArcBIN.cs" />
|
||||
<Compile Include="Eushully\ArcGPC.cs" />
|
||||
<Compile Include="Eushully\ImageGP.cs" />
|
||||
@@ -101,12 +98,8 @@
|
||||
<Compile Include="Glib2\ArcG2.cs" />
|
||||
<Compile Include="Glib2\ImagePGX.cs" />
|
||||
<Compile Include="ImagePSD.cs" />
|
||||
<Compile Include="Kaguya\ArcANM.cs" />
|
||||
<Compile Include="Kaguya\ArcLINK.cs" />
|
||||
<Compile Include="Kaguya\ImageAO.cs" />
|
||||
<Compile Include="KiriKiri\CryptAlgorithms.cs" />
|
||||
<Compile Include="Liar\ArcLWG.cs" />
|
||||
<Compile Include="MokoPro\CompressedFile.cs" />
|
||||
<Compile Include="Propeller\ArcMGR.cs" />
|
||||
<Compile Include="Propeller\ArcMPK.cs" />
|
||||
<Compile Include="Propeller\ImageMGR.cs" />
|
||||
@@ -114,7 +107,6 @@
|
||||
<Compile Include="Silky\ImageAKB.cs" />
|
||||
<Compile Include="Softpal\ArcVAFS.cs" />
|
||||
<Compile Include="Softpal\ImageBPIC.cs" />
|
||||
<Compile Include="Softpal\ImagePIC.cs" />
|
||||
<Compile Include="StudioEgo\ArcPAK0.cs" />
|
||||
<Compile Include="StudioEgo\ImageANT.cs" />
|
||||
<Compile Include="TechnoBrain\ImageIPH.cs" />
|
||||
@@ -233,8 +225,6 @@
|
||||
<Compile Include="Tmr-Hiro\ImageGRD.cs" />
|
||||
<Compile Include="TopCat\ArcTCD3.cs" />
|
||||
<Compile Include="TopCat\ImageSPD.cs" />
|
||||
<Compile Include="Triangle\ArcCGF.cs" />
|
||||
<Compile Include="Triangle\ArcIAF.cs" />
|
||||
<Compile Include="uGOS\ArcDET.cs" />
|
||||
<Compile Include="uGOS\ImageBMP.cs" />
|
||||
<Compile Include="UMeSoft\ArcPK.cs" />
|
||||
@@ -423,8 +413,6 @@
|
||||
<Compile Include="YuRis\WidgetYPF.xaml.cs">
|
||||
<DependentUpon>WidgetYPF.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Softpal\WaveTable1" />
|
||||
<EmbeddedResource Include="Softpal\WaveTable2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GameRes\GameRes.csproj">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Jun 15 16:11:56 2015
|
||||
//! \brief Circus archive format.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
// Copyright (C) 2015 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
|
||||
@@ -51,8 +51,6 @@ namespace GameRes.Formats.Circus
|
||||
if (count <= 1 || count > 0xfffff)
|
||||
return null;
|
||||
var dir = ReadIndex (file, count, 0x30);
|
||||
if (null == dir)
|
||||
dir = ReadIndex (file, count, 0x3C);
|
||||
if (null == dir)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
@@ -66,14 +64,12 @@ namespace GameRes.Formats.Circus
|
||||
return null;
|
||||
--count;
|
||||
uint next_offset = file.View.ReadUInt32 (index_offset+name_length);
|
||||
if (next_offset < 4+index_size)
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
string name = file.View.ReadString (index_offset, (uint)name_length);
|
||||
if (0 == name.Length)
|
||||
if (0 == file.View.ReadByte (index_offset))
|
||||
return null;
|
||||
string name = file.View.ReadString (index_offset, (uint)name_length);
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
index_offset += name_length;
|
||||
uint offset = next_offset;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
//! \file ArcPCK.cs
|
||||
//! \date Fri Feb 05 16:01:26 2016
|
||||
//! \brief Circus resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Circus
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PckOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PCK/CIRCUS"; } }
|
||||
public override string Description { get { return "Circus resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public PckOpener ()
|
||||
{
|
||||
Extensions = new string[] { "pck" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
int index_size = count * 0x48 + 4;
|
||||
uint first_offset = file.View.ReadUInt32 (4);
|
||||
if (first_offset < index_size || first_offset >= file.MaxOffset)
|
||||
return null;
|
||||
int index_offset = 4 + count * 8;
|
||||
file.View.Reserve (index_offset, (uint)count * 0x40);
|
||||
if (first_offset != file.View.ReadUInt32 (index_offset+0x38))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0x38);
|
||||
if (0 == name.Length)
|
||||
return null;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x38);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x3C);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x40;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Jun 15 16:30:24 2015
|
||||
//! \brief Circus PCM audio format.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
// Copyright (C) 2015 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
|
||||
@@ -102,60 +102,50 @@ namespace GameRes.Formats.Circus
|
||||
{
|
||||
this.Source = new StreamRegion (file, file.Position, src_size);
|
||||
}
|
||||
else if (1 == mode || 3 == mode)
|
||||
else if (1 == mode)
|
||||
{
|
||||
var decoder = new PcmDecoder (input, src_size, extra, (XpcmCompression)mode);
|
||||
var decoder = new PcmDecoder (input, src_size, extra);
|
||||
this.Source = new MemoryStream (decoder.Unpack(), 0, src_size);
|
||||
file.Dispose();
|
||||
}
|
||||
else if (3 == mode)
|
||||
{
|
||||
uint packed_size = input.ReadUInt32();
|
||||
this.Source = ZLibCompressor.DeCompress (file);
|
||||
file.Dispose();
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException ("Not supported Circus PCM audio compression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum XpcmCompression
|
||||
{
|
||||
None = 0,
|
||||
Lzss = 1,
|
||||
Zlib = 3,
|
||||
}
|
||||
|
||||
internal class PcmDecoder
|
||||
{
|
||||
byte[] m_pcm_data;
|
||||
byte[] m_encoded;
|
||||
int m_pcm_size;
|
||||
int m_packed_size;
|
||||
int m_extra;
|
||||
|
||||
public byte[] Data { get { return m_pcm_data; } }
|
||||
|
||||
public PcmDecoder (BinaryReader input, int pcm_size, int extra, XpcmCompression mode)
|
||||
public PcmDecoder (BinaryReader input, int pcm_size, int extra)
|
||||
{
|
||||
if (extra < 0 || extra > 3)
|
||||
throw new InvalidFormatException();
|
||||
int packed_size = input.ReadInt32();
|
||||
m_packed_size = input.ReadInt32();
|
||||
m_extra = extra;
|
||||
m_pcm_size = pcm_size;
|
||||
m_pcm_data = new byte[pcm_size + 8192];
|
||||
m_encoded = new byte[(pcm_size / 0xFE0 << 12) + 16386];
|
||||
if (XpcmCompression.Lzss == mode)
|
||||
{
|
||||
if (packed_size != input.Read (m_pcm_data, 0, packed_size))
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
UnpackV1 (m_pcm_data, packed_size, m_encoded);
|
||||
}
|
||||
else if (XpcmCompression.Zlib == mode)
|
||||
{
|
||||
using (var z = new ZLibStream (input.BaseStream, CompressionMode.Decompress, true))
|
||||
z.Read (m_encoded, 0, m_encoded.Length);
|
||||
}
|
||||
else
|
||||
throw new InvalidFormatException ("Unknown PCM compression mode");
|
||||
if (m_packed_size != input.Read (m_pcm_data, 0, m_packed_size))
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
UnpackV1 (m_pcm_data, m_packed_size, m_encoded);
|
||||
Buffer.BlockCopy (unk_43A254, m_extra*0x40, dword_43A214, 0, 0x40);
|
||||
DecodeV1 (m_pcm_data, m_encoded, m_pcm_size);
|
||||
return m_pcm_data;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Jun 15 15:14:59 2015
|
||||
//! \brief Circus image format.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
// Copyright (C) 2015 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
|
||||
@@ -29,16 +29,14 @@ using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Circus
|
||||
{
|
||||
internal class CrxMetaData : ImageMetaData
|
||||
{
|
||||
public int Compression;
|
||||
public int Colors;
|
||||
public int Mode;
|
||||
public int Colors;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@@ -53,29 +51,31 @@ namespace GameRes.Formats.Circus
|
||||
var header = new byte[0x14];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
int depth = LittleEndian.ToInt16 (header, 0x10);
|
||||
int type = LittleEndian.ToInt32 (header, 0x10);
|
||||
var info = new CrxMetaData
|
||||
{
|
||||
Width = LittleEndian.ToUInt16 (header, 8),
|
||||
Height = LittleEndian.ToUInt16 (header, 10),
|
||||
OffsetX = LittleEndian.ToInt16 (header, 4),
|
||||
OffsetY = LittleEndian.ToInt16 (header, 6),
|
||||
BPP = 0 == depth ? 24 : 1 == depth ? 32 : 8,
|
||||
Compression = LittleEndian.ToUInt16 (header, 0xC),
|
||||
Colors = depth,
|
||||
Mode = LittleEndian.ToUInt16 (header, 0x12),
|
||||
BPP = 0 == type ? 24 : 1 == type ? 32 : 8,
|
||||
Mode = LittleEndian.ToUInt16 (header, 12),
|
||||
Colors = type,
|
||||
};
|
||||
if (info.Compression != 1 && info.Compression != 2)
|
||||
if (info.Mode != 1 && info.Mode != 2)
|
||||
return null;
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
using (var reader = new Reader (stream, (CrxMetaData)info))
|
||||
var meta = info as CrxMetaData;
|
||||
if (null == meta)
|
||||
throw new ArgumentException ("CrxFormat.Read should be supplied with CrxMetaData", "info");
|
||||
|
||||
stream.Position = 0x14;
|
||||
using (var reader = new Reader (stream, meta))
|
||||
{
|
||||
reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,26 +86,23 @@ namespace GameRes.Formats.Circus
|
||||
|
||||
internal sealed class Reader : IDisposable
|
||||
{
|
||||
Stream m_input;
|
||||
BinaryReader m_input;
|
||||
byte[] m_output;
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_stride;
|
||||
int m_bpp;
|
||||
int m_compression;
|
||||
int m_mode;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public PixelFormat Format { get; private set; }
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
public int Stride { get { return m_stride; } }
|
||||
|
||||
public Reader (Stream input, CrxMetaData info)
|
||||
{
|
||||
m_width = (int)info.Width;
|
||||
m_height = (int)info.Height;
|
||||
m_bpp = info.BPP;
|
||||
m_compression = info.Compression;
|
||||
m_mode = info.Mode;
|
||||
switch (m_bpp)
|
||||
{
|
||||
@@ -116,8 +113,7 @@ namespace GameRes.Formats.Circus
|
||||
}
|
||||
m_stride = (m_width * m_bpp / 8 + 3) & ~3;
|
||||
m_output = new byte[m_height*m_stride];
|
||||
m_input = input;
|
||||
m_input.Position = 0x14;
|
||||
m_input = new ArcView.Reader (input);
|
||||
if (8 == m_bpp)
|
||||
ReadPalette (info.Colors);
|
||||
}
|
||||
@@ -143,14 +139,13 @@ namespace GameRes.Formats.Circus
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
if (1 == m_compression)
|
||||
if (1 == m_mode)
|
||||
UnpackV1();
|
||||
else
|
||||
UnpackV2();
|
||||
|
||||
if (32 == m_bpp && m_mode != 1)
|
||||
if (32 == m_bpp)
|
||||
{
|
||||
int alpha_flip = 2 == m_mode ? 0 : 0xFF;
|
||||
int line = 0;
|
||||
for (int h = 0; h < m_height; h++)
|
||||
{
|
||||
@@ -159,12 +154,12 @@ namespace GameRes.Formats.Circus
|
||||
for (int w = 0; w < m_width; w++)
|
||||
{
|
||||
int pixel = line + w * 4;
|
||||
int alpha = m_output[pixel];
|
||||
byte alpha = m_output[pixel];
|
||||
int b = m_output[pixel+1];
|
||||
int g = m_output[pixel+2];
|
||||
int r = m_output[pixel+3];
|
||||
|
||||
if (alpha != alpha_flip)
|
||||
if (alpha != 0xff)
|
||||
{
|
||||
b += (w & 1) + shift;
|
||||
if (b < 0)
|
||||
@@ -187,7 +182,7 @@ namespace GameRes.Formats.Circus
|
||||
m_output[pixel] = (byte)b;
|
||||
m_output[pixel+1] = (byte)g;
|
||||
m_output[pixel+2] = (byte)r;
|
||||
m_output[pixel+3] = (byte)(alpha ^ alpha_flip);
|
||||
m_output[pixel+3] = alpha;
|
||||
shift = -shift;
|
||||
}
|
||||
line += m_stride;
|
||||
@@ -239,61 +234,58 @@ namespace GameRes.Formats.Circus
|
||||
|
||||
private void UnpackV1 ()
|
||||
{
|
||||
using (var src = new ArcView.Reader (m_input))
|
||||
byte[] window = new byte[0x10000];
|
||||
int flag = 0;
|
||||
int win_pos = 0;
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
byte[] window = new byte[0x10000];
|
||||
int flag = 0;
|
||||
int win_pos = 0;
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
flag >>= 1;
|
||||
if (0 == (flag & 0x100))
|
||||
flag = src.ReadByte() | 0xff00;
|
||||
flag >>= 1;
|
||||
if (0 == (flag & 0x100))
|
||||
flag = m_input.ReadByte() | 0xff00;
|
||||
|
||||
if (0 != (flag & 1))
|
||||
if (0 != (flag & 1))
|
||||
{
|
||||
byte dat = m_input.ReadByte();
|
||||
window[win_pos++] = dat;
|
||||
win_pos &= 0xffff;
|
||||
m_output[dst++] = dat;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte control = m_input.ReadByte();
|
||||
int count, offset;
|
||||
|
||||
if (control >= 0xc0)
|
||||
{
|
||||
byte dat = src.ReadByte();
|
||||
window[win_pos++] = dat;
|
||||
win_pos &= 0xffff;
|
||||
m_output[dst++] = dat;
|
||||
offset = ((control & 3) << 8) | m_input.ReadByte();
|
||||
count = 4 + ((control >> 2) & 0xf);
|
||||
}
|
||||
else if (0 != (control & 0x80))
|
||||
{
|
||||
offset = control & 0x1f;
|
||||
count = 2 + ((control >> 5) & 3);
|
||||
if (0 == offset)
|
||||
offset = m_input.ReadByte();
|
||||
}
|
||||
else if (0x7f == control)
|
||||
{
|
||||
count = 2 + m_input.ReadUInt16();
|
||||
offset = m_input.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
byte control = src.ReadByte();
|
||||
int count, offset;
|
||||
|
||||
if (control >= 0xc0)
|
||||
{
|
||||
offset = ((control & 3) << 8) | src.ReadByte();
|
||||
count = 4 + ((control >> 2) & 0xf);
|
||||
}
|
||||
else if (0 != (control & 0x80))
|
||||
{
|
||||
offset = control & 0x1f;
|
||||
count = 2 + ((control >> 5) & 3);
|
||||
if (0 == offset)
|
||||
offset = src.ReadByte();
|
||||
}
|
||||
else if (0x7f == control)
|
||||
{
|
||||
count = 2 + src.ReadUInt16();
|
||||
offset = src.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = src.ReadUInt16();
|
||||
count = control + 4;
|
||||
}
|
||||
offset = win_pos - offset;
|
||||
for (int k = 0; k < count && dst < m_output.Length; k++)
|
||||
{
|
||||
offset &= 0xffff;
|
||||
byte dat = window[offset++];
|
||||
window[win_pos++] = dat;
|
||||
win_pos &= 0xffff;
|
||||
m_output[dst++] = dat;
|
||||
}
|
||||
offset = m_input.ReadUInt16();
|
||||
count = control + 4;
|
||||
}
|
||||
offset = win_pos - offset;
|
||||
for (int k = 0; k < count && dst < m_output.Length; k++)
|
||||
{
|
||||
offset &= 0xffff;
|
||||
byte dat = window[offset++];
|
||||
window[win_pos++] = dat;
|
||||
win_pos &= 0xffff;
|
||||
m_output[dst++] = dat;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,84 +293,7 @@ namespace GameRes.Formats.Circus
|
||||
|
||||
private void UnpackV2 ()
|
||||
{
|
||||
int pixel_size = m_bpp / 8;
|
||||
int src_stride = m_width * pixel_size;
|
||||
using (var zlib = new ZLibStream (m_input, CompressionMode.Decompress, true))
|
||||
using (var src = new BinaryReader (zlib))
|
||||
{
|
||||
if (m_bpp >= 24)
|
||||
{
|
||||
for (int y = 0; y < m_height; ++y)
|
||||
{
|
||||
byte ctl = src.ReadByte();
|
||||
int dst = y * m_stride;
|
||||
int prev_row = dst - m_stride;
|
||||
switch (ctl)
|
||||
{
|
||||
case 0:
|
||||
src.Read (m_output, dst, pixel_size);
|
||||
for (int x = pixel_size; x < src_stride; ++x)
|
||||
m_output[dst+x] = (byte)(src.ReadByte() + m_output[dst+x - pixel_size]);
|
||||
break;
|
||||
case 1:
|
||||
for (int x = 0; x < src_stride; ++x)
|
||||
m_output[dst+x] = (byte)(src.ReadByte() + m_output[prev_row+x]);
|
||||
break;
|
||||
case 2:
|
||||
src.Read (m_output, dst, pixel_size);
|
||||
for (int x = pixel_size; x < src_stride; ++x)
|
||||
m_output[dst+x] = (byte)(src.ReadByte() + m_output[prev_row+x - pixel_size]);
|
||||
break;
|
||||
case 3:
|
||||
for (int x = src_stride - pixel_size; x > 0; --x)
|
||||
m_output[dst++] = (byte)(src.ReadByte() + m_output[prev_row++ + pixel_size]);
|
||||
src.Read (m_output, dst, pixel_size);
|
||||
break;
|
||||
case 4:
|
||||
for (int i = 0; i < pixel_size; ++i)
|
||||
{
|
||||
int w = m_width;
|
||||
byte val = src.ReadByte();
|
||||
while (w > 0)
|
||||
{
|
||||
m_output[dst] = val;
|
||||
dst += pixel_size;
|
||||
if (0 == --w)
|
||||
break;
|
||||
byte next = src.ReadByte();
|
||||
if (val == next)
|
||||
{
|
||||
int count = src.ReadByte();
|
||||
for (int j = 0; j < count; ++j)
|
||||
{
|
||||
m_output[dst] = val;
|
||||
dst += pixel_size;
|
||||
}
|
||||
w -= count;
|
||||
if (w > 0)
|
||||
val = src.ReadByte();
|
||||
}
|
||||
else
|
||||
val = next;
|
||||
}
|
||||
dst -= src_stride - 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_height; ++y)
|
||||
{
|
||||
src.Read (m_output, dst, src_stride);
|
||||
dst += m_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException ("CRX v2 not implemented");
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
@@ -388,6 +303,7 @@ namespace GameRes.Formats.Circus
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
//! \file ArcPCS.cs
|
||||
//! \date Mon Jan 25 01:36:53 2016
|
||||
//! \brief C's ware resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.CsWare
|
||||
{
|
||||
internal class PcsEntry : Entry
|
||||
{
|
||||
public byte Key;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PcsOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PCS"; } }
|
||||
public override string Description { get { return "C's ware resource archive"; } }
|
||||
public override uint Signature { get { return 0x53434350; } } // 'PCCS'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int version = file.View.ReadUInt16 (4);
|
||||
if (version < 1 || version > 4)
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint data_offset = file.View.ReadUInt32 (12);
|
||||
int index_size = (int)data_offset - 0x10;
|
||||
|
||||
int index_offset = 0x10;
|
||||
var index_buffer = new byte[0x40];
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int name_length;
|
||||
if (version > 1)
|
||||
{
|
||||
name_length = file.View.ReadInt32 (index_offset);
|
||||
if (0 == name_length)
|
||||
break;
|
||||
if (name_length > index_size)
|
||||
return null;
|
||||
index_offset += 5 + name_length;
|
||||
}
|
||||
name_length = file.View.ReadInt32 (index_offset);
|
||||
if (0 == name_length)
|
||||
break;
|
||||
if (name_length > index_size)
|
||||
return null;
|
||||
if (name_length > index_buffer.Length)
|
||||
index_buffer = new byte[name_length];
|
||||
file.View.Read (index_offset+5, index_buffer, 0, (uint)name_length);
|
||||
index_offset += 5 + name_length;
|
||||
byte checksum;
|
||||
var name = DecryptName (index_buffer, name_length, out checksum);
|
||||
Entry entry;
|
||||
if (4 == version)
|
||||
{
|
||||
entry = FormatCatalog.Instance.Create<PcsEntry> (name);
|
||||
(entry as PcsEntry).Key = checksum;
|
||||
int c = -1 - checksum;
|
||||
file.View.Read (index_offset, index_buffer, 0, 8);
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
byte key = (byte)((checksum + (17 << j)) & 0x33);
|
||||
index_buffer[j] = (byte)(c + key - index_buffer[j]);
|
||||
index_buffer[j+4] = (byte)(c + key - index_buffer[j+4]);
|
||||
}
|
||||
entry.Offset = LittleEndian.ToUInt32 (index_buffer, 0);
|
||||
entry.Size = LittleEndian.ToUInt32 (index_buffer, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+4);
|
||||
}
|
||||
index_offset += 0x10;
|
||||
if (index_offset > data_offset)
|
||||
return null;
|
||||
entry.Offset += data_offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PcsEntry;
|
||||
if (null == pent)
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint header_size = Math.Min (entry.Size, 512u);
|
||||
var header = arc.File.View.ReadBytes (entry.Offset, header_size);
|
||||
for (int i = 0; i < header.Length; ++i)
|
||||
{
|
||||
header[i] = (byte)(pent.Key - header[i] - 1);
|
||||
}
|
||||
if (header_size == entry.Size)
|
||||
return new MemoryStream (header);
|
||||
var rest = arc.File.CreateStream (entry.Offset+512, entry.Size-512);
|
||||
return new PrefixStream (header, rest);
|
||||
}
|
||||
|
||||
string DecryptName (byte[] name_buffer, int length, out byte checksum)
|
||||
{
|
||||
int count;
|
||||
checksum = 0;
|
||||
for (count = 0; count < length; ++count)
|
||||
{
|
||||
if (0 == name_buffer[count])
|
||||
break;
|
||||
name_buffer[count] = Binary.RotByteL (name_buffer[count], 4);
|
||||
checksum += name_buffer[count];
|
||||
}
|
||||
return Encodings.cp932.GetString (name_buffer, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,61 +123,56 @@ namespace GameRes.Formats.Entis
|
||||
ulong size = arc.File.View.ReadUInt64 (entry.Offset+8);
|
||||
if (size > int.MaxValue)
|
||||
throw new FileSizeException();
|
||||
if (size <= 4)
|
||||
if (0 == size)
|
||||
return Stream.Null;
|
||||
|
||||
if (EncType.ERISACode == nent.Encryption)
|
||||
{
|
||||
using (var enc = arc.File.CreateStream (entry.Offset+0x10, (uint)size-4))
|
||||
return DecodeNemesis (enc);
|
||||
}
|
||||
|
||||
var narc = arc as NoaArchive;
|
||||
var input = arc.File.CreateStream (entry.Offset+0x10, (uint)size);
|
||||
if (EncType.Raw == nent.Encryption || null == narc || null == narc.Password)
|
||||
return input;
|
||||
|
||||
if (EncType.BSHFCrypt == nent.Encryption)
|
||||
try
|
||||
{
|
||||
using (input)
|
||||
return DecodeBSHF (input, narc.Password);
|
||||
}
|
||||
Trace.WriteLine (string.Format ("{0}: encryption scheme 0x{1:x8} not implemented",
|
||||
nent.Name, nent.Encryption));
|
||||
return input;
|
||||
}
|
||||
var narc = arc as NoaArchive;
|
||||
if (0 == nent.Encryption || size < 4 || null == narc || null == narc.Password)
|
||||
return input;
|
||||
if (0x40000000 != nent.Encryption)
|
||||
{
|
||||
Trace.WriteLine (string.Format ("{0}: unknown encryption scheme 0x{1:x8}",
|
||||
nent.Name, nent.Encryption));
|
||||
return input;
|
||||
}
|
||||
uint nTotalBytes = (uint)(size - 4);
|
||||
var pBSHF = new BSHFDecodeContext (0x10000);
|
||||
pBSHF.AttachInputFile (input);
|
||||
pBSHF.PrepareToDecodeBSHFCode (narc.Password);
|
||||
|
||||
Stream DecodeNemesis (Stream input)
|
||||
{
|
||||
var decoder = new NemesisDecodeContext();
|
||||
decoder.AttachInputFile (input);
|
||||
decoder.PrepareToDecodeERISANCode();
|
||||
var file = new MemoryStream ((int)input.Length);
|
||||
var buffer = new byte[0x10000];
|
||||
for (;;)
|
||||
byte[] buf = new byte[nTotalBytes];
|
||||
uint decoded = pBSHF.DecodeBSHFCodeBytes (buf, nTotalBytes);
|
||||
if (decoded < nTotalBytes)
|
||||
throw new EndOfStreamException ("Unexpected end of encrypted stream");
|
||||
|
||||
/* Something wrong with preceding length calculation, resulting CRC doesn't match
|
||||
byte[] bufCRC = new byte[4];
|
||||
int iCRC = 0;
|
||||
for (int i = 0; i < buf.Length; ++i)
|
||||
{
|
||||
bufCRC[iCRC] ^= buf[i];
|
||||
iCRC = (iCRC + 1) & 0x03;
|
||||
}
|
||||
uint orgCRC = arc.File.View.ReadUInt32 (entry.Offset+0x10+nTotalBytes);
|
||||
uint crc = LittleEndian.ToUInt32 (bufCRC, 0);
|
||||
if (orgCRC != crc)
|
||||
{
|
||||
Trace.WriteLine (string.Format ("{0}: CRC mismatch", nent.Name));
|
||||
input.Position = 0;
|
||||
return input;
|
||||
}
|
||||
*/
|
||||
input.Dispose();
|
||||
return new MemoryStream (buf);
|
||||
}
|
||||
catch
|
||||
{
|
||||
int read = (int)decoder.DecodeNemesisCodeBytes (buffer, 0x10000);
|
||||
if (0 == read)
|
||||
break;
|
||||
file.Write (buffer, 0, read);
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
file.Position = 0;
|
||||
return file;
|
||||
}
|
||||
|
||||
Stream DecodeBSHF (Stream input, string password)
|
||||
{
|
||||
uint nTotalBytes = (uint)input.Length - 4;
|
||||
var pBSHF = new BSHFDecodeContext (0x10000);
|
||||
pBSHF.AttachInputFile (input);
|
||||
pBSHF.PrepareToDecodeBSHFCode (password);
|
||||
|
||||
byte[] buf = new byte[nTotalBytes];
|
||||
uint decoded = pBSHF.DecodeBSHFCodeBytes (buf, nTotalBytes);
|
||||
if (decoded < nTotalBytes)
|
||||
throw new EndOfStreamException ("Unexpected end of encrypted stream");
|
||||
|
||||
return new MemoryStream (buf);
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions ()
|
||||
@@ -193,16 +188,6 @@ namespace GameRes.Formats.Entis
|
||||
return new GUI.WidgetNOA();
|
||||
}
|
||||
|
||||
internal static class EncType
|
||||
{
|
||||
public const uint Raw = 0x00000000;
|
||||
public const uint ERISACode = 0x80000010;
|
||||
public const uint BSHFCrypt = 0x40000000;
|
||||
public const uint SimpleCrypt32 = 0x20000000;
|
||||
public const uint ERISACrypt = 0xC0000010;
|
||||
public const uint ERISACrypt32 = 0xA0000010;
|
||||
}
|
||||
|
||||
internal class IndexReader
|
||||
{
|
||||
ArcView m_file;
|
||||
@@ -250,7 +235,8 @@ namespace GameRes.Formats.Entis
|
||||
dir_offset += 4;
|
||||
|
||||
entry.Encryption = m_file.View.ReadUInt32 (dir_offset);
|
||||
m_found_encrypted = m_found_encrypted || (EncType.Raw != entry.Encryption && EncType.ERISACode != entry.Encryption);
|
||||
if (0 != entry.Encryption)
|
||||
m_found_encrypted = true;
|
||||
dir_offset += 4;
|
||||
|
||||
entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset);
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace GameRes.Formats.Entis
|
||||
return null;
|
||||
if (0x03000100 != LittleEndian.ToUInt32 (header, 8))
|
||||
return null;
|
||||
if (!Binary.AsciiEqual (header, 0x10, "Music Interleaved"))
|
||||
if (!Binary.AsciiEqual (header, 0x10, "Music Interleaved and Orthogonal transformed"))
|
||||
return null;
|
||||
|
||||
return new MioInput (file);
|
||||
|
||||
@@ -1368,15 +1368,16 @@ namespace GameRes.Formats.Entis
|
||||
|
||||
internal class ProbDecodeContext : RLEDecodeContext
|
||||
{
|
||||
protected uint m_dwCodeRegister;
|
||||
protected uint m_dwAugendRegister;
|
||||
protected int m_nPostBitCount;
|
||||
uint m_dwCodeRegister;
|
||||
uint m_dwAugendRegister;
|
||||
int m_nPostBitCount;
|
||||
byte[] m_bytLastSymbol = new byte[4];
|
||||
|
||||
protected ErisaProbModel m_pPhraseLenProb = new ErisaProbModel();
|
||||
protected ErisaProbModel m_pPhraseIndexProb = new ErisaProbModel();
|
||||
protected ErisaProbModel m_pRunLenProb = new ErisaProbModel();
|
||||
protected ErisaProbModel m_pLastERISAProb;
|
||||
protected ErisaProbModel[] m_ppTableERISA;
|
||||
ErisaProbModel m_pPhraseLenProb = new ErisaProbModel();
|
||||
ErisaProbModel m_pPhraseIndexProb = new ErisaProbModel();
|
||||
ErisaProbModel m_pRunLenProb = new ErisaProbModel();
|
||||
ErisaProbModel m_pLastERISAProb;
|
||||
ErisaProbModel[] m_ppTableERISA;
|
||||
|
||||
public ProbDecodeContext (uint nBufferingSize) : base (nBufferingSize)
|
||||
{
|
||||
@@ -1466,7 +1467,7 @@ namespace GameRes.Formats.Entis
|
||||
return nSymbol;
|
||||
}
|
||||
|
||||
protected int DecodeERISACodeIndex (ErisaProbModel pModel)
|
||||
int DecodeERISACodeIndex (ErisaProbModel pModel)
|
||||
{
|
||||
uint dwAcc = m_dwCodeRegister * pModel.TotalCount / m_dwAugendRegister;
|
||||
if (dwAcc >= ErisaProbModel.TotalLimit)
|
||||
@@ -1498,7 +1499,7 @@ namespace GameRes.Formats.Entis
|
||||
{
|
||||
if ((++m_nPostBitCount) >= 256)
|
||||
return -1;
|
||||
nNextBit = 0;
|
||||
nNextBit = 0 ;
|
||||
}
|
||||
m_dwCodeRegister = (m_dwCodeRegister << 1) | ((uint)nNextBit & 1);
|
||||
m_dwAugendRegister <<= 1;
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
//! \file ErisaNemesis.cs
|
||||
//! \date Tue Jan 19 18:24:23 2016
|
||||
//! \brief Erisa Nemesis encoding implementation.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GameRes.Formats.Entis
|
||||
{
|
||||
class NemesisDecodeContext : ProbDecodeContext
|
||||
{
|
||||
int m_iLastSymbol;
|
||||
int m_nNemesisLeft;
|
||||
int m_nNemesisNext;
|
||||
byte[] m_pNemesisBuf;
|
||||
int m_nNemesisIndex;
|
||||
bool m_flagEOF;
|
||||
|
||||
ErisaProbBase m_pProbERISA;
|
||||
NemesisPhraseLookup[] m_pNemesisLookup;
|
||||
byte[] m_bytLastSymbol = new byte[4];
|
||||
|
||||
public NemesisDecodeContext (uint buffer_size = 0x10000) : base (buffer_size)
|
||||
{
|
||||
m_flagEOF = false;
|
||||
}
|
||||
|
||||
public void PrepareToDecodeERISANCode ()
|
||||
{
|
||||
if (null == m_pProbERISA)
|
||||
m_pProbERISA = new ErisaProbBase();
|
||||
|
||||
m_iLastSymbol = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_bytLastSymbol[i] = 0;
|
||||
|
||||
m_pProbERISA.dwWorkUsed = 0;
|
||||
m_pProbERISA.epmBaseModel.Initialize();
|
||||
|
||||
for (int i = 0; i < ErisaProbBase.SlotMax; ++i)
|
||||
{
|
||||
m_pProbERISA.ptrProbIndex[i] = new ErisaProbModel();
|
||||
}
|
||||
PrepareToDecodeERISACode();
|
||||
|
||||
if (null == m_pNemesisBuf)
|
||||
{
|
||||
m_pNemesisBuf = new byte[Nemesis.BufSize];
|
||||
}
|
||||
if (null == m_pNemesisLookup)
|
||||
{
|
||||
m_pNemesisLookup = new NemesisPhraseLookup[0x100];
|
||||
}
|
||||
for (int i = 0; i < m_pNemesisBuf.Length; ++i)
|
||||
m_pNemesisBuf[i] = 0;
|
||||
for (int i = 0; i < m_pNemesisLookup.Length; ++i)
|
||||
m_pNemesisLookup[i] = new NemesisPhraseLookup();
|
||||
m_nNemesisIndex = 0;
|
||||
|
||||
m_nNemesisLeft = 0;
|
||||
m_flagEOF = false;
|
||||
}
|
||||
|
||||
public override uint DecodeBytes (Array ptrDst, uint nCount)
|
||||
{
|
||||
return DecodeNemesisCodeBytes (ptrDst as byte[], nCount);
|
||||
}
|
||||
|
||||
public uint DecodeNemesisCodeBytes (byte[] ptrDst, uint nCount)
|
||||
{
|
||||
if (m_flagEOF)
|
||||
return 0;
|
||||
|
||||
ErisaProbBase pBase = m_pProbERISA;
|
||||
uint nDecoded = 0;
|
||||
int dst = 0;
|
||||
byte bytSymbol;
|
||||
while (nDecoded < nCount)
|
||||
{
|
||||
if (m_nNemesisLeft > 0)
|
||||
{
|
||||
uint nNemesisCount = (uint)m_nNemesisLeft;
|
||||
if (nNemesisCount > nCount - nDecoded)
|
||||
{
|
||||
nNemesisCount = nCount - nDecoded;
|
||||
}
|
||||
byte bytLastSymbol = m_pNemesisBuf[(m_nNemesisIndex - 1) & Nemesis.BufMask];
|
||||
|
||||
for (uint i = 0; i < nNemesisCount; ++i)
|
||||
{
|
||||
bytSymbol = bytLastSymbol;
|
||||
if (m_nNemesisNext >= 0)
|
||||
{
|
||||
bytSymbol = m_pNemesisBuf[m_nNemesisNext++];
|
||||
m_nNemesisNext &= Nemesis.BufMask;
|
||||
}
|
||||
m_bytLastSymbol[m_iLastSymbol++] = bytSymbol;
|
||||
m_iLastSymbol &= 3;
|
||||
|
||||
var phrase = m_pNemesisLookup[bytSymbol];
|
||||
phrase.index[phrase.first] = (uint)m_nNemesisIndex;
|
||||
phrase.first = (phrase.first + 1) & Nemesis.IndexMask;
|
||||
bytLastSymbol = bytSymbol;
|
||||
|
||||
m_pNemesisBuf[m_nNemesisIndex++] = bytSymbol;
|
||||
m_nNemesisIndex &= Nemesis.BufMask;
|
||||
|
||||
ptrDst[dst++] = bytSymbol;
|
||||
}
|
||||
m_nNemesisLeft -= (int)nNemesisCount;
|
||||
nDecoded += nNemesisCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
int iDeg;
|
||||
ErisaProbModel pModel = pBase.epmBaseModel;
|
||||
for (iDeg = 0; iDeg < 4; ++iDeg)
|
||||
{
|
||||
int iLast = m_bytLastSymbol[(m_iLastSymbol + 3 - iDeg) & 3]
|
||||
>> ErisaProbBase.m_nShiftCount[iDeg];
|
||||
if (pModel.SubModel[iLast].Symbol < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Debug.Assert ((uint)pModel.SubModel[iLast].Symbol < pBase.dwWorkUsed);
|
||||
pModel = pBase.ptrProbIndex[pModel.SubModel[iLast].Symbol];
|
||||
}
|
||||
int iSym = DecodeERISACodeIndex (pModel);
|
||||
if (iSym < 0)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
int nSymbol = pModel.SymTable[iSym].Symbol;
|
||||
int iSymIndex = pModel.IncreaseSymbol (iSym);
|
||||
|
||||
bool fNemesis = false;
|
||||
if (nSymbol == ErisaProbModel.EscCode)
|
||||
{
|
||||
if (pModel != pBase.epmBaseModel)
|
||||
{
|
||||
iSym = DecodeERISACodeIndex (pBase.epmBaseModel);
|
||||
if (iSym < 0)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
nSymbol = pBase.epmBaseModel.SymTable[iSym].Symbol;
|
||||
pBase.epmBaseModel.IncreaseSymbol (iSym);
|
||||
if (nSymbol != ErisaProbModel.EscCode)
|
||||
{
|
||||
pModel.AddSymbol ((short)nSymbol);
|
||||
iSym = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fNemesis = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fNemesis = true;
|
||||
}
|
||||
}
|
||||
if (fNemesis)
|
||||
{
|
||||
int nLength, nPhraseIndex;
|
||||
nPhraseIndex = DecodeERISACode (m_pPhraseIndexProb);
|
||||
if (nPhraseIndex == ErisaProbModel.EscCode)
|
||||
{
|
||||
m_flagEOF = true;
|
||||
return nDecoded;
|
||||
}
|
||||
if (0 == nPhraseIndex)
|
||||
{
|
||||
nLength = DecodeERISACode (m_pRunLenProb);
|
||||
}
|
||||
else
|
||||
{
|
||||
nLength = DecodeERISACode (m_pPhraseLenProb);
|
||||
}
|
||||
if (nLength == ErisaProbModel.EscCode)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
byte bytLastSymbol = m_pNemesisBuf[(m_nNemesisIndex - 1) & Nemesis.BufMask];
|
||||
var phrase = m_pNemesisLookup[bytLastSymbol];
|
||||
m_nNemesisLeft = nLength;
|
||||
if (0 == nPhraseIndex)
|
||||
{
|
||||
m_nNemesisNext = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nNemesisNext = (int)phrase.index[(phrase.first - nPhraseIndex) & Nemesis.IndexMask];
|
||||
Debug.Assert (m_pNemesisBuf[m_nNemesisNext] == bytLastSymbol);
|
||||
m_nNemesisNext = (m_nNemesisNext + 1) & Nemesis.BufMask;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bytSymbol = (byte)nSymbol;
|
||||
m_bytLastSymbol[m_iLastSymbol++] = bytSymbol;
|
||||
m_iLastSymbol &= 3;
|
||||
|
||||
var ppl = m_pNemesisLookup[bytSymbol];
|
||||
ppl.index[ppl.first] = (uint)m_nNemesisIndex;
|
||||
ppl.first = (ppl.first + 1) & Nemesis.IndexMask;
|
||||
m_pNemesisBuf[m_nNemesisIndex++] = bytSymbol;
|
||||
m_nNemesisIndex &= Nemesis.BufMask;
|
||||
|
||||
ptrDst[dst++] = bytSymbol;
|
||||
nDecoded++;
|
||||
|
||||
if ((pBase.dwWorkUsed < ErisaProbBase.SlotMax) && (iDeg < 4))
|
||||
{
|
||||
int iSymbol = ((byte)nSymbol) >> ErisaProbBase.m_nShiftCount[iDeg];
|
||||
Debug.Assert (iSymbol < ErisaProbModel.SubSortMax);
|
||||
if (++pModel.SubModel[iSymbol].Occured >= ErisaProbBase.m_nNewProbLimit[iDeg])
|
||||
{
|
||||
int i;
|
||||
ErisaProbModel pParent = pModel;
|
||||
pModel = pBase.epmBaseModel;
|
||||
for (i = 0; i <= iDeg; ++i)
|
||||
{
|
||||
iSymbol = m_bytLastSymbol[(m_iLastSymbol + 3 - i) & 3]
|
||||
>> ErisaProbBase.m_nShiftCount[i];
|
||||
if (pModel.SubModel[iSymbol].Symbol < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Debug.Assert ((uint)pModel.SubModel[iSymbol].Symbol < pBase.dwWorkUsed);
|
||||
pModel = pBase.ptrProbIndex[pModel.SubModel[iSymbol].Symbol];
|
||||
}
|
||||
if ((i <= iDeg) && (pModel.SubModel[iSymbol].Symbol < 0))
|
||||
{
|
||||
ErisaProbModel pNew = pBase.ptrProbIndex[pBase.dwWorkUsed];
|
||||
pModel.SubModel[iSymbol].Symbol = (short)(pBase.dwWorkUsed++);
|
||||
|
||||
pNew.TotalCount = 0;
|
||||
int j = 0;
|
||||
for (i = 0; i < (int)pParent.SymbolSorts; ++i)
|
||||
{
|
||||
ushort wOccured = (ushort)(pParent.SymTable[i].Occured >> 4);
|
||||
if (wOccured > 0 && (pParent.SymTable[i].Symbol != ErisaProbModel.EscCode))
|
||||
{
|
||||
pNew.TotalCount += wOccured;
|
||||
pNew.SymTable[j].Occured = wOccured;
|
||||
pNew.SymTable[j].Symbol = pParent.SymTable[i].Symbol;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
pNew.TotalCount++;
|
||||
pNew.SymTable[j].Occured = 1;
|
||||
pNew.SymTable[j].Symbol = ErisaProbModel.EscCode;
|
||||
pNew.SymbolSorts = ++j;
|
||||
|
||||
for (i = 0; i < ErisaProbModel.SubSortMax; ++i)
|
||||
{
|
||||
pNew.SubModel[i].Occured = 0;
|
||||
pNew.SubModel[i].Symbol = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nDecoded;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ErisaProbBase
|
||||
{
|
||||
public uint dwWorkUsed;
|
||||
public ErisaProbModel epmBaseModel = new ErisaProbModel();
|
||||
public ErisaProbModel[] ptrProbIndex = new ErisaProbModel[SlotMax];
|
||||
|
||||
public const int SlotMax = 0x800;
|
||||
|
||||
public static readonly int[] m_nShiftCount = { 1, 3, 4, 5 };
|
||||
public static readonly int[] m_nNewProbLimit = { 0x01, 0x08, 0x10, 0x20 };
|
||||
}
|
||||
|
||||
internal static class Nemesis
|
||||
{
|
||||
public const int BufSize = 0x10000;
|
||||
public const int BufMask = 0xFFFF;
|
||||
public const int IndexLimit = 0x100;
|
||||
public const int IndexMask = 0xFF;
|
||||
}
|
||||
|
||||
internal class NemesisPhraseLookup
|
||||
{
|
||||
public uint first;
|
||||
public uint[] index = new uint[Nemesis.IndexLimit];
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,8 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Utility;
|
||||
|
||||
@@ -50,8 +47,6 @@ namespace GameRes.Formats.Entis
|
||||
public int LappedBlock;
|
||||
public int FrameTransform;
|
||||
public int FrameDegree;
|
||||
public EriFileHeader Header;
|
||||
public string Description;
|
||||
}
|
||||
|
||||
public enum CvType
|
||||
@@ -62,18 +57,8 @@ namespace GameRes.Formats.Entis
|
||||
LOT_ERI_MSS = 0x00000105,
|
||||
}
|
||||
|
||||
internal class EriFileHeader
|
||||
{
|
||||
public int Version;
|
||||
public int ContainedFlag;
|
||||
public int KeyFrameCount;
|
||||
public int FrameCount;
|
||||
public int AllFrameTime;
|
||||
}
|
||||
|
||||
public enum EriCode
|
||||
{
|
||||
ArithmeticCode = 32,
|
||||
RunlengthGamma = -1,
|
||||
RunlengthHuffman = -4,
|
||||
Nemesis = -16,
|
||||
@@ -82,13 +67,9 @@ namespace GameRes.Formats.Entis
|
||||
public enum EriImage
|
||||
{
|
||||
RGB = 0x00000001,
|
||||
Gray = 0x00000002,
|
||||
BGR = 0x00000003,
|
||||
YUV = 0x00000004,
|
||||
HSB = 0x00000006,
|
||||
RGBA = 0x04000001,
|
||||
BGRA = 0x04000003,
|
||||
TypeMask = 0x0000FFFF,
|
||||
Gray = 0x00000002,
|
||||
TypeMask = 0x00FFFFFF,
|
||||
WithPalette = 0x01000000,
|
||||
UseClipping = 0x02000000,
|
||||
WithAlpha = 0x04000000,
|
||||
@@ -103,7 +84,7 @@ namespace GameRes.Formats.Entis
|
||||
public long Length;
|
||||
}
|
||||
|
||||
public EriFile (Stream stream) : base (stream, Encoding.Unicode, true)
|
||||
public EriFile (Stream stream) : base (stream, System.Text.Encoding.ASCII, true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -157,26 +138,14 @@ namespace GameRes.Formats.Entis
|
||||
return null;
|
||||
int header_size = (int)section.Length;
|
||||
int stream_pos = 0x50 + header_size;
|
||||
EriFileHeader file_header = null;
|
||||
EriMetaData info = null;
|
||||
string desc = null;
|
||||
while (header_size > 0x10)
|
||||
while (header_size > 8)
|
||||
{
|
||||
section = reader.ReadSection();
|
||||
header_size -= 0x10;
|
||||
header_size -= 8;
|
||||
if (section.Length <= 0 || section.Length > header_size)
|
||||
break;
|
||||
if ("FileHdr " == section.Id)
|
||||
{
|
||||
file_header = new EriFileHeader { Version = reader.ReadInt32() };
|
||||
if (file_header.Version > 0x00020100)
|
||||
throw new InvalidFormatException ("Invalid ERI file version");
|
||||
file_header.ContainedFlag = reader.ReadInt32();
|
||||
file_header.KeyFrameCount = reader.ReadInt32();
|
||||
file_header.FrameCount = reader.ReadInt32();
|
||||
file_header.AllFrameTime = reader.ReadInt32();
|
||||
}
|
||||
else if ("ImageInf" == section.Id)
|
||||
if ("ImageInf" == section.Id)
|
||||
{
|
||||
int version = reader.ReadInt32();
|
||||
if (version != 0x00020100 && version != 0x00020200)
|
||||
@@ -199,33 +168,10 @@ namespace GameRes.Formats.Entis
|
||||
info.LappedBlock = reader.ReadInt32();
|
||||
info.FrameTransform = reader.ReadInt32();
|
||||
info.FrameDegree = reader.ReadInt32();
|
||||
}
|
||||
else if ("descript" == section.Id)
|
||||
{
|
||||
if (0xFEFF == reader.PeekChar())
|
||||
{
|
||||
reader.Read();
|
||||
var desc_chars = reader.ReadChars ((int)section.Length/2 - 1);
|
||||
desc = new string (desc_chars);
|
||||
}
|
||||
else
|
||||
{
|
||||
var desc_chars = reader.ReadBytes ((int)section.Length);
|
||||
desc = Encoding.UTF8.GetString (desc_chars);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
header_size -= (int)section.Length;
|
||||
}
|
||||
if (info != null)
|
||||
{
|
||||
if (file_header != null)
|
||||
info.Header = file_header;
|
||||
if (desc != null)
|
||||
info.Description = desc;
|
||||
reader.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -233,11 +179,34 @@ namespace GameRes.Formats.Entis
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var reader = ReadImageData (stream, (EriMetaData)info);
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
|
||||
var meta = info as EriMetaData;
|
||||
if (null == meta)
|
||||
throw new ArgumentException ("EriFormat.Read should be supplied with EriMetaData", "info");
|
||||
stream.Position = meta.StreamPos;
|
||||
using (var input = new EriFile (stream))
|
||||
{
|
||||
Color[] palette = null;
|
||||
for (;;) // ReadSection throws an exception in case of EOF
|
||||
{
|
||||
var section = input.ReadSection();
|
||||
if ("Stream " == section.Id)
|
||||
continue;
|
||||
if ("ImageFrm" == section.Id)
|
||||
break;
|
||||
if ("Palette " == section.Id && info.BPP <= 8 && section.Length <= 0x400)
|
||||
{
|
||||
palette = ReadPalette (stream, (int)section.Length);
|
||||
continue;
|
||||
}
|
||||
input.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
||||
}
|
||||
var reader = new EriReader (stream, meta, palette);
|
||||
reader.DecodeImage();
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
|
||||
}
|
||||
}
|
||||
|
||||
internal Color[] ReadPalette (Stream input, int palette_length)
|
||||
private Color[] ReadPalette (Stream input, int palette_length)
|
||||
{
|
||||
var palette_data = new byte[0x400];
|
||||
if (palette_length > palette_data.Length)
|
||||
@@ -252,122 +221,6 @@ namespace GameRes.Formats.Entis
|
||||
return colors;
|
||||
}
|
||||
|
||||
internal EriReader ReadImageData (Stream stream, EriMetaData meta)
|
||||
{
|
||||
stream.Position = meta.StreamPos;
|
||||
Color[] palette = null;
|
||||
using (var input = new EriFile (stream))
|
||||
{
|
||||
for (;;) // ReadSection throws an exception in case of EOF
|
||||
{
|
||||
var section = input.ReadSection();
|
||||
if ("Stream " == section.Id)
|
||||
continue;
|
||||
if ("ImageFrm" == section.Id)
|
||||
break;
|
||||
if ("Palette " == section.Id && meta.BPP <= 8 && section.Length <= 0x400)
|
||||
{
|
||||
palette = ReadPalette (stream, (int)section.Length);
|
||||
continue;
|
||||
}
|
||||
input.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
||||
}
|
||||
}
|
||||
var reader = new EriReader (stream, meta, palette);
|
||||
reader.DecodeImage();
|
||||
|
||||
if (!string.IsNullOrEmpty (meta.Description))
|
||||
{
|
||||
var tags = ParseTagInfo (meta.Description);
|
||||
string ref_file;
|
||||
if (tags.TryGetValue ("reference-file", out ref_file))
|
||||
{
|
||||
ref_file = ref_file.TrimEnd (null);
|
||||
if (!string.IsNullOrEmpty (ref_file))
|
||||
{
|
||||
if ((meta.BPP + 7) / 8 < 3)
|
||||
throw new InvalidFormatException();
|
||||
|
||||
ref_file = VFS.CombinePath (Path.GetDirectoryName (meta.FileName), ref_file);
|
||||
using (var ref_src = VFS.OpenSeekableStream (ref_file))
|
||||
{
|
||||
var ref_info = ReadMetaData (ref_src) as EriMetaData;
|
||||
if (null == ref_info)
|
||||
throw new FileNotFoundException ("Referenced image not found");
|
||||
ref_info.FileName = ref_file;
|
||||
var ref_reader = ReadImageData (ref_src, ref_info);
|
||||
AddImageBuffer (meta, reader.Data, ref_info, ref_reader.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
if (string.IsNullOrEmpty (desc))
|
||||
{
|
||||
return dict;
|
||||
}
|
||||
if ('#' != desc[0])
|
||||
{
|
||||
dict["comment"] = desc;
|
||||
return dict;
|
||||
}
|
||||
var tag_value = new StringBuilder();
|
||||
using (var reader = new StringReader (desc))
|
||||
{
|
||||
string line = reader.ReadLine();
|
||||
while (null != line)
|
||||
{
|
||||
var match = s_TagRe.Match (line);
|
||||
if (!match.Success)
|
||||
break;
|
||||
string tag = match.Groups[1].Value;
|
||||
|
||||
tag_value.Clear();
|
||||
for (;;)
|
||||
{
|
||||
line = reader.ReadLine();
|
||||
if (null == line)
|
||||
break;
|
||||
if (line.StartsWith ("#"))
|
||||
{
|
||||
if (line.Length < 2 || '#' != line[1])
|
||||
break;
|
||||
line = line.Substring (1);
|
||||
}
|
||||
tag_value.AppendLine (line);
|
||||
}
|
||||
dict[tag] = tag_value.ToString();
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new NotImplementedException ("EriFormat.Write not implemented");
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace GameRes.Formats.Eushully
|
||||
int bpp = stream.ReadByte();
|
||||
if (alpha_channel < 0 || alpha_channel > 1 || method < 0 || method > 2
|
||||
|| align1 < 0 || align1 > 4 || align2 < 0 || align2 > 4
|
||||
|| bpp <= 0 || !(bpp <= 16 || 24 == bpp || 32 == bpp))
|
||||
|| bpp < 0 || !(bpp <= 16 || 24 == bpp || 32 == bpp))
|
||||
return null;
|
||||
using (var reader = new ArcView.Reader (stream))
|
||||
{
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
//! \file ArcANM.cs
|
||||
//! \date Sat Jan 23 04:23:39 2016
|
||||
//! \brief KaGuYa script engine animation resource.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Kaguya
|
||||
{
|
||||
internal class AnmArchive : ArcFile
|
||||
{
|
||||
public readonly ImageMetaData ImageInfo;
|
||||
|
||||
public AnmArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData base_info)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
ImageInfo = base_info;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class AnmOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "ANM/KAGUYA"; } }
|
||||
public override string Description { get { return "KaGuYa script engine animation resource"; } }
|
||||
public override uint Signature { get { return 0x30304E41; } } // 'AN00'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public AnmOpener ()
|
||||
{
|
||||
Extensions = new string[] { "anm" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int frame_count = file.View.ReadInt16 (0x14);
|
||||
uint current_offset = 0x18 + (uint)frame_count * 4;
|
||||
int count = file.View.ReadInt16 (current_offset);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var base_info = new ImageMetaData
|
||||
{
|
||||
OffsetX = file.View.ReadInt32 (4),
|
||||
OffsetY = file.View.ReadInt32 (8),
|
||||
Width = file.View.ReadUInt32 (0x0C),
|
||||
Height = file.View.ReadUInt32 (0x10),
|
||||
BPP = 32,
|
||||
};
|
||||
current_offset += 2;
|
||||
string base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint width = file.View.ReadUInt32 (current_offset+8);
|
||||
uint height = file.View.ReadUInt32 (current_offset+12);
|
||||
var entry = new Entry
|
||||
{
|
||||
Name = string.Format ("{0}#{1:D2}.tga", base_name, i),
|
||||
Type = "image",
|
||||
Offset = current_offset,
|
||||
Size = 0x10 + 4*width*height,
|
||||
};
|
||||
dir.Add (entry);
|
||||
current_offset += entry.Size;
|
||||
}
|
||||
return new AnmArchive (file, this, dir, base_info);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var base_info = ((AnmArchive)arc).ImageInfo;
|
||||
// emulate TGA image
|
||||
var offset = entry.Offset;
|
||||
var info = new ImageMetaData
|
||||
{
|
||||
OffsetX = base_info.OffsetX + arc.File.View.ReadInt32 (offset),
|
||||
OffsetY = base_info.OffsetY + arc.File.View.ReadInt32 (offset+4),
|
||||
Width = arc.File.View.ReadUInt32 (offset+8),
|
||||
Height = arc.File.View.ReadUInt32 (offset+12),
|
||||
BPP = 32,
|
||||
};
|
||||
offset += 0x10;
|
||||
var pixels = arc.File.View.ReadBytes (offset, 4*info.Width*info.Height);
|
||||
return TgaStream.Create (info, pixels, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
//! \file ArcLINK.cs
|
||||
//! \date Fri Jan 22 18:44:56 2016
|
||||
//! \brief KaGuYa archive format.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Kaguya
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "LINK/KAGUYA"; } }
|
||||
public override string Description { get { return "KaGuYa script engine resource archive"; } }
|
||||
public override uint Signature { get { return 0x4B4E494C; } } // 'LINK'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int version = file.View.ReadByte (4) - '0';
|
||||
if (version != 3)
|
||||
return null;
|
||||
|
||||
long current_offset = 8;
|
||||
var dir = new List<Entry>();
|
||||
while (current_offset+4 < file.MaxOffset)
|
||||
{
|
||||
uint size = file.View.ReadUInt32 (current_offset);
|
||||
if (0 == size)
|
||||
break;
|
||||
if (size < 0x10)
|
||||
return null;
|
||||
bool is_compressed = file.View.ReadInt32 (current_offset+4) != 0;
|
||||
uint name_length = file.View.ReadByte (current_offset+0xD);
|
||||
var name = file.View.ReadString (current_offset+0x10, name_length);
|
||||
current_offset += 0x10 + name_length;
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = current_offset;
|
||||
entry.Size = size - (0x10 + name_length);
|
||||
entry.IsPacked = is_compressed && file.View.AsciiEqual (current_offset, "BMR");
|
||||
dir.Add (entry);
|
||||
current_offset += entry.Size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PackedEntry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
using (var bmr = new BmrDecoder (input))
|
||||
{
|
||||
bmr.Unpack();
|
||||
return new MemoryStream (bmr.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BmrDecoder : IDisposable
|
||||
{
|
||||
byte[] m_output;
|
||||
MsbBitStream m_input;
|
||||
int m_final_size;
|
||||
int m_step;
|
||||
int m_key;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
|
||||
public BmrDecoder (Stream input)
|
||||
{
|
||||
input.Position = 3;
|
||||
using (var header = new ArcView.Reader (input))
|
||||
{
|
||||
m_step = header.ReadByte();
|
||||
m_final_size = header.ReadInt32();
|
||||
m_key = header.ReadInt32();
|
||||
int unpacked_size = header.ReadInt32();
|
||||
m_output = new byte[unpacked_size];
|
||||
m_input = new MsbBitStream (input, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
m_input.Input.Position = 0x14;
|
||||
UnpackHuffman();
|
||||
DescrambleOutput();
|
||||
m_output = Decode (m_output, m_key);
|
||||
if (m_step != 0)
|
||||
m_output = DecompressRLE();
|
||||
}
|
||||
|
||||
byte[] DecompressRLE ()
|
||||
{
|
||||
var result = new byte[m_final_size];
|
||||
int src = 0;
|
||||
for (int i = 0; i < m_step; ++i)
|
||||
{
|
||||
byte v1 = m_output[src++];
|
||||
result[i] = v1;
|
||||
int dst = i + m_step;
|
||||
while (dst < result.Length)
|
||||
{
|
||||
byte v2 = m_output[src++];
|
||||
result[dst] = v2;
|
||||
dst += m_step;
|
||||
if (v2 == v1)
|
||||
{
|
||||
int count = m_output[src++];
|
||||
if (0 != (count & 0x80))
|
||||
count = m_output[src++] + ((count & 0x7F) << 8) + 128;
|
||||
while (count --> 0)
|
||||
{
|
||||
result[dst] = v2;
|
||||
dst += m_step;
|
||||
}
|
||||
if (dst < m_output.Length)
|
||||
{
|
||||
v2 = m_output[src++];
|
||||
result[dst] = v2;
|
||||
dst += m_step;
|
||||
}
|
||||
}
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void DescrambleOutput ()
|
||||
{
|
||||
var scramble = new byte[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
scramble[i] = (byte)i;
|
||||
for (int i = 0; i < m_output.Length; ++i)
|
||||
{
|
||||
byte v = m_output[i];
|
||||
m_output[i] = scramble[v];
|
||||
for (int j = v; j > 0; --j)
|
||||
{
|
||||
scramble[j] = scramble[j-1];
|
||||
}
|
||||
scramble[0] = m_output[i];
|
||||
}
|
||||
}
|
||||
|
||||
byte[] Decode (byte[] input, int key)
|
||||
{
|
||||
var freq_table = new int[256];
|
||||
for (int i = 0; i < input.Length; ++i)
|
||||
{
|
||||
++freq_table[input[i]];
|
||||
}
|
||||
for (int i = 1; i < 256; ++i)
|
||||
{
|
||||
freq_table[i] += freq_table[i-1];
|
||||
}
|
||||
var distrib_table = new int[input.Length];
|
||||
for (int i = input.Length-1; i >= 0; --i)
|
||||
{
|
||||
int v = input[i];
|
||||
int freq = freq_table[v] - 1;
|
||||
freq_table[v] = freq;
|
||||
distrib_table[freq] = i;
|
||||
}
|
||||
int pos = key;
|
||||
var copy_out = new byte[input.Length];
|
||||
for (int i = 0; i < copy_out.Length; ++i)
|
||||
{
|
||||
pos = distrib_table[pos];
|
||||
copy_out[i] = input[pos];
|
||||
}
|
||||
return copy_out;
|
||||
}
|
||||
|
||||
ushort m_token;
|
||||
ushort[,] m_tree = new ushort[2,256];
|
||||
|
||||
void UnpackHuffman ()
|
||||
{
|
||||
m_token = 256;
|
||||
ushort root = CreateHuffmanTree();
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
ushort symbol = root;
|
||||
while (symbol >= 0x100)
|
||||
{
|
||||
int bit = m_input.GetNextBit();
|
||||
if (-1 == bit)
|
||||
throw new EndOfStreamException();
|
||||
symbol = m_tree[bit,symbol-256];
|
||||
}
|
||||
m_output[dst++] = (byte)symbol;
|
||||
}
|
||||
}
|
||||
|
||||
ushort CreateHuffmanTree ()
|
||||
{
|
||||
if (0 != m_input.GetNextBit())
|
||||
{
|
||||
ushort v = m_token++;
|
||||
m_tree[0,v-256] = CreateHuffmanTree();
|
||||
m_tree[1,v-256] = CreateHuffmanTree();
|
||||
return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ushort)m_input.GetBits (8);
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -112,48 +112,4 @@ namespace GameRes.Formats.Kaguya
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Ap2Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "AP-2"; } }
|
||||
public override string Description { get { return "KaGuYa script engine image format"; } }
|
||||
public override uint Signature { get { return 0x322D5041; } } // 'AP-2'
|
||||
|
||||
public Ap2Format ()
|
||||
{
|
||||
Extensions = new string[] { "alp" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
stream.Position = 4;
|
||||
using (var file = new ArcView.Reader (stream))
|
||||
{
|
||||
var info = new ImageMetaData();
|
||||
info.OffsetX = file.ReadInt32();
|
||||
info.OffsetY = file.ReadInt32();
|
||||
info.Width = file.ReadUInt32();
|
||||
info.Height = file.ReadUInt32();
|
||||
info.BPP = 32;
|
||||
if (info.Width > 0x8000 || info.Height > 0x8000)
|
||||
return null;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
stream.Position = 0x18;
|
||||
var pixels = new byte[4*info.Width*info.Height];
|
||||
if (pixels.Length != stream.Read (pixels, 0, pixels.Length))
|
||||
throw new EndOfStreamException();
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgra32, null, pixels, 4*(int)info.Width);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Ap2Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-2015 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
|
||||
@@ -32,6 +32,8 @@ using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Diagnostics;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
@@ -338,73 +340,20 @@ NextEntry:
|
||||
var xp3_entry = entry as Xp3Entry;
|
||||
if (null == xp3_entry)
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
|
||||
Stream input;
|
||||
// Trace.WriteLine (string.Format ("{0,-16} {3:X8} {1,11} {2,12}", xp3_entry.Name,
|
||||
// xp3_entry.IsEncrypted ? "[encrypted]" : "",
|
||||
// xp3_entry.Segments.First().IsCompressed ? "[compressed]" : "",
|
||||
// xp3_entry.Hash));
|
||||
if (1 == xp3_entry.Segments.Count && !xp3_entry.IsEncrypted)
|
||||
{
|
||||
var segment = xp3_entry.Segments.First();
|
||||
if (segment.IsCompressed)
|
||||
input = new ZLibStream (arc.File.CreateStream (segment.Offset, segment.PackedSize),
|
||||
return new ZLibStream (arc.File.CreateStream (segment.Offset, segment.PackedSize),
|
||||
CompressionMode.Decompress);
|
||||
else
|
||||
input = arc.File.CreateStream (segment.Offset, segment.Size);
|
||||
}
|
||||
else
|
||||
input = new Xp3Stream (arc.File, xp3_entry);
|
||||
|
||||
if (entry.Size <= 5 || "image" == entry.Type || "audio" == entry.Type)
|
||||
return input;
|
||||
|
||||
var header = new byte[5];
|
||||
input.Read (header, 0, 5);
|
||||
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 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 arc.File.CreateStream (segment.Offset, segment.Size);
|
||||
}
|
||||
return new Xp3Stream (arc.File, xp3_entry);
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions ()
|
||||
@@ -837,4 +786,505 @@ NextEntry:
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public abstract class ICrypt
|
||||
{
|
||||
/// <summary>
|
||||
/// whether Adler32 checksum should be calculated after contents have been encrypted.
|
||||
/// </summary>
|
||||
public virtual bool HashAfterCrypt { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// whether XP3 index is obfuscated:
|
||||
/// - duplicate entries
|
||||
/// - entries have additional dummy segments
|
||||
/// </summary>
|
||||
public virtual bool ObfuscatedIndex { get { return false; } }
|
||||
|
||||
public virtual byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte[] buffer = new byte[1] { value };
|
||||
Decrypt (entry, offset, buffer, 0, 1);
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
public abstract void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count);
|
||||
|
||||
public virtual void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
throw new NotImplementedException (arcStrings.MsgEncNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NoCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class FateCrypt : ICrypt
|
||||
{
|
||||
public override bool HashAfterCrypt { get { return true; } }
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte result = (byte)(value ^ 0x36);
|
||||
if (0x13 == offset)
|
||||
result ^= 1;
|
||||
else if (0x2ea29 == offset)
|
||||
result ^= 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= 0x36;
|
||||
}
|
||||
if (offset > 0x2ea29)
|
||||
return;
|
||||
if (offset + count > 0x2ea29)
|
||||
values[pos+0x2ea29-offset] ^= 3;
|
||||
if (offset > 0x13)
|
||||
return;
|
||||
if (offset + count > 0x13)
|
||||
values[pos+0x13-offset] ^= 1;
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HashCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ entry.Hash);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class XorCrypt : ICrypt
|
||||
{
|
||||
private byte m_key;
|
||||
|
||||
public byte Key
|
||||
{
|
||||
get { return m_key; }
|
||||
set { m_key = value; }
|
||||
}
|
||||
|
||||
public XorCrypt (uint key)
|
||||
{
|
||||
m_key = (byte)key;
|
||||
}
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ m_key);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= m_key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SwanSongCrypt : ICrypt
|
||||
{
|
||||
static private byte Adjust (uint hash, out int shift)
|
||||
{
|
||||
int cl = (int)(hash & 0xff);
|
||||
if (0 == cl) cl = 0x0f;
|
||||
shift = cl & 7;
|
||||
int ch = (int)((hash >> 8) & 0xff);
|
||||
if (0 == ch) ch = 0xf0;
|
||||
return (byte)ch;
|
||||
}
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
uint data = (uint)(value ^ xor);
|
||||
return (byte)((data >> shift) | (data << (8 - shift)));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint data = (uint)(values[pos+i] ^ xor);
|
||||
values[pos+i] = (byte)((data >> shift) | (data << (8 - shift)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint data = values[pos+i];
|
||||
data = (byte)((data << shift) | (data >> (8 - shift)));
|
||||
values[pos+i] = (byte)(data ^ xor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SeitenCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
int ecx = (int)key & 0x18;
|
||||
value ^= (byte)((key >> ecx) | (key >> (ecx & 8)));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
value += (byte)key;
|
||||
}
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
value -= (byte)(key >> (int)(key & 0x10));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int shift;
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
byte v = buffer[pos+i];
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
shift = (int)key & 0x18;
|
||||
uint ebx = key >> shift;
|
||||
shift &= 8;
|
||||
v ^= (byte)(ebx | (key >> shift));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
v += (byte)key;
|
||||
}
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
shift = (int)key & 0x10;
|
||||
v -= (byte)(key >> shift);
|
||||
}
|
||||
buffer[pos+i] = v;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
values[pos+i] += (byte)(key >> (int)(key & 0x10));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
values[pos+i] -= (byte)key;
|
||||
}
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
int ecx = (int)key & 0x18;
|
||||
values[pos+i] ^= (byte)((key >> ecx) | (key >> (ecx & 8)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class OkibaCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (offset < 0x65)
|
||||
return (byte)(value ^ (byte)(entry.Hash >> 4));
|
||||
uint key = entry.Hash;
|
||||
// 0,1,2,3 -> 1,0,3,2
|
||||
key = ((key & 0xff0000) << 8) | ((key & 0xff000000) >> 8)
|
||||
| ((key & 0xff00) >> 8) | ((key & 0xff) << 8);
|
||||
key >>= 8 * ((int)(offset - 0x65) & 3);
|
||||
return (byte)(value ^ (byte)key);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int i = 0;
|
||||
if (offset < 0x65)
|
||||
{
|
||||
uint key = entry.Hash >> 4;
|
||||
int limit = Math.Min (count, (int)(0x65 - offset));
|
||||
for (; i < limit; ++i)
|
||||
{
|
||||
values[pos+i] ^= (byte)key;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
if (i < count)
|
||||
{
|
||||
offset -= 0x65;
|
||||
uint key = entry.Hash;
|
||||
key = ((key & 0xff0000) << 8) | ((key & 0xff000000) >> 8)
|
||||
| ((key & 0xff00) >> 8) | ((key & 0xff) << 8);
|
||||
do
|
||||
{
|
||||
values[pos+i] ^= (byte)(key >> (8 * ((int)offset & 3)));
|
||||
++offset;
|
||||
}
|
||||
while (++i < count);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SaiminCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
if (offset < 0x7B)
|
||||
value ^= (byte)(21 * key);
|
||||
else if (offset < 0xF6)
|
||||
value += (byte)(-32 * key);
|
||||
else if (offset < 0x171)
|
||||
value ^= (byte)(43 * key);
|
||||
else if (offset <= 0xffffffffL)
|
||||
value += (byte)(-54 * key);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count && offset <= 0xffffffffL; ++i, ++offset)
|
||||
{
|
||||
if (offset < 0x7B)
|
||||
values[pos+i] ^= (byte)(21 * key);
|
||||
else if (offset < 0xF6)
|
||||
values[pos+i] += (byte)(-32 * key);
|
||||
else if (offset >= 0x171)
|
||||
values[pos+i] += (byte)(-54 * key);
|
||||
else
|
||||
values[pos+i] ^= (byte)(43 * key);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count && offset <= 0xffffffffL; ++i, ++offset)
|
||||
{
|
||||
if (offset < 0x7B)
|
||||
values[pos+i] ^= (byte)(21 * key);
|
||||
else if (offset < 0xF6)
|
||||
values[pos+i] -= (byte)(-32 * key);
|
||||
else if (offset >= 0x171)
|
||||
values[pos+i] -= (byte)(-54 * key);
|
||||
else
|
||||
values[pos+i] ^= (byte)(43 * key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DameganeCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
return (byte)(value ^ entry.Hash);
|
||||
else
|
||||
return (byte)(value ^ offset);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i, ++offset)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
values[pos+i] ^= (byte)entry.Hash;
|
||||
else
|
||||
values[pos+i] ^= (byte)offset;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GakuenButouCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
return (byte)(value ^ offset);
|
||||
else
|
||||
return (byte)(value ^ entry.Hash);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i, ++offset)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
values[pos+i] ^= (byte)offset;
|
||||
else
|
||||
values[pos+i] ^= (byte)entry.Hash;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AlteredPinkCrypt : ICrypt
|
||||
{
|
||||
static readonly byte[] KeyTable = {
|
||||
0x43, 0xF8, 0xAD, 0x08, 0xDF, 0xB7, 0x26, 0x44, 0xF0, 0xD9, 0xE9, 0x24, 0x1A, 0xC1, 0xEE, 0xB4,
|
||||
0x11, 0x4B, 0xE4, 0xAF, 0x01, 0x5B, 0xF0, 0xAB, 0x6A, 0x70, 0x78, 0x84, 0xB0, 0x78, 0x4F, 0xED,
|
||||
0x39, 0x52, 0x69, 0xAF, 0xC4, 0x92, 0x2A, 0x21, 0xDE, 0xDC, 0x6E, 0x63, 0x9D, 0x9B, 0x63, 0xE1,
|
||||
0xB1, 0x94, 0x40, 0x6E, 0x3A, 0x52, 0x5A, 0x28, 0x08, 0x4D, 0xFB, 0x22, 0x18, 0xEB, 0xBA, 0x98,
|
||||
0x49, 0x77, 0xBF, 0xAA, 0x43, 0x75, 0xF5, 0xD3, 0x83, 0x71, 0x58, 0xA4, 0xAF, 0x1B, 0x53, 0x99,
|
||||
0x8A, 0x27, 0x5B, 0xC2, 0x7F, 0x7A, 0xCD, 0x8D, 0x33, 0x59, 0xEB, 0xA6, 0xFA, 0x7C, 0x00, 0x19,
|
||||
0xC4, 0xAA, 0x24, 0xF8, 0x84, 0xCD, 0xF7, 0x20, 0x4B, 0xAB, 0xF1, 0xD5, 0x01, 0x6F, 0x7C, 0x91,
|
||||
0x08, 0x7D, 0x8D, 0x89, 0x7C, 0x71, 0x65, 0x99, 0x9B, 0x6F, 0x3A, 0x1C, 0x49, 0xE3, 0xAF, 0x1F,
|
||||
0xC6, 0xA5, 0x79, 0xFE, 0xAE, 0xA1, 0xCA, 0x59, 0x3C, 0xEE, 0xC1, 0x02, 0xBD, 0x2B, 0x8E, 0xC5,
|
||||
0x7D, 0x38, 0x80, 0x8F, 0x72, 0xF3, 0x86, 0x5D, 0xF4, 0x20, 0x0A, 0x5B, 0xA0, 0xE3, 0x85, 0xB5,
|
||||
0x67, 0x43, 0x96, 0xBB, 0x75, 0x86, 0x8D, 0x7E, 0x7E, 0xE6, 0xAA, 0x18, 0x57, 0xC4, 0xAA, 0x87,
|
||||
0xDC, 0x74, 0x05, 0xAA, 0xBD, 0x5E, 0x4F, 0xA9, 0xB5, 0x5E, 0xC5, 0xE8, 0x11, 0x6D, 0x68, 0x89,
|
||||
0x17, 0x7C, 0x10, 0x05, 0xA2, 0xBA, 0x43, 0x01, 0xD6, 0xFD, 0x26, 0x19, 0x57, 0xFA, 0x4D, 0x01,
|
||||
0xB0, 0xED, 0x3A, 0x55, 0xEB, 0x65, 0x8E, 0xD1, 0x58, 0x27, 0xAD, 0xA1, 0x5E, 0x57, 0x3F, 0xA0,
|
||||
0xEF, 0x59, 0x3E, 0xA4, 0xEB, 0x12, 0x15, 0x60, 0xBE, 0x95, 0x61, 0x0B, 0x98, 0xF5, 0xF4, 0x12,
|
||||
0x1C, 0xD8, 0x62, 0x3F, 0xFD, 0xCF, 0x01, 0x3A, 0xE7, 0xC2, 0x19, 0x38, 0x6C, 0xC3, 0x90, 0x3E,
|
||||
};
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ KeyTable[offset & 0xFF]);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= KeyTable[(offset+i) & 0xFF];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NatsupochiCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ (entry.Hash >> 3));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)(entry.Hash >> 3);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class IncubusCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)~(value ^ (entry.Hash + 1));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)~(entry.Hash + 1);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,530 +0,0 @@
|
||||
//! \file CryptAlgorithms.cs
|
||||
//! \date Thu Feb 04 12:08:40 2016
|
||||
//! \brief KiriKiri engine encryption algorithms.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace GameRes.Formats.KiriKiri
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class ICrypt
|
||||
{
|
||||
/// <summary>
|
||||
/// whether Adler32 checksum should be calculated after contents have been encrypted.
|
||||
/// </summary>
|
||||
public virtual bool HashAfterCrypt { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// whether XP3 index is obfuscated:
|
||||
/// - duplicate entries
|
||||
/// - entries have additional dummy segments
|
||||
/// </summary>
|
||||
public virtual bool ObfuscatedIndex { get { return false; } }
|
||||
|
||||
public virtual byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte[] buffer = new byte[1] { value };
|
||||
Decrypt (entry, offset, buffer, 0, 1);
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
public abstract void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count);
|
||||
|
||||
public virtual void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
throw new NotImplementedException (Strings.arcStrings.MsgEncNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NoCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class FateCrypt : ICrypt
|
||||
{
|
||||
public override bool HashAfterCrypt { get { return true; } }
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte result = (byte)(value ^ 0x36);
|
||||
if (0x13 == offset)
|
||||
result ^= 1;
|
||||
else if (0x2ea29 == offset)
|
||||
result ^= 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= 0x36;
|
||||
}
|
||||
if (offset > 0x2ea29)
|
||||
return;
|
||||
if (offset + count > 0x2ea29)
|
||||
values[pos+0x2ea29-offset] ^= 3;
|
||||
if (offset > 0x13)
|
||||
return;
|
||||
if (offset + count > 0x13)
|
||||
values[pos+0x13-offset] ^= 1;
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HashCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ entry.Hash);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class XorCrypt : ICrypt
|
||||
{
|
||||
private byte m_key;
|
||||
|
||||
public byte Key
|
||||
{
|
||||
get { return m_key; }
|
||||
set { m_key = value; }
|
||||
}
|
||||
|
||||
public XorCrypt (uint key)
|
||||
{
|
||||
m_key = (byte)key;
|
||||
}
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ m_key);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= m_key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SwanSongCrypt : ICrypt
|
||||
{
|
||||
static private byte Adjust (uint hash, out int shift)
|
||||
{
|
||||
int cl = (int)(hash & 0xff);
|
||||
if (0 == cl) cl = 0x0f;
|
||||
shift = cl & 7;
|
||||
int ch = (int)((hash >> 8) & 0xff);
|
||||
if (0 == ch) ch = 0xf0;
|
||||
return (byte)ch;
|
||||
}
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
uint data = (uint)(value ^ xor);
|
||||
return (byte)((data >> shift) | (data << (8 - shift)));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint data = (uint)(values[pos+i] ^ xor);
|
||||
values[pos+i] = (byte)((data >> shift) | (data << (8 - shift)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int shift;
|
||||
byte xor = Adjust (entry.Hash, out shift);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint data = values[pos+i];
|
||||
data = (byte)((data << shift) | (data >> (8 - shift)));
|
||||
values[pos+i] = (byte)(data ^ xor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SeitenCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
int ecx = (int)key & 0x18;
|
||||
value ^= (byte)((key >> ecx) | (key >> (ecx & 8)));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
value += (byte)key;
|
||||
}
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
value -= (byte)(key >> (int)(key & 0x10));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int shift;
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
byte v = buffer[pos+i];
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
shift = (int)key & 0x18;
|
||||
uint ebx = key >> shift;
|
||||
shift &= 8;
|
||||
v ^= (byte)(ebx | (key >> shift));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
v += (byte)key;
|
||||
}
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
shift = (int)key & 0x10;
|
||||
v -= (byte)(key >> shift);
|
||||
}
|
||||
buffer[pos+i] = v;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint key = entry.Hash ^ (uint)offset;
|
||||
if (0 != (key & 8))
|
||||
{
|
||||
values[pos+i] += (byte)(key >> (int)(key & 0x10));
|
||||
}
|
||||
if (0 != (key & 4))
|
||||
{
|
||||
values[pos+i] -= (byte)key;
|
||||
}
|
||||
if (0 != (key & 2))
|
||||
{
|
||||
int ecx = (int)key & 0x18;
|
||||
values[pos+i] ^= (byte)((key >> ecx) | (key >> (ecx & 8)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class OkibaCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (offset < 0x65)
|
||||
return (byte)(value ^ (byte)(entry.Hash >> 4));
|
||||
uint key = entry.Hash;
|
||||
// 0,1,2,3 -> 1,0,3,2
|
||||
key = ((key & 0xff0000) << 8) | ((key & 0xff000000) >> 8)
|
||||
| ((key & 0xff00) >> 8) | ((key & 0xff) << 8);
|
||||
key >>= 8 * ((int)(offset - 0x65) & 3);
|
||||
return (byte)(value ^ (byte)key);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
int i = 0;
|
||||
if (offset < 0x65)
|
||||
{
|
||||
uint key = entry.Hash >> 4;
|
||||
int limit = Math.Min (count, (int)(0x65 - offset));
|
||||
for (; i < limit; ++i)
|
||||
{
|
||||
values[pos+i] ^= (byte)key;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
if (i < count)
|
||||
{
|
||||
offset -= 0x65;
|
||||
uint key = entry.Hash;
|
||||
key = ((key & 0xff0000) << 8) | ((key & 0xff000000) >> 8)
|
||||
| ((key & 0xff00) >> 8) | ((key & 0xff) << 8);
|
||||
do
|
||||
{
|
||||
values[pos+i] ^= (byte)(key >> (8 * ((int)offset & 3)));
|
||||
++offset;
|
||||
}
|
||||
while (++i < count);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SaiminCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
if (offset < 123)
|
||||
value ^= (byte)(21 * key);
|
||||
else if (offset < 246)
|
||||
value += (byte)(-32 * key);
|
||||
else if (offset < 369)
|
||||
value ^= (byte)(43 * key);
|
||||
else if (offset <= 0xffffffffL)
|
||||
value += (byte)(-54 * key);
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count && offset <= 0xffffffffL; ++i, ++offset)
|
||||
{
|
||||
if (offset < 123)
|
||||
values[pos+i] ^= (byte)(21 * key);
|
||||
else if (offset < 246)
|
||||
values[pos+i] += (byte)(-32 * key);
|
||||
else if (offset < 369)
|
||||
values[pos+i] ^= (byte)(43 * key);
|
||||
else
|
||||
values[pos+i] += (byte)(-54 * key);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)entry.Hash;
|
||||
for (int i = 0; i < count && offset <= 0xffffffffL; ++i, ++offset)
|
||||
{
|
||||
if (offset < 123)
|
||||
values[pos+i] ^= (byte)(21 * key);
|
||||
else if (offset < 246)
|
||||
values[pos+i] -= (byte)(-32 * key);
|
||||
else if (offset < 369)
|
||||
values[pos+i] ^= (byte)(43 * key);
|
||||
else
|
||||
values[pos+i] -= (byte)(-54 * key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DameganeCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
return (byte)(value ^ entry.Hash);
|
||||
else
|
||||
return (byte)(value ^ offset);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i, ++offset)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
values[pos+i] ^= (byte)entry.Hash;
|
||||
else
|
||||
values[pos+i] ^= (byte)offset;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GakuenButouCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
return (byte)(value ^ offset);
|
||||
else
|
||||
return (byte)(value ^ entry.Hash);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i, ++offset)
|
||||
{
|
||||
if (0 != (offset & 1))
|
||||
values[pos+i] ^= (byte)offset;
|
||||
else
|
||||
values[pos+i] ^= (byte)entry.Hash;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AlteredPinkCrypt : ICrypt
|
||||
{
|
||||
static readonly byte[] KeyTable = {
|
||||
0x43, 0xF8, 0xAD, 0x08, 0xDF, 0xB7, 0x26, 0x44, 0xF0, 0xD9, 0xE9, 0x24, 0x1A, 0xC1, 0xEE, 0xB4,
|
||||
0x11, 0x4B, 0xE4, 0xAF, 0x01, 0x5B, 0xF0, 0xAB, 0x6A, 0x70, 0x78, 0x84, 0xB0, 0x78, 0x4F, 0xED,
|
||||
0x39, 0x52, 0x69, 0xAF, 0xC4, 0x92, 0x2A, 0x21, 0xDE, 0xDC, 0x6E, 0x63, 0x9D, 0x9B, 0x63, 0xE1,
|
||||
0xB1, 0x94, 0x40, 0x6E, 0x3A, 0x52, 0x5A, 0x28, 0x08, 0x4D, 0xFB, 0x22, 0x18, 0xEB, 0xBA, 0x98,
|
||||
0x49, 0x77, 0xBF, 0xAA, 0x43, 0x75, 0xF5, 0xD3, 0x83, 0x71, 0x58, 0xA4, 0xAF, 0x1B, 0x53, 0x99,
|
||||
0x8A, 0x27, 0x5B, 0xC2, 0x7F, 0x7A, 0xCD, 0x8D, 0x33, 0x59, 0xEB, 0xA6, 0xFA, 0x7C, 0x00, 0x19,
|
||||
0xC4, 0xAA, 0x24, 0xF8, 0x84, 0xCD, 0xF7, 0x20, 0x4B, 0xAB, 0xF1, 0xD5, 0x01, 0x6F, 0x7C, 0x91,
|
||||
0x08, 0x7D, 0x8D, 0x89, 0x7C, 0x71, 0x65, 0x99, 0x9B, 0x6F, 0x3A, 0x1C, 0x49, 0xE3, 0xAF, 0x1F,
|
||||
0xC6, 0xA5, 0x79, 0xFE, 0xAE, 0xA1, 0xCA, 0x59, 0x3C, 0xEE, 0xC1, 0x02, 0xBD, 0x2B, 0x8E, 0xC5,
|
||||
0x7D, 0x38, 0x80, 0x8F, 0x72, 0xF3, 0x86, 0x5D, 0xF4, 0x20, 0x0A, 0x5B, 0xA0, 0xE3, 0x85, 0xB5,
|
||||
0x67, 0x43, 0x96, 0xBB, 0x75, 0x86, 0x8D, 0x7E, 0x7E, 0xE6, 0xAA, 0x18, 0x57, 0xC4, 0xAA, 0x87,
|
||||
0xDC, 0x74, 0x05, 0xAA, 0xBD, 0x5E, 0x4F, 0xA9, 0xB5, 0x5E, 0xC5, 0xE8, 0x11, 0x6D, 0x68, 0x89,
|
||||
0x17, 0x7C, 0x10, 0x05, 0xA2, 0xBA, 0x43, 0x01, 0xD6, 0xFD, 0x26, 0x19, 0x57, 0xFA, 0x4D, 0x01,
|
||||
0xB0, 0xED, 0x3A, 0x55, 0xEB, 0x65, 0x8E, 0xD1, 0x58, 0x27, 0xAD, 0xA1, 0x5E, 0x57, 0x3F, 0xA0,
|
||||
0xEF, 0x59, 0x3E, 0xA4, 0xEB, 0x12, 0x15, 0x60, 0xBE, 0x95, 0x61, 0x0B, 0x98, 0xF5, 0xF4, 0x12,
|
||||
0x1C, 0xD8, 0x62, 0x3F, 0xFD, 0xCF, 0x01, 0x3A, 0xE7, 0xC2, 0x19, 0x38, 0x6C, 0xC3, 0x90, 0x3E,
|
||||
};
|
||||
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ KeyTable[offset & 0xFF]);
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= KeyTable[(offset+i) & 0xFF];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NatsupochiCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)(value ^ (entry.Hash >> 3));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)(entry.Hash >> 3);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class IncubusCrypt : ICrypt
|
||||
{
|
||||
public override byte Decrypt (Xp3Entry entry, long offset, byte value)
|
||||
{
|
||||
return (byte)~(value ^ (entry.Hash + 1));
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
byte key = (byte)~(entry.Hash + 1);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
values[pos+i] ^= key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, values, pos, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
//! \file CompressedFile.cs
|
||||
//! \date Fri Feb 05 01:33:25 2016
|
||||
//! \brief Mokopro compressed resources.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Mokopro
|
||||
{
|
||||
internal class NNNNMetaData : ImageMetaData
|
||||
{
|
||||
public ImageMetaData BmpInfo;
|
||||
public MokoCrypt Input;
|
||||
}
|
||||
|
||||
internal class MokoCrypt
|
||||
{
|
||||
static readonly byte[] DefaultKey = new byte[] { 1, 0x23 };
|
||||
|
||||
byte[] m_input;
|
||||
int m_unpacked_size;
|
||||
|
||||
public MokoCrypt (Stream stream)
|
||||
{
|
||||
var header = new byte[8];
|
||||
if (8 != stream.Read (header, 0, 8) || !Binary.AsciiEqual (header, 0, "NNNN"))
|
||||
throw new InvalidFormatException();
|
||||
m_unpacked_size = LittleEndian.ToInt32 (header, 4);
|
||||
if (m_unpacked_size <= 0)
|
||||
throw new InvalidFormatException();
|
||||
m_input = new byte[stream.Length-8];
|
||||
stream.Read (m_input, 0, m_input.Length);
|
||||
Decrypt (m_input, DefaultKey);
|
||||
}
|
||||
|
||||
public Stream UnpackStream ()
|
||||
{
|
||||
var lzss = new LzssStream (new MemoryStream (m_input));
|
||||
lzss.Config.FrameFill = 0x20;
|
||||
return lzss;
|
||||
}
|
||||
|
||||
public byte[] UnpackBytes ()
|
||||
{
|
||||
using (var mem = new MemoryStream (m_input))
|
||||
using (var lzss = new LzssReader (mem, m_input.Length, m_unpacked_size))
|
||||
{
|
||||
lzss.FrameFill = 0x20;
|
||||
lzss.Unpack();
|
||||
return lzss.Data;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Decrypt (byte[] input, byte[] key)
|
||||
{
|
||||
for (int i = input.Length-2; i >= 0; --i)
|
||||
{
|
||||
input[i] ^= (byte)(key[1] ^ input[i+1]);
|
||||
input[i+1] ^= (byte)(key[0] ^ input[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class NNNNBmpFormat : BmpFormat
|
||||
{
|
||||
public override string Tag { get { return "BMP/NNNN"; } }
|
||||
public override string Description { get { return "Mokopro compressed bitmap"; } }
|
||||
public override uint Signature { get { return 0x4E4E4E4E; } } // 'NNNN'
|
||||
|
||||
public NNNNBmpFormat ()
|
||||
{
|
||||
Extensions = new string[0];
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var moko = new MokoCrypt (stream);
|
||||
using (var lzss = moko.UnpackStream())
|
||||
{
|
||||
var info = base.ReadMetaData (lzss);
|
||||
if (null == info)
|
||||
return null;
|
||||
return new NNNNMetaData
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
BPP = info.BPP,
|
||||
BmpInfo = info,
|
||||
Input = moko,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var meta = (NNNNMetaData)info;
|
||||
using (var lzss = meta.Input.UnpackStream())
|
||||
return base.Read (lzss, meta.BmpInfo);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("NNNNFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class NNNNOggAudio : AudioFormat
|
||||
{
|
||||
public override string Tag { get { return "OGG/NNNN"; } }
|
||||
public override string Description { get { return "Mokopro compressed audio"; } }
|
||||
public override uint Signature { get { return 0x4E4E4E4E; } } // 'NNNN'
|
||||
|
||||
public NNNNOggAudio ()
|
||||
{
|
||||
Extensions = new string[0];
|
||||
}
|
||||
|
||||
public override SoundInput TryOpen (Stream stream)
|
||||
{
|
||||
var moko = new MokoCrypt (stream);
|
||||
var ogg = moko.UnpackBytes();
|
||||
var output = new MemoryStream (ogg);
|
||||
try
|
||||
{
|
||||
return new OggInput (output);
|
||||
}
|
||||
catch
|
||||
{
|
||||
output.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class NNNNOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "DAT/NNNN"; } }
|
||||
public override string Description { get { return "Mokopro compressed file"; } }
|
||||
public override uint Signature { get { return 0x4E4E4E4E; } } // 'NNNN'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public NNNNOpener ()
|
||||
{
|
||||
Extensions = new string[] { "dat" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var name = Path.GetFileName (file.Name);
|
||||
var dir = new List<Entry> (1);
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.Offset = 0;
|
||||
entry.Size = (uint)file.MaxOffset;
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (4);
|
||||
dir.Add (entry);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var moko = new MokoCrypt (input);
|
||||
return moko.UnpackStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Wed Feb 25 20:28:14 2015
|
||||
//! \brief Nitro+ PAK archives implementation.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
// Copyright (C) 2015 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
|
||||
@@ -125,7 +125,8 @@ namespace GameRes.Formats.NitroPlus
|
||||
uint size_xor = file.View.ReadUInt32 (0x104);
|
||||
if (0x64 != size_xor)
|
||||
return null;
|
||||
byte[] name_buf = file.View.ReadBytes (4, 0x100);
|
||||
byte[] name_buf = new byte[0x100];
|
||||
file.View.Read (4, name_buf, 0, 0x100);
|
||||
int name_len = 0;
|
||||
for (int i = 0; i < name_buf.Length; ++i)
|
||||
{
|
||||
@@ -222,12 +223,13 @@ namespace GameRes.Formats.NitroPlus
|
||||
uint enc_size = Math.Min (entry.Size, 0x10u);
|
||||
if (0 == enc_size)
|
||||
return Stream.Null;
|
||||
var buf = arc.File.View.ReadBytes (entry.Offset, enc_size);
|
||||
var buf = new byte[enc_size];
|
||||
arc.File.View.Read (entry.Offset, buf, 0, enc_size);
|
||||
uint key = entry.Key;
|
||||
for (int i = 0; i < buf.Length; ++i)
|
||||
{
|
||||
buf[i] ^= (byte)key;
|
||||
key = Binary.RotR (key, 8);
|
||||
key = key >> 8 | key << 24;
|
||||
}
|
||||
if (enc_size == entry.Size)
|
||||
return new MemoryStream (buf, false);
|
||||
|
||||
@@ -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.17.706")]
|
||||
[assembly: AssemblyFileVersion ("1.2.17.706")]
|
||||
[assembly: AssemblyVersion ("1.2.16.641")]
|
||||
[assembly: AssemblyFileVersion ("1.2.16.641")]
|
||||
|
||||
@@ -27,8 +27,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Softpal
|
||||
{
|
||||
@@ -43,25 +41,15 @@ namespace GameRes.Formats.Softpal
|
||||
|
||||
public VafsOpener ()
|
||||
{
|
||||
Extensions = new string[] { "052", "055" };
|
||||
Extensions = new string[] { "052" };
|
||||
}
|
||||
|
||||
static readonly Lazy<ImageFormat> s_PicFormat = new Lazy<ImageFormat> (() => ImageFormat.FindByTag ("PIC/SOFTPAL"));
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if ('H' != file.View.ReadByte (4))
|
||||
return null;
|
||||
uint index_offset = 0x10;
|
||||
uint data_offset = file.View.ReadUInt32 (index_offset);
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name).ToUpperInvariant();
|
||||
if (0 == data_offset && "TP" == base_name)
|
||||
{
|
||||
if (file.Name.EndsWith (".055"))
|
||||
return OpenTp055Arc (file);
|
||||
else
|
||||
return OpenTpArc (file);
|
||||
}
|
||||
if (data_offset < index_offset || data_offset >= file.MaxOffset)
|
||||
return null;
|
||||
int count = (int)(data_offset - index_offset) / 4;
|
||||
@@ -69,38 +57,16 @@ namespace GameRes.Formats.Softpal
|
||||
return null;
|
||||
|
||||
uint next_offset = data_offset;
|
||||
bool is_bgm = "BGM" == base_name;
|
||||
bool is_pic = "PIC" == base_name;
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name).ToUpperInvariant();
|
||||
bool is_audio = "BGM" == base_name;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; next_offset != file.MaxOffset && i < count; ++i)
|
||||
for (int i = 0; next_offset != 0 && next_offset != file.MaxOffset && i < count; ++i)
|
||||
{
|
||||
index_offset += 4;
|
||||
var name = string.Format("{0}#{1:D5}", base_name, i);
|
||||
var offset = next_offset;
|
||||
var entry = AutoEntry.Create (file, next_offset, name);
|
||||
next_offset = index_offset == data_offset ? 0 : file.View.ReadUInt32 (index_offset);
|
||||
if (uint.MaxValue == next_offset || next_offset < offset)
|
||||
break;
|
||||
uint size = next_offset - offset;
|
||||
if (size < 4)
|
||||
continue;
|
||||
Entry entry;
|
||||
if (is_pic)
|
||||
entry = new Entry { Name = name, Type = "image" };
|
||||
else if (is_bgm)
|
||||
entry = new Entry { Name = name + ".wav", Type = "audio" };
|
||||
else
|
||||
entry = new AutoEntry (name, () => {
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
uint s16 = signature & 0xFFFF;
|
||||
if (1 == s16 || 3 == s16 || 4 == s16)
|
||||
return s_PicFormat.Value;
|
||||
if (size > 0x200 && (size >> 9) == (signature >> 9))
|
||||
return AudioFormat.Wav;
|
||||
return AutoEntry.DetectFileType (signature);
|
||||
});
|
||||
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
entry.Size = (uint)((0 != next_offset ? (long)next_offset : file.MaxOffset) - entry.Offset);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
@@ -109,290 +75,5 @@ namespace GameRes.Formats.Softpal
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
ArcFile OpenTpArc (ArcView file)
|
||||
{
|
||||
uint index_offset = 0x20;
|
||||
uint data_offset;
|
||||
for (;;)
|
||||
{
|
||||
data_offset = file.View.ReadUInt32 (index_offset);
|
||||
if (0 != data_offset)
|
||||
break;
|
||||
index_offset += 0x10;
|
||||
if (0xA010 == index_offset)
|
||||
return null;
|
||||
}
|
||||
if (data_offset >= file.MaxOffset)
|
||||
return null;
|
||||
var dir = new List<Entry>();
|
||||
while (index_offset < data_offset)
|
||||
{
|
||||
var offset = file.View.ReadUInt32 (index_offset);
|
||||
if (0 != offset)
|
||||
{
|
||||
var name = string.Format("TP#{0:D5}.wav", index_offset/0x10 - 1);
|
||||
var entry = new Entry {
|
||||
Name = name,
|
||||
Type = "audio",
|
||||
Offset = offset,
|
||||
Size = 0x402 * file.View.ReadUInt32 (index_offset+4),
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
index_offset += 0x10;
|
||||
}
|
||||
return new TpArchive (file, this, dir);
|
||||
}
|
||||
|
||||
ArcFile OpenTp055Arc (ArcView file)
|
||||
{
|
||||
uint index_offset = 0x20;
|
||||
uint data_offset;
|
||||
for (;;)
|
||||
{
|
||||
data_offset = file.View.ReadUInt32 (index_offset);
|
||||
if (0 != data_offset)
|
||||
break;
|
||||
index_offset += 0x10;
|
||||
if (0xA010 == index_offset)
|
||||
return null;
|
||||
}
|
||||
if (data_offset >= file.MaxOffset)
|
||||
return null;
|
||||
var dir = new List<Entry>();
|
||||
while (index_offset < data_offset)
|
||||
{
|
||||
var offset = file.View.ReadUInt32 (index_offset);
|
||||
if (0 != offset)
|
||||
{
|
||||
if (offset >= file.MaxOffset)
|
||||
return null;
|
||||
var name = string.Format("TP#{0:D6}.wav", index_offset/0x10 - 1);
|
||||
var entry = new Tp055Entry {
|
||||
Name = name,
|
||||
Offset = offset,
|
||||
ChunkCount = file.View.ReadInt32 (index_offset+4),
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
index_offset += 0x10;
|
||||
}
|
||||
for (int i = 0; i < dir.Count-1; ++i)
|
||||
{
|
||||
dir[i].Size = (uint)(dir[i+1].Offset - dir[i].Offset);
|
||||
}
|
||||
dir[dir.Count-1].Size = (uint)(file.MaxOffset - dir[dir.Count-1].Offset);
|
||||
return new TpArchive (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry is Tp055Entry)
|
||||
return OpenVoice055Entry (arc, entry as Tp055Entry);
|
||||
else if (arc is TpArchive)
|
||||
return OpenVoiceEntry (arc, entry);
|
||||
else if ("audio" == entry.Type
|
||||
&& AudioFormat.Wav.Signature != arc.File.View.ReadUInt32 (entry.Offset))
|
||||
return OpenAudioEntry (arc, entry);
|
||||
else
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
|
||||
Stream OpenAudioEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var offset = entry.Offset;
|
||||
int size = arc.File.View.ReadInt32 (offset);
|
||||
offset += 4;
|
||||
int chunk_count = size >> 9;
|
||||
int pcm_size = chunk_count * 0x400;
|
||||
var output = new MemoryStream (0x24 + pcm_size);
|
||||
using (var wav = new BinaryWriter (output, Encoding.ASCII, true))
|
||||
{
|
||||
WriteWavHeader (wav, 2, 22050, pcm_size);
|
||||
var buffer = new byte[0x204];
|
||||
for (int chunk = 0; chunk < chunk_count; ++chunk)
|
||||
{
|
||||
arc.File.View.Read (offset, buffer, 0, 0x204);
|
||||
offset += 0x204;
|
||||
int src = 0;
|
||||
var data_offset = wav.BaseStream.Position;
|
||||
for (int channel = 0; channel < 2; ++channel)
|
||||
{
|
||||
int addend = buffer[src++] << 8;
|
||||
int pcm = LittleEndian.ToInt16 (buffer, src);
|
||||
src += 2;
|
||||
wav.BaseStream.Position = data_offset + channel * 2;
|
||||
wav.Write ((short)pcm);
|
||||
for (int i = 0; i < 255; ++i)
|
||||
{
|
||||
byte v = buffer[src++];
|
||||
int diff = v + addend;
|
||||
pcm += WaveTable1.Value[diff];
|
||||
if (pcm > 32767)
|
||||
pcm = 32767;
|
||||
else if (pcm < -32767)
|
||||
pcm = -32767;
|
||||
wav.BaseStream.Seek (2, SeekOrigin.Current);
|
||||
wav.Write ((short)pcm);
|
||||
addend += WaveTable2.Value[v];
|
||||
if (addend < 0)
|
||||
addend = 0;
|
||||
else if (addend >= 16384)
|
||||
addend = 16128;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Position = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
Stream OpenVoiceEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
int remaining = (int)entry.Size;
|
||||
int chunk_count = remaining / 0x402;
|
||||
int pcm_size = chunk_count * 0x800;
|
||||
var output = new MemoryStream (0x24 + pcm_size);
|
||||
var offset = entry.Offset;
|
||||
using (var wav = new BinaryWriter (output, Encoding.ASCII, true))
|
||||
{
|
||||
WriteWavHeader (wav, 1, 22050, pcm_size);
|
||||
var buffer = new byte[0x402];
|
||||
while (remaining > 0)
|
||||
{
|
||||
arc.File.View.Read (offset, buffer, 0, 0x402);
|
||||
offset += 0x402;
|
||||
remaining -= 0x402;
|
||||
int pcm = LittleEndian.ToInt16 (buffer, 1);
|
||||
int addend = buffer[0] << 8;
|
||||
wav.Write ((short)pcm);
|
||||
for (int src = 3; src < buffer.Length; ++src)
|
||||
{
|
||||
byte v = buffer[src];
|
||||
int diff = v + addend;
|
||||
pcm += WaveTable1.Value[diff];
|
||||
if (pcm > 32767)
|
||||
pcm = 32767;
|
||||
else if (pcm < -32767)
|
||||
pcm = -32767;
|
||||
wav.Write ((short)pcm);
|
||||
addend += WaveTable2.Value[v];
|
||||
if (addend < 0)
|
||||
addend = 0;
|
||||
else if (addend >= 16384)
|
||||
addend = 16128;
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Position = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
Stream OpenVoice055Entry (ArcFile arc, Tp055Entry entry)
|
||||
{
|
||||
int pcm_size = entry.ChunkCount * 0x800;
|
||||
var offset = entry.Offset;
|
||||
var wav_mem = new MemoryStream (0x24 + pcm_size);
|
||||
using (var wav = new BinaryWriter (wav_mem, Encoding.ASCII, true))
|
||||
{
|
||||
WriteWavHeader (wav, 1, 22050, pcm_size);
|
||||
var buffer = new byte[0x402];
|
||||
var output = new int[0x400];
|
||||
for (int i = 0; i < entry.ChunkCount; ++i)
|
||||
{
|
||||
int ctl = arc.File.View.ReadByte (offset++) & 0x3F;
|
||||
for (int j = 0; j < output.Length; ++j)
|
||||
output[j] = 0;
|
||||
for (; ctl != 0; ctl >>= 1)
|
||||
{
|
||||
if (0 == (ctl & 1))
|
||||
continue;
|
||||
arc.File.View.Read (offset, buffer, 0, 0x402);
|
||||
offset += 0x402;
|
||||
int pcm = LittleEndian.ToInt16 (buffer, 1);
|
||||
int dst = 0;
|
||||
int addend = buffer[0] << 8;
|
||||
output[dst++] += pcm;
|
||||
for (int src = 3; src < buffer.Length; ++src)
|
||||
{
|
||||
byte v = buffer[src];
|
||||
int diff = v + addend;
|
||||
pcm += WaveTable1.Value[diff];
|
||||
output[dst++] += (short)pcm;
|
||||
addend += WaveTable2.Value[v];
|
||||
if (addend < 0)
|
||||
addend = 0;
|
||||
else if (addend >= 16384)
|
||||
addend = 16128;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < output.Length; ++j)
|
||||
{
|
||||
int pcm = output[j];
|
||||
if (pcm > 32767)
|
||||
pcm = 32767;
|
||||
else if (pcm < -32767)
|
||||
pcm = -32767;
|
||||
wav.Write ((short)pcm);
|
||||
}
|
||||
}
|
||||
}
|
||||
wav_mem.Position = 0;
|
||||
return wav_mem;
|
||||
}
|
||||
|
||||
void WriteWavHeader (BinaryWriter wav, short channels, int freq, int pcm_size)
|
||||
{
|
||||
wav.Write ("RIFF".ToCharArray());
|
||||
wav.Write (0x24 + pcm_size);
|
||||
wav.Write ("WAVE".ToCharArray());
|
||||
wav.Write ("fmt ".ToCharArray());
|
||||
wav.Write (0x10);
|
||||
wav.Write ((ushort)1);
|
||||
wav.Write ((ushort)channels);
|
||||
wav.Write (freq);
|
||||
wav.Write (freq*channels*2);
|
||||
wav.Write ((ushort)(channels*2));
|
||||
wav.Write ((ushort)16);
|
||||
wav.Write ("data".ToCharArray());
|
||||
wav.Write (pcm_size);
|
||||
}
|
||||
|
||||
static readonly Lazy<short[]> WaveTable1 = new Lazy<short[]> (() => LoadWaveTable ("WaveTable1"));
|
||||
static readonly Lazy<short[]> WaveTable2 = new Lazy<short[]> (() => LoadWaveTable ("WaveTable2"));
|
||||
|
||||
static short[] LoadWaveTable (string name)
|
||||
{
|
||||
|
||||
var assembly = typeof(VafsOpener).Assembly;
|
||||
using (var stream = assembly.GetManifestResourceStream ("GameRes.Formats.Softpal."+name))
|
||||
{
|
||||
if (null == stream)
|
||||
return null;
|
||||
var src = new byte[stream.Length];
|
||||
stream.Read (src, 0, src.Length);
|
||||
var array = new short[src.Length/2];
|
||||
Buffer.BlockCopy (src, 0, array, 0, src.Length);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TpArchive : ArcFile
|
||||
{
|
||||
public TpArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class Tp055Entry : Entry
|
||||
{
|
||||
public int ChunkCount;
|
||||
|
||||
public override string Type { get { return "audio"; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace GameRes.Formats.Softpal
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
int pixel_size = LittleEndian.ToInt32 (header, 12);
|
||||
if (pixel_size != 4 && pixel_size != 3 && pixel_size != 1)
|
||||
if (pixel_size != 3 && pixel_size != 4)
|
||||
return null;
|
||||
return new ImageMetaData
|
||||
{
|
||||
@@ -60,17 +60,13 @@ namespace GameRes.Formats.Softpal
|
||||
var pixels = new byte[(int)info.Width * (int)info.Height * pixel_size];
|
||||
if (pixels.Length != stream.Read (pixels, 0, pixels.Length))
|
||||
throw new EndOfStreamException();
|
||||
if (pixel_size > 1)
|
||||
for (int i = 2; i < pixels.Length; i += pixel_size)
|
||||
{
|
||||
for (int i = 2; i < pixels.Length; i += pixel_size)
|
||||
{
|
||||
byte t = pixels[i];
|
||||
pixels[i] = pixels[i-2];
|
||||
pixels[i-2] = t;
|
||||
}
|
||||
byte t = pixels[i];
|
||||
pixels[i] = pixels[i-2];
|
||||
pixels[i-2] = t;
|
||||
}
|
||||
PixelFormat format = 24 == info.BPP ? PixelFormats.Bgr24 :
|
||||
8 == info.BPP ? PixelFormats.Gray8 : PixelFormats.Bgra32;
|
||||
PixelFormat format = 24 == info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgr32;
|
||||
return ImageData.Create (info, format, null, pixels);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
//! \file ImagePIC.cs
|
||||
//! \date Sun Jan 24 18:48:25 2016
|
||||
//! \brief Softpal engine image format.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Softpal
|
||||
{
|
||||
internal class PicMetaData : ImageMetaData
|
||||
{
|
||||
public int BlocksWidth;
|
||||
public int BlocksHeight;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PicFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "PIC/SOFTPAL"; } }
|
||||
public override string Description { get { return "Softpal engine image format"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public PicFormat ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var header = new byte[8];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
int bpp = LittleEndian.ToInt16 (header, 0);
|
||||
if (1 != bpp && 3 != bpp && 4 != bpp)
|
||||
return null;
|
||||
uint width = LittleEndian.ToUInt16 (header, 2);
|
||||
uint height = LittleEndian.ToUInt16 (header, 4);
|
||||
if (0 == width || 0 == height || header[6]*8 < width || header[7]*8 < height)
|
||||
return null;
|
||||
return new PicMetaData
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = bpp * 8,
|
||||
BlocksWidth = header[6],
|
||||
BlocksHeight = header[7],
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var meta = (PicMetaData)info;
|
||||
using (var reader = new PicReader (stream, meta))
|
||||
{
|
||||
reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, null, reader.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PicFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PicReader : IDisposable
|
||||
{
|
||||
BinaryReader m_input;
|
||||
byte[] m_output;
|
||||
PicMetaData m_info;
|
||||
byte[] m_control;
|
||||
int m_src_stride;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
|
||||
readonly byte[] Values2bit = InitBlockValues (2);
|
||||
readonly byte[] Values4bit = InitBlockValues (8);
|
||||
readonly byte[] Values6bit = InitBlockValues (0x20);
|
||||
|
||||
public PicReader (Stream input, PicMetaData info)
|
||||
{
|
||||
m_input = new ArcView.Reader (input);
|
||||
m_info = info;
|
||||
m_src_stride = m_info.BlocksWidth * m_info.BPP;
|
||||
}
|
||||
|
||||
static byte[] InitBlockValues (int length)
|
||||
{
|
||||
var values = new byte[length*2];
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
values[i] = (byte)i;
|
||||
values[values.Length-1-i] = (byte)(-1-i);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
m_input.BaseStream.Position = 8;
|
||||
m_control = m_input.ReadBytes (m_info.BlocksHeight * m_info.BlocksWidth);
|
||||
m_output = new byte[m_src_stride * m_info.BlocksHeight * 8];
|
||||
if (8 == m_info.BPP)
|
||||
{
|
||||
Format = PixelFormats.Gray8;
|
||||
Unpack8bpp();
|
||||
}
|
||||
else if (24 == m_info.BPP)
|
||||
{
|
||||
Format = PixelFormats.Bgr24;
|
||||
Unpack24bpp();
|
||||
}
|
||||
else if (32 == m_info.BPP)
|
||||
{
|
||||
Format = PixelFormats.Bgra32;
|
||||
Unpack32bpp();
|
||||
}
|
||||
else
|
||||
throw new InvalidFormatException();
|
||||
|
||||
int dst_stride = (int)m_info.Width * m_info.BPP / 8;
|
||||
byte[] flipped = new byte[dst_stride*(int)m_info.Height];
|
||||
int dst = 0;
|
||||
int src = m_output.Length - m_src_stride;
|
||||
while (dst < flipped.Length)
|
||||
{
|
||||
Buffer.BlockCopy (m_output, src, flipped, dst, dst_stride);
|
||||
dst += dst_stride;
|
||||
src -= m_src_stride;
|
||||
}
|
||||
m_output = flipped;
|
||||
}
|
||||
|
||||
void Unpack24bpp()
|
||||
{
|
||||
int pixel_size = 3;
|
||||
var pixel = new byte[4];
|
||||
var block = new byte[64];
|
||||
for (int y = 0; y < m_info.BlocksHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < m_info.BlocksWidth; ++x)
|
||||
{
|
||||
byte ctl = m_control[x + m_info.BlocksWidth * (m_info.BlocksHeight - y - 1)];
|
||||
if (0x80 == ctl)
|
||||
{
|
||||
int dst = pixel_size * 8 * (x + 8 * y * m_info.BlocksWidth);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
for (int j = 0; j < 24; ++j)
|
||||
m_output[dst+j] = 0;
|
||||
dst += m_src_stride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (pixel, 0, pixel_size);
|
||||
for (int channel = 0; channel < pixel_size; ++channel)
|
||||
{
|
||||
DecodeBlock (ctl & 3, pixel[channel], block);
|
||||
int dst = pixel_size * 8 * (x + 8 * y * m_info.BlocksWidth) + channel;
|
||||
int src = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
m_output[dst] = block[src++];
|
||||
m_output[dst+3] = block[src++];
|
||||
m_output[dst+6] = block[src++];
|
||||
m_output[dst+9] = block[src++];
|
||||
m_output[dst+12] = block[src++];
|
||||
m_output[dst+15] = block[src++];
|
||||
m_output[dst+18] = block[src++];
|
||||
m_output[dst+21] = block[src++];
|
||||
dst += m_src_stride;
|
||||
}
|
||||
ctl >>= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unpack32bpp()
|
||||
{
|
||||
int pixel_size = 4;
|
||||
var pixel = new byte[4];
|
||||
var block = new byte[64];
|
||||
for (int y = 0; y < m_info.BlocksHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < m_info.BlocksWidth; ++x)
|
||||
{
|
||||
byte ctl = m_control[x + m_info.BlocksWidth * (m_info.BlocksHeight - y - 1)];
|
||||
m_input.Read (pixel, 0, pixel_size);
|
||||
for (int channel = 0; channel < pixel_size; ++channel)
|
||||
{
|
||||
DecodeBlock (ctl & 3, pixel[channel], block);
|
||||
int dst = 8 * pixel_size * (x + 8 * y * m_info.BlocksWidth) + ChannelOrder[channel];
|
||||
int src = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
m_output[dst] = block[src++];
|
||||
m_output[dst+4] = block[src++];
|
||||
m_output[dst+8] = block[src++];
|
||||
m_output[dst+12] = block[src++];
|
||||
m_output[dst+16] = block[src++];
|
||||
m_output[dst+20] = block[src++];
|
||||
m_output[dst+24] = block[src++];
|
||||
m_output[dst+28] = block[src++];
|
||||
dst += m_src_stride;
|
||||
}
|
||||
ctl >>= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly byte[] ChannelOrder = { 3, 0, 1, 2 };
|
||||
|
||||
void Unpack8bpp()
|
||||
{
|
||||
var block = new byte[64];
|
||||
for (int y = 0; y < m_info.BlocksHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < m_info.BlocksWidth; ++x)
|
||||
{
|
||||
byte ctl = m_control[x + m_info.BlocksWidth * (m_info.BlocksHeight - y - 1)];
|
||||
if (0x80 == ctl)
|
||||
{
|
||||
for (int i = 0; i < block.Length; ++i)
|
||||
block[i] = 0;
|
||||
}
|
||||
else if (0x81 == ctl)
|
||||
{
|
||||
for (int i = 0; i < block.Length; ++i)
|
||||
block[i] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte pixel = m_input.ReadByte();
|
||||
DecodeBlock (ctl, pixel, block);
|
||||
}
|
||||
int dst = 8 * (x + 8 * y * m_info.BlocksWidth);
|
||||
int src = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
Buffer.BlockCopy (block, src, m_output, dst, 8);
|
||||
dst += m_src_stride;
|
||||
src += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeBlock (int control, byte pixel, byte[] block)
|
||||
{
|
||||
int dst = 0;
|
||||
switch (control)
|
||||
{
|
||||
case 0:
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
block[dst++] = Values2bit[b >> 6];
|
||||
block[dst++] = Values2bit[(b >> 4) & 3];
|
||||
block[dst++] = Values2bit[(b >> 2) & 3];
|
||||
block[dst++] = Values2bit[b & 3];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
block[dst++] = Values4bit[b >> 4];
|
||||
block[dst++] = Values4bit[b & 0xF];
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int b1 = m_input.ReadByte();
|
||||
int b2 = m_input.ReadByte();
|
||||
int b3 = m_input.ReadByte();
|
||||
int b4 = (b3 >> 6) | ((b2 >> 4) & 0xC) | ((b1 >> 2) & 0x30);
|
||||
block[dst++] = Values6bit[b1 & 0x3F];
|
||||
block[dst++] = Values6bit[b2 & 0x3F];
|
||||
block[dst++] = Values6bit[b3 & 0x3F];
|
||||
block[dst++] = Values6bit[b4];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (64 != m_input.Read (block, 0, 64))
|
||||
throw new EndOfStreamException();
|
||||
break;
|
||||
}
|
||||
int v32 = pixel + block[27];
|
||||
block[27] = (byte)v32;
|
||||
block[26] += (byte)v32;
|
||||
block[25] += block[26];
|
||||
block[24] += block[25];
|
||||
int v34 = v32 + block[18];
|
||||
block[19] += (byte)v32;
|
||||
block[18] = (byte)v34;
|
||||
block[17] += (byte)v34;
|
||||
block[16] += block[17];
|
||||
int v36 = v34 + block[9];
|
||||
block[11] += block[19];
|
||||
block[10] += (byte)v34;
|
||||
block[9] = (byte)v36;
|
||||
block[8] += (byte)v36;
|
||||
block[3] += block[11];
|
||||
block[2] += block[10];
|
||||
block[1] += (byte)v36;
|
||||
block[0] += (byte)v36;
|
||||
int v39 = pixel + block[28];
|
||||
block[28] = (byte)v39;
|
||||
block[29] += (byte)v39;
|
||||
block[30] += block[29];
|
||||
block[31] += block[30];
|
||||
int v41 = v39 + block[21];
|
||||
block[20] += (byte)v39;
|
||||
block[21] = (byte)v41;
|
||||
block[22] += (byte)v41;
|
||||
block[23] += block[22];
|
||||
int v43 = v41 + block[14];
|
||||
block[12] += block[20];
|
||||
block[13] += (byte)v41;
|
||||
block[14] = (byte)v43;
|
||||
block[15] += (byte)v43;
|
||||
block[4] += block[12];
|
||||
block[5] += block[13];
|
||||
block[6] += (byte)v43;
|
||||
block[7] += (byte)v43;
|
||||
int v46 = pixel + block[35];
|
||||
block[35] = (byte)v46;
|
||||
block[34] += (byte)v46;
|
||||
block[33] += block[34];
|
||||
block[32] += block[33];
|
||||
int v48 = v46 + block[42];
|
||||
block[43] += (byte)v46;
|
||||
block[42] = (byte)v48;
|
||||
block[41] += (byte)v48;
|
||||
block[40] += block[41];
|
||||
int v50 = v48 + block[49];
|
||||
block[51] += block[43];
|
||||
block[50] += (byte)v48;
|
||||
block[49] = (byte)v50;
|
||||
block[48] += (byte)v50;
|
||||
block[59] += block[51];
|
||||
block[58] += block[50];
|
||||
block[57] += (byte)v50;
|
||||
block[56] += (byte)v50;
|
||||
int v53 = pixel + block[36];
|
||||
block[36] = (byte)v53;
|
||||
block[37] += (byte)v53;
|
||||
block[38] += block[37];
|
||||
block[39] += block[38];
|
||||
int v56 = v53 + block[45];
|
||||
block[44] += (byte)v53;
|
||||
block[45] = (byte)v56;
|
||||
block[46] += (byte)v56;
|
||||
block[47] += block[46];
|
||||
int v58 = v56 + block[54];
|
||||
block[52] += block[44];
|
||||
block[53] += (byte)v56;
|
||||
block[54] = (byte)v58;
|
||||
block[55] += (byte)v58;
|
||||
block[60] += block[52];
|
||||
block[61] += block[53];
|
||||
block[62] += (byte)v58;
|
||||
block[63] += (byte)v58;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
@@ -60,7 +60,7 @@ namespace GameRes.Formats.TopCat
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
private static IResource DetectFileType (ArcView file, long offset)
|
||||
new private static IResource DetectFileType (ArcView file, long offset)
|
||||
{
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
return FormatCatalog.Instance.LookupSignature (signature).FirstOrDefault();
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
//! \file ArcCGF.cs
|
||||
//! \date Tue Feb 02 00:47:55 2016
|
||||
//! \brief route2 engine CG archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Triangle
|
||||
{
|
||||
internal class CgfEntry : Entry
|
||||
{
|
||||
public uint Flags;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CgfOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CGF"; } }
|
||||
public override string Description { get { return "route2 engine CG archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count) || file.MaxOffset >= ~0xC0000000)
|
||||
return null;
|
||||
uint offset1 = file.View.ReadUInt32 (0x14);
|
||||
uint offset2 = file.View.ReadUInt32 (0x20);
|
||||
uint entry_size, next_offset;
|
||||
if (4+(uint)count*0x14 == (offset1 & ~0xC0000000))
|
||||
{
|
||||
entry_size = 0x14;
|
||||
next_offset = offset1;
|
||||
}
|
||||
else if (4+(uint)count*0x20 == (offset2 & ~0xC0000000))
|
||||
{
|
||||
entry_size = 0x20;
|
||||
next_offset = offset2;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
|
||||
uint index_size = entry_size * (uint)count;
|
||||
if (index_size > file.View.Reserve (4, index_size))
|
||||
return null;
|
||||
|
||||
uint index_offset = 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, entry_size-4);
|
||||
uint flags = next_offset >> 30;
|
||||
Entry entry;
|
||||
if (1 == flags || name.EndsWith (".iaf", StringComparison.InvariantCultureIgnoreCase))
|
||||
entry = new Entry();
|
||||
else
|
||||
entry = new CgfEntry { Flags = flags };
|
||||
entry.Name = name;
|
||||
entry.Type = "image";
|
||||
entry.Offset = next_offset & ~0xC0000000;
|
||||
|
||||
index_offset += entry_size;
|
||||
next_offset = i+1 == count ? (uint)file.MaxOffset : file.View.ReadUInt32 (index_offset+entry_size-4);
|
||||
if (next_offset < entry.Offset)
|
||||
return null;
|
||||
entry.Size = (next_offset & ~0xC0000000) - (uint)entry.Offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var cent = entry as CgfEntry;
|
||||
if (null == cent)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var offset = entry.Offset;
|
||||
var header = new byte[12];
|
||||
if (2 == cent.Flags)
|
||||
{
|
||||
arc.File.View.Read (offset, header, 0, 8);
|
||||
offset += 0x10;
|
||||
}
|
||||
uint packed_size = arc.File.View.ReadUInt32 (offset);
|
||||
arc.File.View.Read (offset+4, header, 8, 4);
|
||||
var input = arc.File.CreateStream (offset+8, packed_size);
|
||||
return new PrefixStream (header, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
//! \file ArcIAF.cs
|
||||
//! \date Thu Feb 04 03:18:26 2016
|
||||
//! \brief route2 engine multi-frame image.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Triangle
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class IafOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "IAF/MULTI"; } }
|
||||
public override string Description { get { return "route2 engine multi-frame image"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public IafOpener ()
|
||||
{
|
||||
Extensions = new string[] { "iaf" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.EndsWith (".iaf", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| file.MaxOffset < 0x20)
|
||||
return null;
|
||||
uint size = file.View.ReadUInt32 (1);
|
||||
if (size >= file.MaxOffset || size+0x19 >= file.MaxOffset)
|
||||
return null;
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
long current_offset = 0;
|
||||
var dir = new List<Entry>();
|
||||
while (current_offset < file.MaxOffset)
|
||||
{
|
||||
uint packed_size = file.View.ReadUInt32 (current_offset+1);
|
||||
if (0 == packed_size || current_offset+packed_size+0x19 > file.MaxOffset)
|
||||
return null;
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D3}.IAF", base_name, dir.Count),
|
||||
Type = "image",
|
||||
Offset = current_offset,
|
||||
Size = packed_size + 0x19,
|
||||
};
|
||||
dir.Add (entry);
|
||||
current_offset += entry.Size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,20 +76,46 @@ namespace GameRes.Formats.Triangle
|
||||
if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096)
|
||||
return null;
|
||||
int pack_type = (unpacked_size >> 30) & 3;
|
||||
if (3 == pack_type)
|
||||
return null;
|
||||
unpacked_size &= (int)~0xC0000000;
|
||||
stream.Position = data_offset;
|
||||
byte[] bmp = UnpackBitmap (stream, pack_type, packed_size, 0x26);
|
||||
byte[] bmp;
|
||||
if (0 == pack_type)
|
||||
{
|
||||
using (var reader = new LzssReader (stream, (int)stream.Length-12, 0x26))
|
||||
{
|
||||
reader.Unpack();
|
||||
bmp = reader.Data;
|
||||
}
|
||||
}
|
||||
else if (2 == pack_type)
|
||||
{
|
||||
using (var reader = new RleReader (stream, (int)stream.Length-12, 0x26))
|
||||
{
|
||||
reader.Unpack();
|
||||
bmp = reader.Data;
|
||||
}
|
||||
}
|
||||
else if (1 == pack_type)
|
||||
{
|
||||
bmp = new byte[0x26];
|
||||
stream.Read (bmp, 0, bmp.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (bmp[0] != 'B' && bmp[0] != 'C' || bmp[1] != 'M')
|
||||
return null;
|
||||
int width = LittleEndian.ToInt32 (bmp, 0x12);
|
||||
int height = LittleEndian.ToInt32 (bmp, 0x16);
|
||||
int bpp = LittleEndian.ToInt16 (bmp, 0x1c);
|
||||
return new IafMetaData
|
||||
{
|
||||
Width = LittleEndian.ToUInt32 (bmp, 0x12),
|
||||
Height = LittleEndian.ToUInt32 (bmp, 0x16),
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
OffsetX = x,
|
||||
OffsetY = y,
|
||||
BPP = LittleEndian.ToInt16 (bmp, 0x1c),
|
||||
BPP = bpp,
|
||||
DataOffset = data_offset,
|
||||
PackedSize = packed_size,
|
||||
UnpackedSize = unpacked_size,
|
||||
@@ -99,16 +125,41 @@ namespace GameRes.Formats.Triangle
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var meta = (IafMetaData)info;
|
||||
var meta = info as IafMetaData;
|
||||
if (null == meta)
|
||||
throw new ArgumentException ("IafFormat.Read should be supplied with IafMetaData", "info");
|
||||
|
||||
stream.Position = meta.DataOffset;
|
||||
var bitmap = UnpackBitmap (stream, meta.PackType, meta.PackedSize, meta.UnpackedSize);
|
||||
byte[] bitmap;
|
||||
if (2 == meta.PackType)
|
||||
{
|
||||
using (var reader = new RleReader (stream, meta.PackedSize, meta.UnpackedSize))
|
||||
{
|
||||
reader.Unpack();
|
||||
bitmap = reader.Data;
|
||||
}
|
||||
}
|
||||
else if (0 == meta.PackType)
|
||||
{
|
||||
using (var reader = new LzssReader (stream, meta.PackedSize, meta.UnpackedSize))
|
||||
{
|
||||
reader.Unpack();
|
||||
bitmap = reader.Data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap = new byte[meta.UnpackedSize];
|
||||
if (bitmap.Length != stream.Read (bitmap, 0, bitmap.Length))
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
}
|
||||
if ('C' == bitmap[0])
|
||||
{
|
||||
bitmap[0] = (byte)'B';
|
||||
if (info.BPP > 8)
|
||||
bitmap = ConvertCM (bitmap, (int)info.Width, (int)info.Height, info.BPP);
|
||||
}
|
||||
if (info.BPP >= 24) // currently alpha channel could be applied to 24+bpp bitmaps only
|
||||
if (meta.BPP >= 24) // currently alpha channel could be applied to 24+bpp bitmaps only
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -121,7 +172,7 @@ namespace GameRes.Formats.Triangle
|
||||
uint alpha_width = LittleEndian.ToUInt32 (bitmap, bmp_size+0x12);
|
||||
uint alpha_height = LittleEndian.ToUInt32 (bitmap, bmp_size+0x16);
|
||||
if (info.Width == alpha_width && info.Height == alpha_height)
|
||||
return BitmapWithAlphaChannel (info, bitmap, bmp_size);
|
||||
return BitmapWithAlphaChannel (meta, bitmap, bmp_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,35 +186,6 @@ namespace GameRes.Formats.Triangle
|
||||
return base.Read (bmp, info);
|
||||
}
|
||||
|
||||
internal static byte[] UnpackBitmap (Stream stream, int pack_type, int packed_size, int unpacked_size)
|
||||
{
|
||||
if (2 == pack_type)
|
||||
{
|
||||
using (var reader = new RleReader (stream, packed_size, unpacked_size))
|
||||
{
|
||||
reader.Unpack();
|
||||
return reader.Data;
|
||||
}
|
||||
}
|
||||
else if (0 == pack_type)
|
||||
{
|
||||
using (var reader = new LzssReader (stream, packed_size, unpacked_size))
|
||||
{
|
||||
reader.Unpack();
|
||||
return reader.Data;
|
||||
}
|
||||
}
|
||||
else if (1 == pack_type)
|
||||
{
|
||||
var bitmap = new byte[unpacked_size];
|
||||
if (bitmap.Length != stream.Read (bitmap, 0, bitmap.Length))
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
return bitmap;
|
||||
}
|
||||
else
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("IafFormat.Write not implemented");
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace GameRes.Formats.Xuse
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
byte[] title = file.View.ReadBytes (6, 0x40);
|
||||
if (0x40 != title.Length)
|
||||
byte[] title = new byte[0x40];
|
||||
if (0x40 != file.View.Read (6, title, 0, 0x40))
|
||||
return null;
|
||||
int title_length = Array.IndexOf<byte> (title, 0);
|
||||
if (-1 == title_length)
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace GARbro.GUI
|
||||
var filters = new StringBuilder();
|
||||
|
||||
var format = this.ArchiveFormat.SelectedItem as ArchiveFormat;
|
||||
if (null != format && format.Extensions.Any())
|
||||
if (null != format)
|
||||
{
|
||||
var patterns = format.Extensions.Select (ext => "*."+ext);
|
||||
filters.Append (format.Description);
|
||||
|
||||
@@ -77,23 +77,27 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualBasic" />
|
||||
<Reference Include="Microsoft.WindowsAPICodePack">
|
||||
<HintPath>..\packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
|
||||
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.WindowsAPICodePack.Shell">
|
||||
<HintPath>..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
|
||||
<Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NAudio, Version=1.7.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\NAudio.1.7.3\lib\net35\NAudio.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Ookii.Dialogs.Wpf">
|
||||
<HintPath>..\packages\Ookii.Dialogs.1.0\lib\net35\Ookii.Dialogs.Wpf.dll</HintPath>
|
||||
<Reference Include="Ookii.Dialogs.Wpf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c15020868fd6249, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\Ookii.Dialogs.1.0\lib\net35\Ookii.Dialogs.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Windows.Controls.Input.Toolkit">
|
||||
<HintPath>..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll</HintPath>
|
||||
<Reference Include="System.Windows.Controls.Input.Toolkit, Version=3.5.40128.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -106,8 +110,9 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="WPFToolkit">
|
||||
<HintPath>..\packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll</HintPath>
|
||||
<Reference Include="WPFToolkit, Version=3.5.40128.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -285,7 +290,7 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName) "$(SolutionDir)\"
|
||||
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)
|
||||
exit 0</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
@@ -298,4 +303,4 @@ exit 0</PreBuildEvent>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace GARbro.GUI
|
||||
string source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant();
|
||||
if (m_image_format.Extensions.Any (ext => ext == source_ext))
|
||||
return;
|
||||
string target_ext = m_image_format.Extensions.FirstOrDefault();
|
||||
string target_ext = m_image_format.Extensions.First();
|
||||
string target_name = Path.ChangeExtension (filename, target_ext);
|
||||
using (var file = File.OpenRead (filename))
|
||||
{
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace GARbro.GUI
|
||||
if (null == src_format)
|
||||
throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpretImage, entry.Name));
|
||||
file.Position = 0;
|
||||
string target_ext = target_format.Extensions.FirstOrDefault() ?? "";
|
||||
string target_ext = target_format.Extensions.First();
|
||||
string outname = FindUniqueFileName (entry.Name, target_ext);
|
||||
if (src_format.Item1 == target_format)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ using System.Windows;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany ("mørkt")]
|
||||
[assembly: AssemblyProduct("GARbro.GUI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014-2016 mørkt")]
|
||||
[assembly: AssemblyCopyright ("Copyright © 2014-2015 mørkt")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -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.2.13.1024")]
|
||||
[assembly: AssemblyFileVersion ("1.2.13.1024")]
|
||||
[assembly: AssemblyVersion ("1.2.12.0")]
|
||||
[assembly: AssemblyFileVersion ("1.2.12.0")]
|
||||
|
||||
Binary file not shown.
@@ -61,6 +61,7 @@
|
||||
<Compile Include="FileSystem.cs" />
|
||||
<Compile Include="FormatCatalog.cs" />
|
||||
<Compile Include="GameRes.cs" />
|
||||
<Compile Include="GameTitle.cs" />
|
||||
<Compile Include="Image.cs" />
|
||||
<Compile Include="ImageBMP.cs" />
|
||||
<Compile Include="ImageJPEG.cs" />
|
||||
|
||||
70
GameRes/GameTitle.cs
Normal file
70
GameRes/GameTitle.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
//! \file GameTitle.cs
|
||||
//! \date Wed Jan 20 13:07:51 2016
|
||||
//! \brief class for game titles localization.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace GameRes
|
||||
{
|
||||
public class GameTitle : System.IEquatable<GameTitle>
|
||||
{
|
||||
public string EN { get; private set; }
|
||||
public string Original { get; private set; }
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
if ("ja" == CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)
|
||||
return Original;
|
||||
else
|
||||
return EN;
|
||||
}
|
||||
}
|
||||
|
||||
public GameTitle (string en) : this (en, en)
|
||||
{
|
||||
}
|
||||
|
||||
public GameTitle (string en, string jp)
|
||||
{
|
||||
EN = en;
|
||||
Original = jp;
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return Original.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
return Equals (obj as GameTitle);
|
||||
}
|
||||
|
||||
public bool Equals (GameTitle other)
|
||||
{
|
||||
return other != null && other.Original == this.Original;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.14.139")]
|
||||
[assembly: AssemblyFileVersion ("1.2.14.139")]
|
||||
[assembly: AssemblyVersion ("1.2.13.137")]
|
||||
[assembly: AssemblyFileVersion ("1.2.13.137")]
|
||||
|
||||
@@ -20,22 +20,22 @@ sub match_version {
|
||||
return $_[0] =~ /^(\d+)\.(\d+)(?:\.(\d+)\.(\d+))?/;
|
||||
}
|
||||
|
||||
if ($#ARGV < 1) {
|
||||
print "usage: inc-revision.pl PROJECT-FILE CONFIG [ROOT-DIR]\n";
|
||||
unless (1 == $#ARGV) {
|
||||
print "usage: inc-revision.pl PROJECT-FILE CONFIG\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my ($project_path, $config, $root_dir) = @ARGV;
|
||||
my ($project_path, $config) = @ARGV;
|
||||
my $project_dir = dirname ($project_path);
|
||||
$root_dir //= $project_dir;
|
||||
my $project_file = basename ($project_path);
|
||||
my $is_release = 'release' eq lc $config;
|
||||
chdir $root_dir or die "$root_dir: $!\n";
|
||||
chdir $project_dir or die "$project_dir: $!\n";
|
||||
|
||||
my $git_exe = get_git_exe;
|
||||
my $prop_dir = File::Spec->catfile ('.', 'Properties');
|
||||
my $assembly_info = File::Spec->catfile ($prop_dir, 'AssemblyInfo.cs');
|
||||
my $revision = `$git_exe rev-list HEAD --count .`;
|
||||
die "git.exe failed\n" if $? != 0;
|
||||
my $prop_dir = File::Spec->catfile ($project_dir, 'Properties');
|
||||
my $assembly_info = File::Spec->catfile ($prop_dir, 'AssemblyInfo.cs');
|
||||
chomp $revision;
|
||||
|
||||
my $version_changed = 0;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<repositories>
|
||||
<repository path="..\ArcFormats\packages.config" />
|
||||
<repository path="..\GameRes\packages.config" />
|
||||
<repository path="..\GUI\packages.config" />
|
||||
</repositories>
|
||||
@@ -122,10 +122,9 @@ Sweet Pool<br/>
|
||||
Zoku Satsuriku no Django<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.npa</td><td>-</td><td>Yes</td><td>Nitro+</td><td>Steins;Gate</td></tr>
|
||||
<tr><td>*.pak</td><td><tt>\002\000\000\000</tt><br/><tt>\003\000\000\000</tt></td><td>No</td><td>Nitro+<br/>CoreMoreco</td><td>
|
||||
<tr><td>*.pak</td><td><tt>\002\000\000\000</tt><br/><tt>\003\000\000\000</tt></td><td>No</td><td>Nitro+</td><td>
|
||||
Hanachirasu<br/>
|
||||
Jingai Makyou<br/>
|
||||
Chibi Mama<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.nsa<br/>*.sar</td><td>-</td><td>Yes<a href="#note-1" class="footnote">1</a></td><td>NScripter</td><td>
|
||||
Binary Pot<br/>
|
||||
@@ -153,7 +152,6 @@ Folklore Jam<br/>
|
||||
I/O<br/>
|
||||
Natsuiro Kouen ~Denpatou no Shita de Ai wo Kataru~<br/>
|
||||
Onegai O-Hoshi-sama<br/>
|
||||
Toriko Hime ~Hakudaku Mamire no Reijou~<br/>
|
||||
Tsuma Youji<br/>
|
||||
Tsuma Youji 2<br/>
|
||||
Yume Miru Kusuri<br/>
|
||||
@@ -182,12 +180,10 @@ Hanafubuki ~Sennen no Koi o Shimashita~<br/>
|
||||
Haruiro ☆ Communication ♪<br/>
|
||||
Hime to Majin to Koi Suru Tamashii<br/>
|
||||
Imouto Style<br/>
|
||||
Mayoeru Futari to Sekai no Subete<br/>
|
||||
Natsupochi<br/>
|
||||
Nidaime wa ☆ Mahou Shoujo<br/>
|
||||
Nuki Doki!<br/>
|
||||
Okiba ga Nai!<br/>
|
||||
Oku-sama wa Moto Yariman<br/>
|
||||
Ore no Saimin Fantasia<br/>
|
||||
Riding Incubus<br/>
|
||||
Seirei Tenshou<br/>
|
||||
@@ -218,7 +214,6 @@ Ikusa Otome Valkyrie<br/>
|
||||
Onsoku Hishou Sonic Mercedes<br/>
|
||||
Sakura Machizaka Stories vol.1<br/>
|
||||
Sakura Machizaka Stories vol.2<br/>
|
||||
Shoujo Senki Soul Eater<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.prs</td><td><tt>YB</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.way</td><td><tt>WADY</tt></td><td>No</td></tr>
|
||||
@@ -242,7 +237,6 @@ Hensai Keikaku<br/>
|
||||
Himawari Oka Sougou Byouin e Youkoso<br/>
|
||||
Inen no Yakata<br/>
|
||||
Inran OL Sawatari Tokiko<br/>
|
||||
Itsuwari no Kyoushitsu<br/>
|
||||
Kunoichi Kikyou ~Gensou Kannou Emaki~<br/>
|
||||
Momo x Momi<br/>
|
||||
Michibikareshi Mono-tachi no Rakuen ~BEDLAM~<br/>
|
||||
@@ -364,7 +358,6 @@ Dungeon Crusaderz 2 ~Eigou no Rakudo~<br/>
|
||||
Onna Kyoushi<br/>
|
||||
Magical Witch Academy<br/>
|
||||
Medorei ~Okasareta Houkago~<br/>
|
||||
Nerawareta Megami Tenshi Angeltia<br/>
|
||||
Saishuu Chikan Densha<br/>
|
||||
Serina<br/>
|
||||
Ura Nyuugaku ~Ineki ni Nureta Kyoukasho~<br/>
|
||||
@@ -384,19 +377,15 @@ Ryoujoku Gojuusou<br/>
|
||||
<tr class="odd"><td>*.eog</td><td><tt>CRM</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.zbm<br/>*.cwl</td><td><tt>SZDD</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pack</td><td><tt>FilePackVer1.0</tt><br/><tt>FilePackVer2.0</tt><br/><tt>FilePackVer3.0</tt></td><td>No</td><td rowspan="3">QLIE</td><td rowspan="3">
|
||||
Makai Tenshi Djibril -episode 3-<br/>
|
||||
Mehime no Toriko<br/>
|
||||
Nanatsu no Fushigi no Owaru Toki<br/>
|
||||
Soshite Ashita no Sekai yori<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.b</td><td><tt>ABMP7</tt><br/><tt>abmp10</tt><br/><tt>abmp11</tt></td><td>No</td></tr>
|
||||
<tr><td>*.png</td><td><tt>DPNG</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.dat</td><td>-</td><td>No</td><td rowspan="4">Circus</td><td rowspan="4">
|
||||
Infantaria XP<br/>
|
||||
<tr class="odd"><td>*.dat</td><td>-</td><td>No</td><td rowspan="3">Circus</td><td rowspan="3">
|
||||
Maid no Yakata ~Zetsubou Hen~<br/>
|
||||
RPG Gakuen<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.pck</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.crx</td><td><tt>CRXG</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.pcm</td><td><tt>XPCM</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pak</td><td><tt>CHERRY PACK 2.0</tt><br/><tt>CHERRY PACK 3.0</tt><br/>-</td><td>No</td><td>Cherry</td><td>
|
||||
@@ -459,13 +448,11 @@ Gokudou no Hanayome<br/>
|
||||
Knight Carnival!<br/>
|
||||
Seal Princess<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.sud</td><td>-</td><td>No</td><td rowspan="3">Triangle</td><td rowspan="3">
|
||||
<tr><td>*.sud</td><td>-</td><td>No</td><td rowspan="2">Triangle</td><td rowspan="2">
|
||||
Kourin Tenshi En Ciel Rena<br/>
|
||||
Mamagoto<br/>
|
||||
Vampire Crusaders<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.iaf</td><td>-</td><td>No</td></tr>
|
||||
<tr><td>*.cgf</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.bin+*.pak</td><td><tt>hed</tt></td><td>No</td><td rowspan="2">elf</td><td rowspan="2">Adult Video King</td></tr>
|
||||
<tr class="odd"><td>*.hip<br/>*.hiz<br/></td><td><tt>hip</tt><br/><tt>hiz</tt></td><td>No</td></tr>
|
||||
<tr><td>*.gpk+*.gtb<br/>*.vpk+*.vtb</td><td>-</td><td>No</td><td rowspan="3">Black Cyc</td><td rowspan="3">
|
||||
@@ -621,7 +608,6 @@ AstralAir no Shiroki Towa<br/>
|
||||
<tr><td>*.hzc</td><td><tt>hzc1</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.bin</td><td><tt>ESC-ARC1</tt><br/><tt>ESC-ARC2</tt></td><td>No</td><td>Escu:de</td><td>
|
||||
Otome Renshin Prister<br/>
|
||||
Suisei Tenshi Primaveil Zwei<br/>
|
||||
Wondering Repair!<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.pac</td><td>-</td><td>No</td><td>Tmr-Hiro ADV System</td><td>
|
||||
@@ -645,23 +631,6 @@ Eien no Aselia -The Spirit of Eternity Sword-
|
||||
<tr><td>*.dat</td><td><tt>DAF2</tt></td><td>No</td><td>DenSDK</td><td>
|
||||
Ayakashi H<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.arc</td><td><tt>LINK3</tt></td><td>No</td><td rowspan="3">KaGuYa</td><td rowspan="3">
|
||||
Dokidoki Onee-san<br/>
|
||||
Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.alp</td><td><tt>AP-2</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.anm</td><td><tt>AN00</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pcs</td><td><tt>PCCS</tt></td><td>No</td><td>C's ware</td><td>
|
||||
Mikan<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.052<br/>*.055</td><td><tt>VAFSH</tt></td><td>No</td><td rowspan="2">Softpal</td><td rowspan="2">
|
||||
Komorebi ni Yureru Tamashii no Koe<br/>
|
||||
Komokyun!! ~Heart ni Yureru Tamashi no Fandisc~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*</td><td><tt>BPIC</tt></td><td>No</td></tr>
|
||||
<tr><td>*</td><td><tt>NNNN</tt></td><td>No</td><td>Moko Pro</td><td>
|
||||
Houmon Hanbai ~Otona no Omocha Irimasen ka?~<br/>
|
||||
</td></tr>
|
||||
</table>
|
||||
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user