mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-27 07:34:00 +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.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
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
|
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
|
//! \file ImageK4.cs
|
||||||
//! \date 2019 Feb 07
|
//! \date 2023 Aug 27
|
||||||
//! \brief Toyo GSX image format.
|
//! \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
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@ -23,6 +23,8 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using GameRes.Utility;
|
||||||
|
using System;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
@ -33,16 +35,16 @@ namespace GameRes.Formats.Gsx
|
|||||||
{
|
{
|
||||||
internal class K4MetaData : ImageMetaData
|
internal class K4MetaData : ImageMetaData
|
||||||
{
|
{
|
||||||
public bool HasAlpha;
|
public byte AlphaMode;
|
||||||
public int FrameCount;
|
public int FrameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export(typeof(ImageFormat))]
|
[Export(typeof(ImageFormat))]
|
||||||
public class K4Format : ImageFormat
|
public class K4Format : ImageFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "K4"; } }
|
public override string Tag => "K4";
|
||||||
public override string Description { get { return "Toyo GSX image format"; } }
|
public override string Description => "GSX engine image format";
|
||||||
public override uint Signature { get { return 0x0201344B; } } // 'K4'
|
public override uint Signature => 0x0201344B; // 'K4'
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||||
{
|
{
|
||||||
@ -51,20 +53,22 @@ namespace GameRes.Formats.Gsx
|
|||||||
return null;
|
return null;
|
||||||
if (header[2] != 1 || header[3] != 2)
|
if (header[2] != 1 || header[3] != 2)
|
||||||
return null;
|
return null;
|
||||||
|
int frame_count = header.ToInt16 (0xC);
|
||||||
|
if (frame_count <= 0)
|
||||||
|
return null;
|
||||||
return new K4MetaData {
|
return new K4MetaData {
|
||||||
Width = header.ToUInt16 (4),
|
Width = header.ToUInt16 (4),
|
||||||
Height = header.ToUInt16 (6),
|
Height = header.ToUInt16 (6),
|
||||||
BPP = header[0xF],
|
BPP = header[0xF],
|
||||||
HasAlpha = header[0xB] != 0,
|
AlphaMode = header[0xB],
|
||||||
FrameCount = header.ToUInt16 (0xC),
|
FrameCount = frame_count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||||
{
|
{
|
||||||
var meta = (K4MetaData)info;
|
var reader = new K4Reader (file, (K4MetaData)info);
|
||||||
|
return reader.Unpack();
|
||||||
return ImageData.Create (info, format, palette, pixels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write (Stream file, ImageData image)
|
public override void Write (Stream file, ImageData image)
|
||||||
@ -72,4 +76,190 @@ namespace GameRes.Formats.Gsx
|
|||||||
throw new System.NotImplementedException ("K4Format.Write not implemented");
|
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.
|
// IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
using GameRes.Utility;
|
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
// [970905][Ucom] Winter Kiss
|
||||||
// [980626][Love Gun] ACE OF SPADES 2
|
// [980626][Love Gun] ACE OF SPADES 2
|
||||||
|
|
||||||
namespace GameRes.Formats.HyperWorks
|
namespace GameRes.Formats.HyperWorks
|
||||||
{
|
{
|
||||||
|
internal class I24MetaData : ImageMetaData
|
||||||
|
{
|
||||||
|
public byte Version;
|
||||||
|
}
|
||||||
|
|
||||||
[Export(typeof(ImageFormat))]
|
[Export(typeof(ImageFormat))]
|
||||||
public class I24Format : ImageFormat
|
public class I24Format : ImageFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "I24"; } }
|
public override string Tag => "I24";
|
||||||
public override string Description { get { return "HyperWorks image format"; } }
|
public override string Description => "HyperWorks RGB image format";
|
||||||
public override uint Signature { get { return 0x41343249; } } // 'I24A'
|
public override uint Signature => 0x41343249; // 'I24A'
|
||||||
|
|
||||||
|
public I24Format ()
|
||||||
|
{
|
||||||
|
Signatures = new[] { 0x41343249u, 0x20343249u };
|
||||||
|
}
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||||
{
|
{
|
||||||
@ -46,16 +56,17 @@ namespace GameRes.Formats.HyperWorks
|
|||||||
int bpp = header.ToInt16 (0x10);
|
int bpp = header.ToInt16 (0x10);
|
||||||
if (bpp != 24)
|
if (bpp != 24)
|
||||||
return null;
|
return null;
|
||||||
return new ImageMetaData {
|
return new I24MetaData {
|
||||||
Width = header.ToUInt16 (0xC),
|
Width = header.ToUInt16 (0xC),
|
||||||
Height = header.ToUInt16 (0xE),
|
Height = header.ToUInt16 (0xE),
|
||||||
BPP = bpp,
|
BPP = bpp,
|
||||||
|
Version = header[3],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||||
{
|
{
|
||||||
var reader = new I24Decoder (file, info);
|
var reader = new I24Decoder (file, (I24MetaData)info);
|
||||||
return reader.Unpack();
|
return reader.Unpack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,9 +79,9 @@ namespace GameRes.Formats.HyperWorks
|
|||||||
internal class I24Decoder
|
internal class I24Decoder
|
||||||
{
|
{
|
||||||
IBinaryStream m_input;
|
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_input = input;
|
||||||
m_info = info;
|
m_info = info;
|
||||||
@ -170,7 +181,9 @@ namespace GameRes.Formats.HyperWorks
|
|||||||
short s2 = shift_table[shift_idx + 1];
|
short s2 = shift_table[shift_idx + 1];
|
||||||
if (shift_token != 0)
|
if (shift_token != 0)
|
||||||
{
|
{
|
||||||
while (shift_token --> 0)
|
if (m_info.Version == 'A')
|
||||||
|
{
|
||||||
|
while (shift_idx > 0)
|
||||||
{
|
{
|
||||||
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
||||||
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
||||||
@ -179,6 +192,14 @@ namespace GameRes.Formats.HyperWorks
|
|||||||
shift_table[0] = s1;
|
shift_table[0] = s1;
|
||||||
shift_table[1] = s2;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
int src = 4 * (x + s1);
|
int src = 4 * (x + s1);
|
||||||
if (color_token >= 216)
|
if (color_token >= 216)
|
||||||
{
|
{
|
||||||
|
@ -30,19 +30,20 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using GameRes.Compression;
|
using GameRes.Compression;
|
||||||
|
|
||||||
|
// [971205][Azlocks] Isle Mystique
|
||||||
// [991001][Inspire] days innocent
|
// [991001][Inspire] days innocent
|
||||||
// [000707][inspire] ambience
|
// [000707][inspire] ambience
|
||||||
|
|
||||||
namespace GameRes.Formats.Inspire
|
namespace GameRes.Formats.Inspire
|
||||||
{
|
{
|
||||||
internal class IdaEntry : Entry
|
internal class IdaEntry : PackedEntry
|
||||||
{
|
{
|
||||||
public uint Flags;
|
public uint Flags;
|
||||||
public uint Key;
|
public uint Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export(typeof(ArchiveFormat))]
|
[Export(typeof(ArchiveFormat))]
|
||||||
public class PakOpener : ArchiveFormat
|
public class IdaOpener : ArchiveFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "IDA"; } }
|
public override string Tag { get { return "IDA"; } }
|
||||||
public override string Description { get { return "Inspire resource archive"; } }
|
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 IsHierarchic { get { return false; } }
|
||||||
public override bool CanWrite { get { return false; } }
|
public override bool CanWrite { get { return false; } }
|
||||||
|
|
||||||
|
public IdaOpener ()
|
||||||
|
{
|
||||||
|
Extensions = new[] { "ida", "mha" };
|
||||||
|
}
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
{
|
{
|
||||||
int version = file.View.ReadInt32 (4);
|
int version = file.View.ReadInt32 (4);
|
||||||
@ -58,8 +64,9 @@ namespace GameRes.Formats.Inspire
|
|||||||
using (var index = file.CreateStream())
|
using (var index = file.CreateStream())
|
||||||
{
|
{
|
||||||
var dir = new List<Entry>();
|
var dir = new List<Entry>();
|
||||||
|
bool has_packed = false;
|
||||||
long index_pos = 8;
|
long index_pos = 8;
|
||||||
for (;;)
|
do
|
||||||
{
|
{
|
||||||
index.Position = index_pos;
|
index.Position = index_pos;
|
||||||
uint entry_length = index.ReadUInt32();
|
uint entry_length = index.ReadUInt32();
|
||||||
@ -76,15 +83,27 @@ namespace GameRes.Formats.Inspire
|
|||||||
|
|
||||||
var entry = FormatCatalog.Instance.Create<IdaEntry> (name);
|
var entry = FormatCatalog.Instance.Create<IdaEntry> (name);
|
||||||
entry.Offset = offset;
|
entry.Offset = offset;
|
||||||
entry.Size = size;
|
entry.Size = entry.UnpackedSize = size;
|
||||||
if (!entry.CheckPlacement (file.MaxOffset))
|
if (offset > file.MaxOffset || offset < index_pos)
|
||||||
return null;
|
return null;
|
||||||
|
entry.IsPacked = (flags & 0x14) != 0;
|
||||||
entry.Flags = flags;
|
entry.Flags = flags;
|
||||||
entry.Key = key;
|
entry.Key = key;
|
||||||
|
has_packed = has_packed || entry.IsPacked;
|
||||||
dir.Add (entry);
|
dir.Add (entry);
|
||||||
}
|
}
|
||||||
|
while (index_pos < dir[0].Offset);
|
||||||
if (0 == dir.Count)
|
if (0 == dir.Count)
|
||||||
return null;
|
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);
|
return new ArcFile (file, this, dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,8 +90,11 @@
|
|||||||
<Compile Include="Asura\ImageMTG.cs" />
|
<Compile Include="Asura\ImageMTG.cs" />
|
||||||
<Compile Include="BlackButterfly\ArcDAT.cs" />
|
<Compile Include="BlackButterfly\ArcDAT.cs" />
|
||||||
<Compile Include="CottonClub\ImageLMG.cs" />
|
<Compile Include="CottonClub\ImageLMG.cs" />
|
||||||
|
<Compile Include="Gsx\ArcK5.cs" />
|
||||||
|
<Compile Include="Gsx\ImageK4.cs" />
|
||||||
<Compile Include="Herb\ArcPAK.cs" />
|
<Compile Include="Herb\ArcPAK.cs" />
|
||||||
<Compile Include="Herb\ImageGRP.cs" />
|
<Compile Include="Herb\ImageGRP.cs" />
|
||||||
|
<Compile Include="HyperWorks\ImageG.cs" />
|
||||||
<Compile Include="James\ImageJMG.cs" />
|
<Compile Include="James\ImageJMG.cs" />
|
||||||
<Compile Include="BRoom\ArcCPC.cs" />
|
<Compile Include="BRoom\ArcCPC.cs" />
|
||||||
<Compile Include="BRoom\ArcPK.cs" />
|
<Compile Include="BRoom\ArcPK.cs" />
|
||||||
@ -125,6 +128,8 @@
|
|||||||
<Compile Include="Lazycrew\ImageDAT.cs" />
|
<Compile Include="Lazycrew\ImageDAT.cs" />
|
||||||
<Compile Include="Liddell\ArcFLK.cs" />
|
<Compile Include="Liddell\ArcFLK.cs" />
|
||||||
<Compile Include="Liddell\ImageBPA.cs" />
|
<Compile Include="Liddell\ImageBPA.cs" />
|
||||||
|
<Compile Include="Logg\ArcARF.cs" />
|
||||||
|
<Compile Include="Logg\ImageFRM.cs" />
|
||||||
<Compile Include="Melonpan\ArcTTD.cs" />
|
<Compile Include="Melonpan\ArcTTD.cs" />
|
||||||
<Compile Include="Mermaid\AudioPWV.cs" />
|
<Compile Include="Mermaid\AudioPWV.cs" />
|
||||||
<Compile Include="Mermaid\ImageGP1.cs" />
|
<Compile Include="Mermaid\ImageGP1.cs" />
|
||||||
@ -132,6 +137,7 @@
|
|||||||
<Compile Include="Mink\ImageFD.cs" />
|
<Compile Include="Mink\ImageFD.cs" />
|
||||||
<Compile Include="Mmfass\ArcSDA.cs" />
|
<Compile Include="Mmfass\ArcSDA.cs" />
|
||||||
<Compile Include="Nyoken\ArcZLK.cs" />
|
<Compile Include="Nyoken\ArcZLK.cs" />
|
||||||
|
<Compile Include="Omi\ArcDAT.cs" />
|
||||||
<Compile Include="Paprika\ArcPKDAT.cs" />
|
<Compile Include="Paprika\ArcPKDAT.cs" />
|
||||||
<Compile Include="Paprika\ImageNP.cs" />
|
<Compile Include="Paprika\ImageNP.cs" />
|
||||||
<Compile Include="PenguinWorks\ArcPAC.cs" />
|
<Compile Include="PenguinWorks\ArcPAC.cs" />
|
||||||
@ -145,8 +151,10 @@
|
|||||||
<Compile Include="PrimeSoft\ImageTHP.cs" />
|
<Compile Include="PrimeSoft\ImageTHP.cs" />
|
||||||
<Compile Include="ProjectMyu\ImageGAM.cs" />
|
<Compile Include="ProjectMyu\ImageGAM.cs" />
|
||||||
<Compile Include="Ransel\ArcBCD.cs" />
|
<Compile Include="Ransel\ArcBCD.cs" />
|
||||||
|
<Compile Include="Rare\ArcX.cs" />
|
||||||
<Compile Include="Regrips\AudioWRG.cs" />
|
<Compile Include="Regrips\AudioWRG.cs" />
|
||||||
<Compile Include="Regrips\ImagePRG.cs" />
|
<Compile Include="Regrips\ImagePRG.cs" />
|
||||||
|
<Compile Include="Rhss\ArcCRG.cs" />
|
||||||
<Compile Include="Rina\ImageRAD.cs" />
|
<Compile Include="Rina\ImageRAD.cs" />
|
||||||
<Compile Include="RSystem\ArcRAD.cs" />
|
<Compile Include="RSystem\ArcRAD.cs" />
|
||||||
<Compile Include="RSystem\ImageRSG.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))]
|
[Export(typeof(ArchiveFormat))]
|
||||||
public class MbmOpener : ArchiveFormat
|
public class MbmOpener : ArchiveFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "MBM"; } }
|
public override string Tag => "MBM";
|
||||||
public override string Description { get { return "Logg Adv engine resource archive"; } }
|
public override string Description => "Logg Adv engine resource archive";
|
||||||
public override uint Signature { get { return 0; } }
|
public override uint Signature => 0;
|
||||||
public override bool IsHierarchic { get { return false; } }
|
public override bool IsHierarchic => false;
|
||||||
public override bool CanWrite { get { return false; } }
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
{
|
{
|
||||||
@ -63,19 +63,27 @@ namespace GameRes.Formats.Logg
|
|||||||
|
|
||||||
IDictionary<uint, string> GetArchiveIndex (ArcView file)
|
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)
|
if (last_offset != file.MaxOffset)
|
||||||
return null;
|
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 file_map = new SortedDictionary<uint,string>();
|
||||||
var comma = new char[] {','};
|
var comma = new char[] {','};
|
||||||
FormatCatalog.Instance.ReadFileList ("logg_pl.lst", line => {
|
FormatCatalog.Instance.ReadFileList (list_name, line => {
|
||||||
var parts = line.Split (comma, 2);
|
var parts = line.Split (comma, 2);
|
||||||
uint offset = uint.Parse (parts[0], NumberStyles.HexNumber);
|
uint offset = uint.Parse (parts[0], NumberStyles.HexNumber);
|
||||||
if (2 == parts.Length)
|
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.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
// [030817][Splush Wave] Knock Out -Taisengata Datsui Mahjong-
|
||||||
|
|
||||||
namespace GameRes.Formats.SplushWave
|
namespace GameRes.Formats.SplushWave
|
||||||
{
|
{
|
||||||
@ -129,5 +132,92 @@ namespace GameRes.Formats.SplushWave
|
|||||||
}
|
}
|
||||||
return new BinMemoryStream (output, 0, dst);
|
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;
|
var meta = (SwgMetaData)info;
|
||||||
PixelFormat format = meta.BPP == 8 ? PixelFormats.Indexed8
|
PixelFormat format = meta.BPP == 8 ? PixelFormats.Indexed8
|
||||||
: meta.BPP == 32 ? PixelFormats.Bgr32 : PixelFormats.Bgr24;
|
: meta.BPP == 32 ? PixelFormats.Bgra32 : PixelFormats.Bgr24;
|
||||||
BitmapPalette palette = null;
|
BitmapPalette palette = null;
|
||||||
if (meta.BPP == 8)
|
if (meta.BPP == 8)
|
||||||
{
|
{
|
||||||
@ -77,36 +77,40 @@ namespace GameRes.Formats.SplushWave
|
|||||||
}
|
}
|
||||||
int stride = meta.iWidth * meta.BPP / 8;
|
int stride = meta.iWidth * meta.BPP / 8;
|
||||||
file.Position = meta.DataOffset;
|
file.Position = meta.DataOffset;
|
||||||
// var pixels = new byte[stride * meta.iHeight];
|
var pixels = new byte[stride * meta.iHeight];
|
||||||
var pixels = new byte[4 * meta.iWidth * meta.iHeight];
|
|
||||||
if (!meta.IsCompressed)
|
if (!meta.IsCompressed)
|
||||||
{
|
{
|
||||||
file.Read (pixels, 0, pixels.Length);
|
file.Read (pixels, 0, pixels.Length);
|
||||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
||||||
}
|
}
|
||||||
var input = file.ReadBytes ((int)(file.Length - file.Position));
|
if (!Decompress (file, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
||||||
if (!Decompress (input, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
|
||||||
throw new InvalidFormatException ("Invalid SWG file.");
|
throw new InvalidFormatException ("Invalid SWG file.");
|
||||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
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;
|
long start_pos = input.Position;
|
||||||
if (input[0] != 0 || input[1] != 1)
|
byte hi = input.ReadUInt8();
|
||||||
|
byte lo = input.ReadUInt8();
|
||||||
|
if (hi != 0 || lo != 1)
|
||||||
{
|
{
|
||||||
|
input.Position = start_pos;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (int i = 0; i < channels; ++i)
|
for (int i = 0; i < channels; ++i)
|
||||||
{
|
{
|
||||||
if (0 == input[i])
|
if (0 == input.ReadByte())
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
if (n != channels)
|
if (n != channels)
|
||||||
return false;
|
return false;
|
||||||
src = 4;
|
input.Position = start_pos + 4;
|
||||||
|
hi = input.ReadUInt8();
|
||||||
|
lo = input.ReadUInt8();
|
||||||
}
|
}
|
||||||
int compress_method = input[src+1] + (input[src] << 8);
|
int compress_method = lo | hi << 8;
|
||||||
src += 2;
|
|
||||||
if (0 == compress_method)
|
if (0 == compress_method)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < channels; ++i)
|
for (int i = 0; i < channels; ++i)
|
||||||
@ -115,7 +119,7 @@ namespace GameRes.Formats.SplushWave
|
|||||||
int count = height * width;
|
int count = height * width;
|
||||||
while (count --> 0)
|
while (count --> 0)
|
||||||
{
|
{
|
||||||
output[pos] = input[src++];
|
output[pos] = input.ReadUInt8();
|
||||||
pos += channels;
|
pos += channels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,61 +127,55 @@ namespace GameRes.Formats.SplushWave
|
|||||||
}
|
}
|
||||||
if (compress_method != 1)
|
if (compress_method != 1)
|
||||||
return false;
|
return false;
|
||||||
int dst = 0;
|
int stride = width * channels;
|
||||||
int v33 = src;
|
var row_sizes = input.ReadBytes (2 * height * channels);
|
||||||
int v37 = height * channels;
|
int ctl_pos = 0;
|
||||||
src += 2 * v37;
|
for (int c = 0; c < channels; ++c)
|
||||||
for (int row = 0; row < v37; ++row)
|
for (int y = height - 1; y >= 0; --y)
|
||||||
{
|
{
|
||||||
int y = row % height;
|
int dst = stride * y + PlaneMap[c];
|
||||||
dst = channels * (width * (height - y - 1) + 1) - row / height - 1;
|
int row_size = row_sizes[ctl_pos+1] | row_sizes[ctl_pos] << 8;
|
||||||
if (dst > output.Length)
|
ctl_pos += 2;
|
||||||
|
DecompressRow (input, row_size, output, dst, channels);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
int v24 = 0;
|
}
|
||||||
int v36 = input[v33+1] + (input[v33] << 8);
|
|
||||||
v33 += 2;
|
internal static void DecompressRow (IBinaryStream input, int row_size, byte[] output, int dst, int step)
|
||||||
do
|
|
||||||
{
|
{
|
||||||
byte lo = input[src];
|
int x = 0;
|
||||||
byte hi = input[src+1];
|
while (x < row_size)
|
||||||
if (lo != 0)
|
|
||||||
{
|
{
|
||||||
if (lo < 0x81)
|
byte ctl = input.ReadUInt8();
|
||||||
|
if (ctl == 0)
|
||||||
{
|
{
|
||||||
++src;
|
byte v = input.ReadUInt8();
|
||||||
int count = lo + 1;
|
x += 2;
|
||||||
v24 += count + 1;
|
output[dst] = v;
|
||||||
|
dst += step;
|
||||||
|
}
|
||||||
|
else if (ctl < 0x81u)
|
||||||
|
{
|
||||||
|
int count = ctl + 1;
|
||||||
|
x += count + 1;
|
||||||
while (count --> 0)
|
while (count --> 0)
|
||||||
{
|
{
|
||||||
output[dst] = input[src++];
|
output[dst] = input.ReadUInt8();
|
||||||
dst += channels;
|
dst += step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
src += 2;
|
byte v = input.ReadUInt8();
|
||||||
v24 += 2;
|
x += 2;
|
||||||
int count = Math.Min (0x101 - lo, output.Length - dst);
|
int count = 0x101 - ctl;
|
||||||
while (count --> 0)
|
while (count --> 0)
|
||||||
{
|
{
|
||||||
output[dst] = hi;
|
output[dst] = v;
|
||||||
dst += channels;
|
dst += step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
src += 2;
|
|
||||||
v24 += 2;
|
|
||||||
output[dst] = hi;
|
|
||||||
dst += channels;
|
|
||||||
}
|
|
||||||
if (dst >= output.Length)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
while (v24 < v36);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write (Stream file, ImageData image)
|
public override void Write (Stream file, ImageData image)
|
||||||
|
Loading…
Reference in New Issue
Block a user