Compare commits

..

2 Commits

Author SHA1 Message Date
morkt
8ea25af83d added GameTitle class. 2016-01-22 14:06:04 +04:00
morkt
1603b45415 Merge remote-tracking branch 'refs/remotes/origin/master' into game-titles 2016-01-22 14:04:56 +04:00
43 changed files with 883 additions and 3219 deletions

View File

@@ -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)

View File

@@ -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">

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);

View 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;

View File

@@ -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];
}
}

View File

@@ -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");

View File

@@ -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))
{

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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");
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);

View File

@@ -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")]

View File

@@ -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"; } }
}
}

View File

@@ -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);
}

View File

@@ -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
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

Binary file not shown.

View File

@@ -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();

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");

View File

@@ -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)

View File

@@ -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);

View File

@@ -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>

View File

@@ -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))
{

View File

@@ -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)
{

View File

@@ -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")]

View File

Binary file not shown.

View File

@@ -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
View 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;
}
}
}

View File

@@ -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")]

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>