mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 05:35:34 +08:00
(Legacy): bunch of formats.
(GSX): K5 archives + K4 images. (HyperWorks): G images. (IDA): better support packed entries. (Logg): ARF, MBM archives, FRM images. (Omi): DAT archives. (Rare): X archives. (RHSS): 'CRG' archives. (SplushWave): better SWG images support.
This commit is contained in:
parent
8b23273fa9
commit
419a5f4e31
@ -26,7 +26,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
// [000225][Light Plan] My Fairink Yousei Byakuya Monogatari
|
||||
// [000225][Light Plan] My Fair Link Yousei Byakuya Monogatari
|
||||
|
||||
namespace GameRes.Formats.Gsx
|
||||
{
|
||||
|
70
Legacy/Gsx/ArcK5.cs
Normal file
70
Legacy/Gsx/ArcK5.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//! \file ArcK5.cs
|
||||
//! \date 2023 Aug 27
|
||||
//! \brief GSX engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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 System.Text;
|
||||
|
||||
namespace GameRes.Formats.Gsx
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class K5Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "K5";
|
||||
public override string Description => "GSX engine resource archive";
|
||||
public override uint Signature => 0x01354B; // 'K5'
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public K5Opener ()
|
||||
{
|
||||
ContainedFormats = new[] { "K4", "OGG" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_offset = file.View.ReadUInt32 (8);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var dir_name = file.View.ReadString (index_offset, 0x80, Encoding.Unicode);
|
||||
var name = file.View.ReadString (index_offset+0x80, 0x40, Encoding.Unicode);
|
||||
name = Path.Combine (dir_name, name);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0xC8);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0xCC);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x100;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
//! \file ImageK4.cs
|
||||
//! \date 2019 Feb 07
|
||||
//! \brief Toyo GSX image format.
|
||||
//! \date 2023 Aug 27
|
||||
//! \brief GSX engine image format.
|
||||
//
|
||||
// Copyright (C) 2019 by morkt
|
||||
// Copyright (C) 2023 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
|
||||
@ -23,6 +23,8 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
@ -33,16 +35,16 @@ namespace GameRes.Formats.Gsx
|
||||
{
|
||||
internal class K4MetaData : ImageMetaData
|
||||
{
|
||||
public bool HasAlpha;
|
||||
public byte AlphaMode;
|
||||
public int FrameCount;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class K4Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "K4"; } }
|
||||
public override string Description { get { return "Toyo GSX image format"; } }
|
||||
public override uint Signature { get { return 0x0201344B; } } // 'K4'
|
||||
public override string Tag => "K4";
|
||||
public override string Description => "GSX engine image format";
|
||||
public override uint Signature => 0x0201344B; // 'K4'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
@ -51,20 +53,22 @@ namespace GameRes.Formats.Gsx
|
||||
return null;
|
||||
if (header[2] != 1 || header[3] != 2)
|
||||
return null;
|
||||
int frame_count = header.ToInt16 (0xC);
|
||||
if (frame_count <= 0)
|
||||
return null;
|
||||
return new K4MetaData {
|
||||
Width = header.ToUInt16 (4),
|
||||
Height = header.ToUInt16 (6),
|
||||
BPP = header[0xF],
|
||||
HasAlpha = header[0xB] != 0,
|
||||
FrameCount = header.ToUInt16 (0xC),
|
||||
AlphaMode = header[0xB],
|
||||
FrameCount = frame_count,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (K4MetaData)info;
|
||||
|
||||
return ImageData.Create (info, format, palette, pixels);
|
||||
var reader = new K4Reader (file, (K4MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -72,4 +76,190 @@ namespace GameRes.Formats.Gsx
|
||||
throw new System.NotImplementedException ("K4Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class K4Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
K4MetaData m_info;
|
||||
|
||||
public K4Reader (IBinaryStream input, K4MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
int m_stride;
|
||||
int m_pixel_size;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
uint base_offset = 0x30;
|
||||
m_input.Position = base_offset;
|
||||
m_info.Width = m_input.ReadUInt16();
|
||||
m_info.Height = m_input.ReadUInt16();
|
||||
m_input.Seek (8, SeekOrigin.Current);
|
||||
int bpp = m_input.ReadUInt16();
|
||||
int flags = m_input.ReadUInt16();
|
||||
m_input.Seek (4, SeekOrigin.Current);
|
||||
uint alpha_pos = m_input.ReadUInt32();
|
||||
m_input.Seek (12, SeekOrigin.Current);
|
||||
int ctl_length = m_input.ReadInt32();
|
||||
|
||||
m_pixel_size = bpp / 8;
|
||||
m_stride = (m_info.iWidth * m_pixel_size + 3) & ~3;
|
||||
var pixels = new byte[m_info.iHeight * m_stride];
|
||||
int dst = 0;
|
||||
bool do_delta = (flags & 1) != 0;
|
||||
var control_bytes = m_input.ReadBytes (ctl_length - 0x10);
|
||||
using (var mem = new MemoryStream (control_bytes))
|
||||
using (var ctl = new MsbBitStream (mem))
|
||||
using (var data = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
while (dst < pixels.Length)
|
||||
{
|
||||
int b = ctl.GetNextBit();
|
||||
if (-1 == b)
|
||||
break;
|
||||
if (b != 0)
|
||||
{
|
||||
if (!do_delta)
|
||||
{
|
||||
pixels[dst++] = (byte)data.GetBits (8);
|
||||
}
|
||||
else if (dst >= m_pixel_size)
|
||||
{
|
||||
pixels[dst] = (byte)(pixels[dst - m_pixel_size] + data.GetBits (9) + 1);
|
||||
++dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels[dst++] = (byte)data.GetBits (9);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int pos, count;
|
||||
if (ctl.GetNextBit() != 0)
|
||||
{
|
||||
pos = data.GetBits (14);
|
||||
count = data.GetBits (4) + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = data.GetBits (9);
|
||||
count = data.GetBits (3) + 2;
|
||||
}
|
||||
int src = dst - pos - 1;
|
||||
count = Math.Min (count, pixels.Length - dst);
|
||||
if (!do_delta || dst < m_pixel_size)
|
||||
{
|
||||
Binary.CopyOverlapped (pixels, src, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
pixels[dst++] = (byte)(pixels[src] + pixels[src + pos - m_pixel_size + 1] - pixels[src - m_pixel_size]);
|
||||
++src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 == alpha_pos)
|
||||
{
|
||||
if (24 == bpp)
|
||||
return ImageData.CreateFlipped (m_info, PixelFormats.Bgr24, null, pixels, m_stride);
|
||||
else
|
||||
return ImageData.CreateFlipped (m_info, PixelFormats.Bgr32, null, pixels, m_stride);
|
||||
}
|
||||
if (0xFF == m_info.AlphaMode)
|
||||
pixels = UnpackAlphaFF (alpha_pos + base_offset, pixels);
|
||||
else if (0xFE == m_info.AlphaMode)
|
||||
pixels = UnpackAlphaFE (alpha_pos + base_offset, pixels);
|
||||
else
|
||||
throw new NotSupportedException (string.Format ("Not supported alpha channel mode 0x{0:X2}", m_info.AlphaMode));
|
||||
m_stride = m_info.iWidth * 4;
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, m_stride);
|
||||
}
|
||||
|
||||
byte[] UnpackAlphaFF (uint alpha_pos, byte[] pixels)
|
||||
{
|
||||
m_input.Position = alpha_pos;
|
||||
var offsets = new int[m_info.iHeight];
|
||||
for (int i = 0; i < offsets.Length; ++i)
|
||||
offsets[i] = m_input.ReadInt32();
|
||||
|
||||
var output = new byte[m_info.iWidth * m_info.iHeight * 4];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; y++)
|
||||
{
|
||||
m_input.Position = alpha_pos + offsets[y];
|
||||
int src = (m_info.iHeight - y - 1) * m_stride;
|
||||
int dst_a = dst + 3;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
output[dst ] = pixels[src ];
|
||||
output[dst+1] = pixels[src+1];
|
||||
output[dst+2] = pixels[src+2];
|
||||
dst += 4;
|
||||
src += m_pixel_size;
|
||||
}
|
||||
for (int x = 0; x < m_info.iWidth; )
|
||||
{
|
||||
byte alpha = m_input.ReadUInt8();
|
||||
int count = m_input.ReadUInt8();
|
||||
count = Math.Min (count, m_info.iWidth - x);
|
||||
x += count;
|
||||
if (alpha > 0)
|
||||
{
|
||||
alpha = (byte)((alpha * 0xFF) >> 7);
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst_a] = alpha;
|
||||
dst_a += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_a += 4 * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
byte[] UnpackAlphaFE (uint alpha_pos, byte[] pixels)
|
||||
{
|
||||
m_input.Position = alpha_pos;
|
||||
var output = new byte[m_info.iWidth * m_info.iHeight * 4];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; y++)
|
||||
{
|
||||
int src = (m_info.iHeight - y - 1) * m_stride;
|
||||
int dst_a = dst + 3;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
output[dst ] = pixels[src ];
|
||||
output[dst+1] = pixels[src+1];
|
||||
output[dst+2] = pixels[src+2];
|
||||
dst += 4;
|
||||
src += m_pixel_size;
|
||||
}
|
||||
for (int x = 0; x < m_info.iWidth; x += 8)
|
||||
{
|
||||
byte alpha = m_input.ReadUInt8();
|
||||
int count = Math.Min (8, m_info.iWidth - x);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
output[dst_a] = (byte)-(alpha & 1);
|
||||
dst_a += 4;
|
||||
alpha >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
421
Legacy/HyperWorks/ImageG.cs
Normal file
421
Legacy/HyperWorks/ImageG.cs
Normal file
@ -0,0 +1,421 @@
|
||||
//! \file ImageG.cs
|
||||
//! \date 2023 Aug 28
|
||||
//! \brief HyperWorks image format.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [951207][Love Gun] ACE OF SPADES
|
||||
// I24 predecessor
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "G";
|
||||
public override string Description => "HyperWorks indexed image format";
|
||||
public override uint Signature => 0x1A477D00;
|
||||
|
||||
public GFormat ()
|
||||
{
|
||||
Signatures = new[] { 0x1A477D00u, 0u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (12);
|
||||
if (header.ToUInt16 (2) != 0x1A47)
|
||||
return null;
|
||||
// not sure if 0x7D00 is a required signature, so rely on filename
|
||||
if (header.ToUInt16 (0) != 0x7D00 && !file.Name.HasExtension (".G"))
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt16 (8),
|
||||
Height = header.ToUInt16 (10),
|
||||
OffsetX = header.ToInt16 (4),
|
||||
OffsetY = header.ToInt16 (6),
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new GReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
int m_stride;
|
||||
byte[] m_palette_data;
|
||||
byte[] m_output;
|
||||
ushort[] m_buffer;
|
||||
int[] m_line_ptr;
|
||||
|
||||
public GReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = (m_info.iWidth * m_info.BPP / 8 + 1) & -2;
|
||||
int s = Math.Max (0x142, m_stride / 2 + 2); // line buffer size
|
||||
m_buffer = new ushort[s * 3 + 1];
|
||||
m_line_ptr = new int[3] { 1, 1 + s, 1 + s*2 };
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x0C;
|
||||
m_palette_data = m_input.ReadBytes (0x30);
|
||||
m_input.Position = 0x40;
|
||||
int width = ((m_info.iWidth + 7) & -8);
|
||||
int rows = ((m_info.iHeight + 1) & -2);
|
||||
m_output = new byte[m_stride * rows];
|
||||
InitColorTable();
|
||||
int blockW = width >> 1;
|
||||
int blockH = rows >> 1;
|
||||
SetupBitReader();
|
||||
for (int y = 0; y < blockH; ++y)
|
||||
{
|
||||
var dst = m_line_ptr[2];
|
||||
m_line_ptr[2] = m_line_ptr[1];
|
||||
m_line_ptr[1] = m_line_ptr[0];
|
||||
m_line_ptr[0] = dst;
|
||||
int x = 0;
|
||||
while (x < blockW)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
m_buffer[dst++] = GetColorFromTable (x);
|
||||
++x;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = ExtractBits (BitTable1);
|
||||
if (count >= 0x40)
|
||||
count += ExtractBits (BitTable1);
|
||||
int idx = ExtractBits (BitTable2) * 2;
|
||||
int src = m_line_ptr[OffTable[idx + 1]];
|
||||
src += OffTable[idx] + x;
|
||||
x += count;
|
||||
while (count --> 0)
|
||||
{
|
||||
m_buffer[dst++] = m_buffer[src++];
|
||||
}
|
||||
}
|
||||
}
|
||||
UnpackRow (y, blockW, m_line_ptr[0]);
|
||||
}
|
||||
var palette = UnpackPalette();
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, m_output, m_stride);
|
||||
}
|
||||
|
||||
void UnpackRow (int y, int width, int buf_pos)
|
||||
{
|
||||
int row1 = m_stride * y * 2;
|
||||
int row2 = row1 + m_stride;
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
ushort v = m_buffer[buf_pos++];
|
||||
ushort v0 = (ushort)((v & 0xF00 | ((v & 0xF000) >> 12)) + 0xA0A);
|
||||
LittleEndian.Pack (v0, m_output, row2);
|
||||
row2 += 2;
|
||||
ushort v1 = (ushort)((((v & 0xF) << 8) | ((v & 0xF0) >> 4)) + 0xA0A);
|
||||
LittleEndian.Pack (v1, m_output, row1);
|
||||
row1 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] g_palIndexes = { 0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 0xC, 0xD, 0xA, 0xB, 0xE, 0xF };
|
||||
|
||||
BitmapPalette UnpackPalette ()
|
||||
{
|
||||
var colors = new Color[42];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int R = m_palette_data[3 * g_palIndexes[i] ]; R |= R << 4;
|
||||
int G = m_palette_data[3 * g_palIndexes[i] + 1]; G |= G << 4;
|
||||
int B = m_palette_data[3 * g_palIndexes[i] + 2]; B |= B << 4;
|
||||
colors[i+10] = Color.FromRgb ((byte)R, (byte)G, (byte)B);
|
||||
|
||||
int b = R & 1;
|
||||
int c = (sbyte)R >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].R = (byte)c;
|
||||
|
||||
b = G & 1;
|
||||
c = (sbyte)G >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].G = (byte)c;
|
||||
|
||||
b = B & 1;
|
||||
c = (sbyte)B >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].B = (byte)c;
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
|
||||
byte[] g_colorTable = new byte[256];
|
||||
|
||||
void InitColorTable ()
|
||||
{
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int j = 0; j < 16; ++j)
|
||||
g_colorTable[dst++] = (byte)((j + i + 1) & 0xF);
|
||||
}
|
||||
|
||||
ushort GetColorFromTable (int x)
|
||||
{
|
||||
ushort b0 = m_buffer[m_line_ptr[1] + x];
|
||||
int n0 = b0 & 0xF;
|
||||
int n1 = (b0 >> 4) & 0xF;
|
||||
int n2 = (b0 >> 8) & 0xF;
|
||||
int n3 = (b0 >> 12) & 0xF;
|
||||
|
||||
ushort b1 = m_buffer[m_line_ptr[1] + x - 1];
|
||||
int m0 = b1 & 0xF;
|
||||
int m1 = (b1 >> 4) & 0xF;
|
||||
int m2 = (b1 >> 8) & 0xF;
|
||||
int m3 = (b1 >> 12) & 0xF;
|
||||
|
||||
ushort b2 = m_buffer[m_line_ptr[0] + x - 1];
|
||||
int p0 = b2 & 0xF;
|
||||
int p1 = (b2 >> 4) & 0xF;
|
||||
int p2 = (b2 >> 8) & 0xF;
|
||||
int p3 = (b2 >> 12) & 0xF;
|
||||
|
||||
int r1 = n1;
|
||||
if (n1 != n3 && (n1 != m1 || n1 != p1))
|
||||
{
|
||||
if (p0 == p1)
|
||||
r1 = p0;
|
||||
else
|
||||
r1 = m2;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r1 = AdjustColorTable (r1);
|
||||
|
||||
int r0 = n0;
|
||||
if (n0 != n2 && (n0 != m0 || n0 != p0))
|
||||
{
|
||||
if (r1 == p1)
|
||||
r0 = p1;
|
||||
else
|
||||
r0 = n3;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r0 = AdjustColorTable (r0);
|
||||
|
||||
int r3 = n3;
|
||||
if (r1 != n3 && (n3 != m3 || n3 != p3))
|
||||
{
|
||||
if (p2 == p3)
|
||||
r3 = p2;
|
||||
else
|
||||
r3 = p0;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r3 = AdjustColorTable (r3);
|
||||
|
||||
int r2 = n2;
|
||||
if (n2 != r0 && (n2 != m2 || n2 != p2))
|
||||
{
|
||||
if (p3 == r3)
|
||||
r2 = p3;
|
||||
else
|
||||
r2 = r1;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r2 = AdjustColorTable (r2);
|
||||
|
||||
return (ushort)((r3 << 12) | (r2 << 8) | (r1 << 4) | r0);
|
||||
}
|
||||
|
||||
byte AdjustColorTable (int idx)
|
||||
{
|
||||
int shift_count = ExtractBits (BitTable3);
|
||||
int i = 16 * idx + shift_count;
|
||||
byte c = g_colorTable[i];
|
||||
if (shift_count != 0)
|
||||
{
|
||||
while (shift_count --> 0)
|
||||
{
|
||||
g_colorTable[i] = g_colorTable[i-1];
|
||||
--i;
|
||||
}
|
||||
g_colorTable[i] = c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int ExtractBits (byte[] table)
|
||||
{
|
||||
int idx = ((bits >> 8) & 0xFF) << 1;
|
||||
int n = table[idx];
|
||||
if (n != 0)
|
||||
{
|
||||
if (n >= bitCount)
|
||||
{
|
||||
n -= bitCount;
|
||||
bits <<= bitCount;
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bitCount = 8;
|
||||
}
|
||||
bits <<= n;
|
||||
bitCount -= n;
|
||||
return table[idx+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
bits <<= bitCount;
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bits <<= 8 - bitCount;
|
||||
int t = table[idx+1];
|
||||
do
|
||||
{
|
||||
int i = GetNextBit();
|
||||
t = OffTable[2 * t + 54 + i];
|
||||
}
|
||||
while (OffTable[2 * t + 54] != 0);
|
||||
return OffTable[2 * t + 55];
|
||||
}
|
||||
}
|
||||
|
||||
int bits;
|
||||
int bitCount;
|
||||
|
||||
private void SetupBitReader ()
|
||||
{
|
||||
bits = m_input.ReadUInt8() << 8;
|
||||
bits |= m_input.ReadUInt8();
|
||||
bitCount = 8;
|
||||
}
|
||||
|
||||
private int GetNextBit ()
|
||||
{
|
||||
bits <<= 1;
|
||||
if (0 == --bitCount)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bitCount = 8;
|
||||
}
|
||||
return (bits >> 16) & 1;
|
||||
}
|
||||
|
||||
static readonly byte[] BitTable1 = new byte[] {
|
||||
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 8,
|
||||
0xD, 0, 0, 0, 1, 8, 0xE, 6, 7, 6, 7, 6, 7, 6, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
7, 0xA, 7, 0xA, 0, 2, 0, 7, 7, 0xB, 7, 0xB, 8, 0xF, 0, 3, 6, 8, 6, 8, 6, 8, 6, 8, 8, 0x10, 0, 4,
|
||||
0, 5, 8, 0x11, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 7, 0xC, 7, 0xC, 0, 8, 0, 6, 6, 9, 6,
|
||||
9, 6, 9, 6, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
static readonly byte[] BitTable2 = new byte[] {
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 7, 0x17, 7, 0x17, 7, 0x16, 7, 0x16, 6, 0x0E, 6,
|
||||
0x0E, 6, 0x0E, 6, 0x0E, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4,
|
||||
3, 4, 3, 4, 3, 4, 3, 6, 0x0D, 6, 0x0D, 6, 0x0D, 6, 0x0D, 6, 0x0C, 6, 0x0C, 6, 0x0C, 6, 0x0C, 5, 6,
|
||||
5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 7, 0x14, 7,
|
||||
0x14, 7, 0x18, 7, 0x18, 6, 0x10, 6, 0x10, 6, 0x10, 6, 0x10, 6, 0x11, 6, 0x11, 6, 0x11, 6, 0x11, 6,
|
||||
0x0F, 6, 0x0F, 6, 0x0F, 6, 0x0F, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5,
|
||||
0x0A, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
|
||||
6, 0x13, 6, 0x13, 6, 0x13, 6, 0x13, 6, 0x12, 6, 0x12, 6, 0x12, 6, 0x12, 5, 0x0B, 5, 0x0B, 5, 0x0B,
|
||||
5, 0x0B, 5, 0x0B, 5, 0x0B, 5, 0x0B, 5, 0x0B, 7, 0x19, 7, 0x19, 7, 0x1A, 7, 0x1A, 6, 0x15, 6, 0x15,
|
||||
6, 0x15, 6, 0x15, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
||||
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
|
||||
0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
|
||||
0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
};
|
||||
static readonly byte[] BitTable3 = new byte[] {
|
||||
4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5,
|
||||
4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 7, 8, 8, 0x0A, 8, 0x0B,
|
||||
4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 6, 7, 6, 7, 6, 7, 7, 9, 7, 9, 0, 0x5F, 8, 0x0C,
|
||||
2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
|
||||
1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
|
||||
2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
|
||||
1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
};
|
||||
static readonly short[] OffTable = new short[] {
|
||||
0, 1, 1, 1, -1, 0, -1, 1, -4, 0, -2, 0, 2, 1, -2, 1, 4, 1, 0, 2, 1, 2, -1, 2, -8, 0, -4, 1, 8, 1,
|
||||
-2, 2, 2, 2, -4, 2, 4, 2, 8, 2, -8, 1, -0x10, 0, -8, 2, 0x10, 1, 0x10, 2, -0x10, 1, -0x10, 2,
|
||||
0x2C, 9, 0x2D, 0x0A, 0x2E, 0x0B, 0x2F, 0x0C, 0x30, 0x0D, 0x31, 0x32, 0x0E, 0x33, 0x0F, 0x10, 0x11,
|
||||
0x12, 0x13, 0x14, 0x34, 0x15, 0x37, 0x35, 0x16, 0x38, 0x17, 0x39, 0x3D, 0x18, 0x19, 0x36, 0x1A,
|
||||
0x1B, 0x3A, 0x3C, 0x1C, 0x3B, 0x1D, 0x3E, 0x3F, 0x1E, 0x40, 0x1F, 0x45, 0x46, 0x43, 0x20, 0x21,
|
||||
0x48, 0x22, 0x41, 0x23, 0x42, 0x24, 0x25, 0x44, 0x47, 0x4F, 0x4A, 0x49, 0x53, 0x4B, 0x4D, 0x57,
|
||||
0x52, 0x59, 0x58, 0x4E, 0x50, 0x56, 0x26, 0x54, 0x4C, 0x51, 0x55, 0x27, 0x28, 0x5A, 0x2B, 0x29,
|
||||
0x5B, 0x2A, 0x5C, 0x5D, 0x5E, 0, 0, 0, 0x12, 0, 0x13, 0, 0x14, 0, 0x15, 0, 0x16, 0, 0x17, 0, 0x18,
|
||||
0, 0x19, 0, 0x1A, 0, 0x1B, 0, 0x1C, 0, 0x1D, 0, 0x1E, 0, 0x1F, 0, 0x20, 0, 0x21, 0, 0x22, 0, 0x23,
|
||||
0, 0x24, 0, 0x25, 0, 0x26, 0, 0x27, 0, 0x28, 0, 0x29, 0, 0x2A, 0, 0x2B, 0, 0x2C, 0, 0x2D, 0, 0x2E,
|
||||
0, 0x2F, 0, 0x30, 0, 0x31, 0, 0x32, 0, 0x33, 0, 0x34, 0, 0x35, 0, 0x36, 0, 0x37, 0, 0x38, 0, 0x39,
|
||||
0, 0x3A, 0, 0x3B, 0, 0x3C, 0, 0x3D, 0, 0x3E, 0, 0x3F, 0, 0x40, 0, 0x80, 0, 0x0C0, 0, 0x100, 0,
|
||||
0x140, 0x60, 0x61, 0, 0x0D, 0, 0x0E,
|
||||
};
|
||||
}
|
||||
}
|
@ -23,22 +23,32 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [970905][Ucom] Winter Kiss
|
||||
// [980626][Love Gun] ACE OF SPADES 2
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
internal class I24MetaData : ImageMetaData
|
||||
{
|
||||
public byte Version;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class I24Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "I24"; } }
|
||||
public override string Description { get { return "HyperWorks image format"; } }
|
||||
public override uint Signature { get { return 0x41343249; } } // 'I24A'
|
||||
public override string Tag => "I24";
|
||||
public override string Description => "HyperWorks RGB image format";
|
||||
public override uint Signature => 0x41343249; // 'I24A'
|
||||
|
||||
public I24Format ()
|
||||
{
|
||||
Signatures = new[] { 0x41343249u, 0x20343249u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
@ -46,16 +56,17 @@ namespace GameRes.Formats.HyperWorks
|
||||
int bpp = header.ToInt16 (0x10);
|
||||
if (bpp != 24)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
return new I24MetaData {
|
||||
Width = header.ToUInt16 (0xC),
|
||||
Height = header.ToUInt16 (0xE),
|
||||
BPP = bpp,
|
||||
Version = header[3],
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new I24Decoder (file, info);
|
||||
var reader = new I24Decoder (file, (I24MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
@ -68,9 +79,9 @@ namespace GameRes.Formats.HyperWorks
|
||||
internal class I24Decoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
I24MetaData m_info;
|
||||
|
||||
public I24Decoder (IBinaryStream input, ImageMetaData info)
|
||||
public I24Decoder (IBinaryStream input, I24MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
@ -170,14 +181,24 @@ namespace GameRes.Formats.HyperWorks
|
||||
short s2 = shift_table[shift_idx + 1];
|
||||
if (shift_token != 0)
|
||||
{
|
||||
while (shift_token --> 0)
|
||||
if (m_info.Version == 'A')
|
||||
{
|
||||
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
||||
shift_idx -= 2;
|
||||
while (shift_idx > 0)
|
||||
{
|
||||
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
||||
shift_idx -= 2;
|
||||
}
|
||||
shift_table[0] = s1;
|
||||
shift_table[1] = s2;
|
||||
}
|
||||
else
|
||||
{
|
||||
shift_table[shift_idx ] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx + 1] = shift_table[shift_idx - 1];
|
||||
shift_table[shift_idx - 2] = s1;
|
||||
shift_table[shift_idx - 1] = s2;
|
||||
}
|
||||
shift_table[0] = s1;
|
||||
shift_table[1] = s2;
|
||||
}
|
||||
int src = 4 * (x + s1);
|
||||
if (color_token >= 216)
|
||||
|
@ -30,19 +30,20 @@ using System.IO;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
|
||||
// [971205][Azlocks] Isle Mystique
|
||||
// [991001][Inspire] days innocent
|
||||
// [000707][inspire] ambience
|
||||
|
||||
namespace GameRes.Formats.Inspire
|
||||
{
|
||||
internal class IdaEntry : Entry
|
||||
internal class IdaEntry : PackedEntry
|
||||
{
|
||||
public uint Flags;
|
||||
public uint Key;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
public class IdaOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "IDA"; } }
|
||||
public override string Description { get { return "Inspire resource archive"; } }
|
||||
@ -50,6 +51,11 @@ namespace GameRes.Formats.Inspire
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public IdaOpener ()
|
||||
{
|
||||
Extensions = new[] { "ida", "mha" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int version = file.View.ReadInt32 (4);
|
||||
@ -58,8 +64,9 @@ namespace GameRes.Formats.Inspire
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
bool has_packed = false;
|
||||
long index_pos = 8;
|
||||
for (;;)
|
||||
do
|
||||
{
|
||||
index.Position = index_pos;
|
||||
uint entry_length = index.ReadUInt32();
|
||||
@ -76,15 +83,27 @@ namespace GameRes.Formats.Inspire
|
||||
|
||||
var entry = FormatCatalog.Instance.Create<IdaEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
entry.Size = entry.UnpackedSize = size;
|
||||
if (offset > file.MaxOffset || offset < index_pos)
|
||||
return null;
|
||||
entry.IsPacked = (flags & 0x14) != 0;
|
||||
entry.Flags = flags;
|
||||
entry.Key = key;
|
||||
has_packed = has_packed || entry.IsPacked;
|
||||
dir.Add (entry);
|
||||
}
|
||||
while (index_pos < dir[0].Offset);
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
if (has_packed) // set proper sizes
|
||||
{
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
dir[i].Size = (uint)(last_offset - dir[i].Offset);
|
||||
last_offset = dir[i].Offset;
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +90,11 @@
|
||||
<Compile Include="Asura\ImageMTG.cs" />
|
||||
<Compile Include="BlackButterfly\ArcDAT.cs" />
|
||||
<Compile Include="CottonClub\ImageLMG.cs" />
|
||||
<Compile Include="Gsx\ArcK5.cs" />
|
||||
<Compile Include="Gsx\ImageK4.cs" />
|
||||
<Compile Include="Herb\ArcPAK.cs" />
|
||||
<Compile Include="Herb\ImageGRP.cs" />
|
||||
<Compile Include="HyperWorks\ImageG.cs" />
|
||||
<Compile Include="James\ImageJMG.cs" />
|
||||
<Compile Include="BRoom\ArcCPC.cs" />
|
||||
<Compile Include="BRoom\ArcPK.cs" />
|
||||
@ -125,6 +128,8 @@
|
||||
<Compile Include="Lazycrew\ImageDAT.cs" />
|
||||
<Compile Include="Liddell\ArcFLK.cs" />
|
||||
<Compile Include="Liddell\ImageBPA.cs" />
|
||||
<Compile Include="Logg\ArcARF.cs" />
|
||||
<Compile Include="Logg\ImageFRM.cs" />
|
||||
<Compile Include="Melonpan\ArcTTD.cs" />
|
||||
<Compile Include="Mermaid\AudioPWV.cs" />
|
||||
<Compile Include="Mermaid\ImageGP1.cs" />
|
||||
@ -132,6 +137,7 @@
|
||||
<Compile Include="Mink\ImageFD.cs" />
|
||||
<Compile Include="Mmfass\ArcSDA.cs" />
|
||||
<Compile Include="Nyoken\ArcZLK.cs" />
|
||||
<Compile Include="Omi\ArcDAT.cs" />
|
||||
<Compile Include="Paprika\ArcPKDAT.cs" />
|
||||
<Compile Include="Paprika\ImageNP.cs" />
|
||||
<Compile Include="PenguinWorks\ArcPAC.cs" />
|
||||
@ -145,8 +151,10 @@
|
||||
<Compile Include="PrimeSoft\ImageTHP.cs" />
|
||||
<Compile Include="ProjectMyu\ImageGAM.cs" />
|
||||
<Compile Include="Ransel\ArcBCD.cs" />
|
||||
<Compile Include="Rare\ArcX.cs" />
|
||||
<Compile Include="Regrips\AudioWRG.cs" />
|
||||
<Compile Include="Regrips\ImagePRG.cs" />
|
||||
<Compile Include="Rhss\ArcCRG.cs" />
|
||||
<Compile Include="Rina\ImageRAD.cs" />
|
||||
<Compile Include="RSystem\ArcRAD.cs" />
|
||||
<Compile Include="RSystem\ImageRSG.cs" />
|
||||
|
141
Legacy/Logg/ArcARF.cs
Normal file
141
Legacy/Logg/ArcARF.cs
Normal file
@ -0,0 +1,141 @@
|
||||
//! \file ArcARF.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Logg resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [980417][Logg] Tenshi Kourin
|
||||
// [980828][Logg] Kazeiro no Romance
|
||||
|
||||
namespace GameRes.Formats.Logg
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ArfOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "ARF";
|
||||
public override string Description => "Logg archive file";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index = 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index);
|
||||
if (offset <= index || offset > file.MaxOffset)
|
||||
return null;
|
||||
uint size = file.View.ReadUInt32 (index+4);
|
||||
byte name_len = file.View.ReadByte (index+8);
|
||||
var name = file.View.ReadString (index+9, name_len);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.UnpackedSize = size;
|
||||
dir.Add (entry);
|
||||
index += name_len + 9u;
|
||||
if (index > dir[0].Offset)
|
||||
return null;
|
||||
}
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = count-1; i >= 0; --i)
|
||||
{
|
||||
var entry = dir[i] as PackedEntry;
|
||||
entry.Size = (uint)(last_offset - entry.Offset);
|
||||
last_offset = entry.Offset;
|
||||
if (string.IsNullOrEmpty (entry.Name))
|
||||
dir.RemoveAt (i);
|
||||
else
|
||||
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
if (!pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var input = arc.File.CreateStream (pent.Offset, pent.Size);
|
||||
var output = new byte[pent.UnpackedSize];
|
||||
Decompress (input, output);
|
||||
return new BinMemoryStream (output, pent.Name);
|
||||
}
|
||||
|
||||
void Decompress (IBinaryStream input, byte[] output)
|
||||
{
|
||||
using (var bits = new LsbBitStream (input.AsStream, true))
|
||||
{
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
if (bits.GetNextBit() == 0)
|
||||
{
|
||||
output[dst++] = (byte)bits.GetBits (8);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count;
|
||||
if (bits.GetNextBit() == 0)
|
||||
count = 2;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 3;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 4;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 5;
|
||||
else
|
||||
{
|
||||
switch (bits.GetBits (2))
|
||||
{
|
||||
case 0: count = 6; break;
|
||||
case 1: count = bits.GetBits (2) + 7; break;
|
||||
case 2: count = bits.GetBits (4) + 11; break;
|
||||
case 3: count = bits.GetBits (10) + 26; break;
|
||||
default: throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
int offset;
|
||||
if (bits.GetNextBit() == 0)
|
||||
offset = bits.GetBits (8);
|
||||
else if (bits.GetNextBit() == 0)
|
||||
offset = bits.GetBits (10) + 0x100;
|
||||
else
|
||||
offset = bits.GetBits (12) + 0x500;
|
||||
Binary.CopyOverlapped (output, dst - offset - 1, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -36,11 +36,11 @@ namespace GameRes.Formats.Logg
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class MbmOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "MBM"; } }
|
||||
public override string Description { get { return "Logg Adv engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override string Tag => "MBM";
|
||||
public override string Description => "Logg Adv engine resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
@ -63,19 +63,27 @@ namespace GameRes.Formats.Logg
|
||||
|
||||
IDictionary<uint, string> GetArchiveIndex (ArcView file)
|
||||
{
|
||||
uint last_offset = FileListMap.Value.Keys.Last();
|
||||
string list_name;
|
||||
if (!ArcSizeToFileListMap.TryGetValue ((uint)file.MaxOffset, out list_name))
|
||||
return null;
|
||||
var file_map = ReadFileList (list_name);
|
||||
uint last_offset = file_map.Keys.Last();
|
||||
if (last_offset != file.MaxOffset)
|
||||
return null;
|
||||
return FileListMap.Value;
|
||||
return file_map;
|
||||
}
|
||||
|
||||
Lazy<IDictionary<uint, string>> FileListMap = new Lazy<IDictionary<uint, string>> (ReadFileList);
|
||||
static readonly Dictionary<uint, string> ArcSizeToFileListMap = new Dictionary<uint, string> {
|
||||
{ 0x0AB0F5F4, "logg_pl.lst" },
|
||||
{ 0x0BFFD3DA, "logg_ak.lst" },
|
||||
{ 0x09809196, "logg_th.lst" },
|
||||
};
|
||||
|
||||
static IDictionary<uint, string> ReadFileList ()
|
||||
static IDictionary<uint, string> ReadFileList (string list_name)
|
||||
{
|
||||
var file_map = new SortedDictionary<uint,string>();
|
||||
var comma = new char[] {','};
|
||||
FormatCatalog.Instance.ReadFileList ("logg_pl.lst", line => {
|
||||
FormatCatalog.Instance.ReadFileList (list_name, line => {
|
||||
var parts = line.Split (comma, 2);
|
||||
uint offset = uint.Parse (parts[0], NumberStyles.HexNumber);
|
||||
if (2 == parts.Length)
|
||||
|
63
Legacy/Logg/ImageFRM.cs
Normal file
63
Legacy/Logg/ImageFRM.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//! \file ImageFRM.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Logg image file.
|
||||
//
|
||||
// Copyright (C) 2023 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Logg
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class FrmFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "FRM"; }
|
||||
public override string Description { get => "Logg image format"; }
|
||||
public override uint Signature { get => 0x4D5246; } // 'FRM'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt32 (4),
|
||||
Height = header.ToUInt32 (8),
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0x0C;
|
||||
int stride = file.ReadInt32();
|
||||
var palette = ReadPalette (file.AsStream);
|
||||
var pixels = file.ReadBytes (stride * info.iHeight);
|
||||
return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels, stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("FrmFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
213
Legacy/Omi/ArcDAT.cs
Normal file
213
Legacy/Omi/ArcDAT.cs
Normal file
@ -0,0 +1,213 @@
|
||||
//! \file ArcDAT.cs
|
||||
//! \date 2023 Sep 04
|
||||
//! \brief OMI Script Engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Omi
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/OMI";
|
||||
public override string Description => "OMI Script Engine resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
internal const uint DefaultKey = 7654321u;
|
||||
|
||||
public DatOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "BMP", "TGA", "WAV", "TXT" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "scrdat"))
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
using (var index = new DecryptedStream (input, DefaultKey, 0))
|
||||
{
|
||||
var line = index.ReadLine();
|
||||
int count = int.Parse (line);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = index.ReadLine();
|
||||
line = index.ReadLine();
|
||||
uint size = uint.Parse (line);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Size = size;
|
||||
entry.IsPacked = entry.Type == "image";
|
||||
dir.Add (entry);
|
||||
}
|
||||
long data_pos = index.Position;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
dir[i].Offset = data_pos;
|
||||
if (!dir[i].CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
data_pos += dir[i].Size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
input = new DecryptedStream (input, DefaultKey, (uint)entry.Offset);
|
||||
if (!pent.IsPacked)
|
||||
return input;
|
||||
using (var packed = new BinaryStream (input, pent.Name))
|
||||
{
|
||||
var unpacked = DecompressRle (packed);
|
||||
if (pent.UnpackedSize == 0)
|
||||
pent.UnpackedSize = (uint)unpacked.Length;
|
||||
return new BinMemoryStream (unpacked, pent.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] DecompressRle (IBinaryStream input)
|
||||
{
|
||||
int size = input.ReadInt32();
|
||||
var output = new byte[size * 2];
|
||||
ushort rle_marker = input.ReadUInt16();
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
input.Read (output, dst, 2);
|
||||
if (output.ToUInt16 (dst) == rle_marker)
|
||||
{
|
||||
input.Read (output, dst, 2);
|
||||
dst += 2;
|
||||
int count = input.ReadUInt16() - 1;
|
||||
if (count > 0)
|
||||
{
|
||||
count *= 2;
|
||||
Binary.CopyOverlapped (output, dst-2, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DecryptedStream : InputProxyStream
|
||||
{
|
||||
private uint m_key;
|
||||
|
||||
static readonly Encoding Encoding = Encodings.cp932;
|
||||
|
||||
public override bool CanSeek { get => false; }
|
||||
public override long Position
|
||||
{
|
||||
get => BaseStream.Position;
|
||||
set => throw new NotSupportedException ("Stream.Position property is not supported");
|
||||
}
|
||||
|
||||
public DecryptedStream (Stream stream, uint key, uint start_offset) : base (stream)
|
||||
{
|
||||
if (start_offset > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
key = 5 * key - 3;
|
||||
}
|
||||
while (--start_offset > 0);
|
||||
}
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
{
|
||||
int read = BaseStream.Read (buffer, offset, count);
|
||||
Decrypt (buffer, offset, read);
|
||||
return read;
|
||||
}
|
||||
|
||||
byte[] m_byte_buffer = new byte[1];
|
||||
|
||||
public override int ReadByte ()
|
||||
{
|
||||
int b = BaseStream.ReadByte();
|
||||
if (-1 != b)
|
||||
{
|
||||
m_byte_buffer[0] = (byte)b;
|
||||
Decrypt (m_byte_buffer, 0, 1);
|
||||
b = m_byte_buffer[0];
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
internal void Decrypt (byte[] data, int offset, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
data[offset+i] = (byte)(Binary.RotByteR (data[offset+i], 1) - m_key);
|
||||
m_key = 5 * m_key - 3;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
|
||||
public string ReadLine ()
|
||||
{
|
||||
if (null == m_buffer)
|
||||
m_buffer = new byte[32];
|
||||
int size = 0;
|
||||
for (;;)
|
||||
{
|
||||
int b = ReadByte();
|
||||
if (-1 == b || '\n' == b)
|
||||
break;
|
||||
if (m_buffer.Length == size)
|
||||
{
|
||||
Array.Resize (ref m_buffer, checked(size/2*3));
|
||||
}
|
||||
m_buffer[size++] = (byte)b;
|
||||
}
|
||||
return Encoding.GetString (m_buffer, 0, size);
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
132
Legacy/Rare/ArcX.cs
Normal file
132
Legacy/Rare/ArcX.cs
Normal file
@ -0,0 +1,132 @@
|
||||
//! \file ArcX.cs
|
||||
//! \date 2023 Sep 02
|
||||
//! \brief Rare archive format.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
// [990528][Rare] Seisen Ren'ai Yuugi
|
||||
|
||||
namespace GameRes.Formats.Rare
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class XOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "X/RARE";
|
||||
public override string Description => "Rare resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
static readonly Dictionary<string, Tuple<uint, int>> KnownExeMap = new Dictionary<string, Tuple<uint, int>> {
|
||||
{ "seisen.exe", Tuple.Create (0x3A9A0u, 715) },
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "PP.X"))
|
||||
return null;
|
||||
string full_exe_name = null;
|
||||
Tuple<uint, int> index_pos = null;
|
||||
foreach (var exe_name in KnownExeMap.Keys)
|
||||
{
|
||||
full_exe_name = VFS.ChangeFileName (file.Name, exe_name);
|
||||
if (VFS.FileExists (full_exe_name))
|
||||
{
|
||||
index_pos = KnownExeMap[exe_name];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == index_pos)
|
||||
return null;
|
||||
uint index_offset = index_pos.Item1;
|
||||
int count = index_pos.Item2;
|
||||
using (var index = VFS.OpenView (full_exe_name))
|
||||
{
|
||||
index.View.Reserve (index_offset, (uint)count * 12u);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new PackedEntry {
|
||||
Name = string.Format ("PP#{0:D5}.BMP", i),
|
||||
Type = "image",
|
||||
Offset = index.View.ReadUInt32 (index_offset),
|
||||
Size = index.View.ReadUInt32 (index_offset+4),
|
||||
UnpackedSize = index.View.ReadUInt32 (index_offset+8),
|
||||
IsPacked = true,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 12;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
{
|
||||
var output = new byte[pent.UnpackedSize];
|
||||
Decompress (input, output);
|
||||
return new BinMemoryStream (output, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Decompress (IBinaryStream input, byte[] output)
|
||||
{
|
||||
var frame = new byte[0x400];
|
||||
int frame_pos = 1;
|
||||
int dst = 0;
|
||||
using (var bits = new MsbBitStream (input.AsStream, true))
|
||||
{
|
||||
while (dst < output.Length)
|
||||
{
|
||||
int ctl = bits.GetNextBit();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
if (ctl != 0)
|
||||
{
|
||||
int v = bits.GetBits (8);
|
||||
output[dst++] = frame[frame_pos++ & 0x3FF] = (byte)v;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = bits.GetBits (10);
|
||||
int count = bits.GetBits (5) + 2;
|
||||
while (count --> 0)
|
||||
{
|
||||
byte v = frame[offset++ & 0x3FF];
|
||||
output[dst++] = frame[frame_pos++ & 0x3FF] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
Legacy/Rhss/ArcCRG.cs
Normal file
83
Legacy/Rhss/ArcCRG.cs
Normal file
@ -0,0 +1,83 @@
|
||||
//! \file ArcCRG.cs
|
||||
//! \date 2023 Aug 28
|
||||
//! \brief Raishū Hyōjun Script System resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Rhss
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/CRG";
|
||||
public override string Description => "RHSS engine resource archive";
|
||||
public override uint Signature => 0x00475243; // 'CRG'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_pos = 8;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos+8, 0x30);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 60;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PackedEntry;
|
||||
if (null != pent)
|
||||
{
|
||||
if (!pent.IsPacked && arc.File.View.AsciiEqual (entry.Offset, "CMP\0"))
|
||||
{
|
||||
pent.IsPacked = true;
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset + 0x4C);
|
||||
}
|
||||
if (pent.IsPacked)
|
||||
{
|
||||
Stream input = arc.File.CreateStream (entry.Offset+0x50, entry.Size-0x50);
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return new XoredStream (input, 0xFF);
|
||||
}
|
||||
}
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
}
|
||||
}
|
@ -27,6 +27,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [030817][Splush Wave] Knock Out -Taisengata Datsui Mahjong-
|
||||
|
||||
namespace GameRes.Formats.SplushWave
|
||||
{
|
||||
@ -129,5 +132,92 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
return new BinMemoryStream (output, 0, dst);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var fent = (FlkEntry)entry;
|
||||
var input = BinaryStream.FromStream (OpenEntry (arc, fent), fent.Name);
|
||||
if ((fent.Flags & 0x10) == 0)
|
||||
return ImageFormatDecoder.Create (input);
|
||||
try
|
||||
{
|
||||
var info = Swg.ReadMetaData (input) as SwgMetaData;
|
||||
if (null == info)
|
||||
{
|
||||
input.Position = 0;
|
||||
return new ImageFormatDecoder(input);
|
||||
}
|
||||
return new Swg1ImageDecoder (input, info);
|
||||
}
|
||||
catch
|
||||
{
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly ResourceInstance<SwgFormat> s_swg = new ResourceInstance<SwgFormat> ("SWG");
|
||||
|
||||
internal static SwgFormat Swg { get => s_swg.Value; }
|
||||
}
|
||||
|
||||
internal sealed class Swg1ImageDecoder : BinaryImageDecoder
|
||||
{
|
||||
SwgMetaData m_info;
|
||||
|
||||
public Swg1ImageDecoder (IBinaryStream input, SwgMetaData info) : base (input, info)
|
||||
{
|
||||
SourceFormat = DatOpener.Swg;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
static readonly byte[] PlaneMap = { 3, 2, 1, 0 };
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = m_info.DataOffset;
|
||||
int stride = 4 * m_info.iWidth;
|
||||
int plane_size = m_info.iWidth * m_info.iHeight;
|
||||
var output = new byte[stride * m_info.iHeight];
|
||||
ushort[] ctl_buf = new ushort[m_info.iHeight];
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
int compress_method = ReadU16BE();
|
||||
if (0 == compress_method)
|
||||
{
|
||||
int dst = PlaneMap[c] + stride * (m_info.iHeight - 1);
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < stride; x += 4)
|
||||
{
|
||||
output[dst+x] = m_input.ReadUInt8();
|
||||
}
|
||||
dst -= stride;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (compress_method != 1)
|
||||
throw new InvalidFormatException();
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
ctl_buf[y] = ReadU16BE();
|
||||
}
|
||||
int row = PlaneMap[c];
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = row;
|
||||
int row_size = ctl_buf[y];
|
||||
SwgFormat.DecompressRow (m_input, row_size, output, dst, 4);
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, output, stride);
|
||||
}
|
||||
|
||||
ushort ReadU16BE ()
|
||||
{
|
||||
ushort le = m_input.ReadUInt16();
|
||||
return (ushort)(le >> 8 | le << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ namespace GameRes.Formats.SplushWave
|
||||
{
|
||||
var meta = (SwgMetaData)info;
|
||||
PixelFormat format = meta.BPP == 8 ? PixelFormats.Indexed8
|
||||
: meta.BPP == 32 ? PixelFormats.Bgr32 : PixelFormats.Bgr24;
|
||||
: meta.BPP == 32 ? PixelFormats.Bgra32 : PixelFormats.Bgr24;
|
||||
BitmapPalette palette = null;
|
||||
if (meta.BPP == 8)
|
||||
{
|
||||
@ -77,36 +77,40 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
int stride = meta.iWidth * meta.BPP / 8;
|
||||
file.Position = meta.DataOffset;
|
||||
// var pixels = new byte[stride * meta.iHeight];
|
||||
var pixels = new byte[4 * meta.iWidth * meta.iHeight];
|
||||
var pixels = new byte[stride * meta.iHeight];
|
||||
if (!meta.IsCompressed)
|
||||
{
|
||||
file.Read (pixels, 0, pixels.Length);
|
||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
||||
}
|
||||
var input = file.ReadBytes ((int)(file.Length - file.Position));
|
||||
if (!Decompress (input, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
||||
if (!Decompress (file, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
||||
throw new InvalidFormatException ("Invalid SWG file.");
|
||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
||||
}
|
||||
|
||||
bool Decompress (byte[] input, byte[] output, int channels, int width, int height)
|
||||
static readonly byte[] PlaneMap = { 2, 1, 0, 3 };
|
||||
|
||||
bool Decompress (IBinaryStream input, byte[] output, int channels, int width, int height)
|
||||
{
|
||||
int src = 0;
|
||||
if (input[0] != 0 || input[1] != 1)
|
||||
long start_pos = input.Position;
|
||||
byte hi = input.ReadUInt8();
|
||||
byte lo = input.ReadUInt8();
|
||||
if (hi != 0 || lo != 1)
|
||||
{
|
||||
input.Position = start_pos;
|
||||
int n = 0;
|
||||
for (int i = 0; i < channels; ++i)
|
||||
{
|
||||
if (0 == input[i])
|
||||
if (0 == input.ReadByte())
|
||||
++n;
|
||||
}
|
||||
if (n != channels)
|
||||
return false;
|
||||
src = 4;
|
||||
input.Position = start_pos + 4;
|
||||
hi = input.ReadUInt8();
|
||||
lo = input.ReadUInt8();
|
||||
}
|
||||
int compress_method = input[src+1] + (input[src] << 8);
|
||||
src += 2;
|
||||
int compress_method = lo | hi << 8;
|
||||
if (0 == compress_method)
|
||||
{
|
||||
for (int i = 0; i < channels; ++i)
|
||||
@ -115,7 +119,7 @@ namespace GameRes.Formats.SplushWave
|
||||
int count = height * width;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[pos] = input[src++];
|
||||
output[pos] = input.ReadUInt8();
|
||||
pos += channels;
|
||||
}
|
||||
}
|
||||
@ -123,63 +127,57 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
if (compress_method != 1)
|
||||
return false;
|
||||
int dst = 0;
|
||||
int v33 = src;
|
||||
int v37 = height * channels;
|
||||
src += 2 * v37;
|
||||
for (int row = 0; row < v37; ++row)
|
||||
int stride = width * channels;
|
||||
var row_sizes = input.ReadBytes (2 * height * channels);
|
||||
int ctl_pos = 0;
|
||||
for (int c = 0; c < channels; ++c)
|
||||
for (int y = height - 1; y >= 0; --y)
|
||||
{
|
||||
int y = row % height;
|
||||
dst = channels * (width * (height - y - 1) + 1) - row / height - 1;
|
||||
if (dst > output.Length)
|
||||
return true;
|
||||
int v24 = 0;
|
||||
int v36 = input[v33+1] + (input[v33] << 8);
|
||||
v33 += 2;
|
||||
do
|
||||
{
|
||||
byte lo = input[src];
|
||||
byte hi = input[src+1];
|
||||
if (lo != 0)
|
||||
{
|
||||
if (lo < 0x81)
|
||||
{
|
||||
++src;
|
||||
int count = lo + 1;
|
||||
v24 += count + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = input[src++];
|
||||
dst += channels;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 2;
|
||||
v24 += 2;
|
||||
int count = Math.Min (0x101 - lo, output.Length - dst);
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = hi;
|
||||
dst += channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 2;
|
||||
v24 += 2;
|
||||
output[dst] = hi;
|
||||
dst += channels;
|
||||
}
|
||||
if (dst >= output.Length)
|
||||
return true;
|
||||
}
|
||||
while (v24 < v36);
|
||||
int dst = stride * y + PlaneMap[c];
|
||||
int row_size = row_sizes[ctl_pos+1] | row_sizes[ctl_pos] << 8;
|
||||
ctl_pos += 2;
|
||||
DecompressRow (input, row_size, output, dst, channels);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void DecompressRow (IBinaryStream input, int row_size, byte[] output, int dst, int step)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < row_size)
|
||||
{
|
||||
byte ctl = input.ReadUInt8();
|
||||
if (ctl == 0)
|
||||
{
|
||||
byte v = input.ReadUInt8();
|
||||
x += 2;
|
||||
output[dst] = v;
|
||||
dst += step;
|
||||
}
|
||||
else if (ctl < 0x81u)
|
||||
{
|
||||
int count = ctl + 1;
|
||||
x += count + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = input.ReadUInt8();
|
||||
dst += step;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte v = input.ReadUInt8();
|
||||
x += 2;
|
||||
int count = 0x101 - ctl;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = v;
|
||||
dst += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("SwgFormat.Write not implemented");
|
||||
|
Loading…
Reference in New Issue
Block a user