updated legacy formats.

(BIZ): moved to Adviz folder.
(GIZ, GIZ2, BIZ2, PR1): new PC-98 image formats.
(DATA, BGM, SED): MyHarvest resource formats.
(HTF): compressed image format.
(PAK): Mina resource archives.
(GCmp): read palette from external resource.
(NCG): Nekotaro image format.
(BND, TCZ, TSZ): Ponytail Soft PC-98 formats.
(NOR): Sophia resource archive.
(SDA, PLA): Squadra D resource archives.
(UCA): added checks to avoid false positives.
This commit is contained in:
morkt 2023-10-11 19:03:45 +04:00
parent 4d26cdcff6
commit f90e2e851c
24 changed files with 3504 additions and 279 deletions

366
Legacy/Adviz/ImageBIZ.cs Normal file
View File

@ -0,0 +1,366 @@
//! \file ImageBIZ.cs
//! \date 2023 Sep 30
//! \brief ADVIZ engine 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 System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
// [970829][Ange] Coin
namespace GameRes.Formats.Adviz
{
[Export(typeof(ImageFormat))]
public class BizFormat : ImageFormat
{
public override string Tag => "BIZ";
public override string Description => "ADVIZ engine image format";
public override uint Signature => 0;
const byte DefaultKey = 0x39;
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (!file.Name.HasExtension (".BIZ"))
return null;
var header = file.ReadHeader (4);
uint width = header.ToUInt16 (0);
uint height = header.ToUInt16 (2);
if (width * height + 4 != file.Length)
return null;
return new ImageMetaData {
Width = width,
Height = height,
BPP = 8,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var palette = ReadPalette (file.Name, 0x300, (pal, off) => ReadPalette (pal, off, 0x100, PaletteFormat.Rgb));
if (null == palette)
throw new FileNotFoundException ("Unable to retrieve palette.");
file.Position = 4;
var pixels = file.ReadBytes (info.iWidth * info.iHeight);
byte key = DefaultKey;
for (int i = 0; i < pixels.Length; ++i)
{
pixels[i] ^= key;
key += pixels[i];
}
return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, pixels, info.iWidth);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("BizFormat.Write not implemented");
}
internal delegate BitmapPalette PaletteReader (ArcView file, int offset);
static readonly Regex TachieRe = new Regex (@"^(T[^._]+_)[2-9][^.]*\.GIZ$", RegexOptions.Compiled);
internal static BitmapPalette ReadPalette (string base_name, int pal_size, PaletteReader read_pal)
{
var dir_name = Path.GetDirectoryName (base_name);
var grp_tbl_name = Path.Combine (dir_name, @"..\GRP_TBL.SYS");
var plt_tbl_name = Path.Combine (dir_name, @"..\PLT_TBL.SYS");
if (!File.Exists (grp_tbl_name) || !File.Exists (plt_tbl_name))
return null;
int index = 0;
uint grp_size = 0;
base_name = Path.GetFileName (base_name).ToUpperInvariant();
var name = base_name;
var ext = Path.GetExtension (name).TrimStart('.');
var match = TachieRe.Match (name);
if (match.Success)
name = match.Groups[1].Value + "1";
else
name = Path.GetFileNameWithoutExtension (name);
if (name.Length < 8)
name += ' ';
using (var grp = new ArcView (grp_tbl_name))
{
grp_size = (uint)grp.MaxOffset;
int pos = 0;
while (pos + 12 <= grp.MaxOffset)
{
if (grp.View.AsciiEqual (pos, name) &&
grp.View.AsciiEqual (pos+8, ext))
{
break;
}
++index;
pos += 12;
}
if (pos >= grp.MaxOffset)
return null;
}
using (var pal = new ArcView (plt_tbl_name))
{
uint plt_size = (uint)pal.MaxOffset;
var id = new GrpIdentifier (grp_size, plt_size);
IGrpMapper mapper;
if (!GrpMap.TryGetValue (id, out mapper))
mapper = new DirectMapper();
index = mapper.GetPaletteIndex (index, base_name);
int pal_offset = index * pal_size;
if (pal_offset + pal_size > pal.MaxOffset)
{
int count = (int)(pal.MaxOffset / pal_size) - 1;
pal_offset = count * pal_size;
}
return read_pal (pal, pal_offset);
}
}
static readonly Dictionary<GrpIdentifier, IGrpMapper> GrpMap = new Dictionary<GrpIdentifier, IGrpMapper> {
{ new GrpIdentifier (1584, 139008), new GrpShiftMapper (52) },
{ new GrpIdentifier (2160, 12288),
new GrpNameMapper { NameMap = new Dictionary<string, int> {
{ "BG01.BIZ", 14 },
{ "BG02.BIZ", 8 },
{ "BG03.BIZ", 8 },
{ "BG04.BIZ", 8 },
{ "BG05.BIZ", 8 },
{ "BG06.BIZ", 8 },
{ "BG07.BIZ", 8 },
{ "BG08.BIZ", 8 },
{ "BG09.BIZ", 8 },
{ "BG10.BIZ", 8 },
{ "BG11.BIZ", 8 },
{ "BG12.BIZ", 8 },
{ "BG13.BIZ", 8 },
{ "BG14.BIZ", 8 },
{ "BG15.BIZ", 8 },
{ "BG16.BIZ", 8 },
{ "BG17.BIZ", 8 },
{ "BG18.BIZ", 8 },
{ "BG19.BIZ", 8 },
{ "CA01.BIZ", 12 },
{ "CA02.BIZ", 12 },
{ "CA03.BIZ", 4 },
{ "CA04.BIZ", 8 },
{ "CA05.BIZ", 8 },
{ "CA06.BIZ", 8 },
{ "CA07.BIZ", 8 },
{ "CA08.BIZ", 8 },
{ "CA09.BIZ", 8 },
{ "CA10.BIZ", 8 },
{ "CA11.BIZ", 8 },
{ "CA12.BIZ", 8 },
{ "CA13.BIZ", 8 },
{ "CA14.BIZ", 8 },
{ "CA15.BIZ", 8 },
{ "CA16.BIZ", 8 },
{ "CA17.BIZ", 8 },
{ "CA18.BIZ", 8 },
{ "CA19.BIZ", 8 },
{ "CA20.BIZ", 8 },
{ "CA21.BIZ", 8 },
{ "CA22.BIZ", 8 },
{ "CA23.BIZ", 8 },
{ "CA24.BIZ", 8 },
{ "CA25.BIZ", 8 },
{ "CA26.BIZ", 8 },
{ "CA27.BIZ", 8 },
{ "CA28.BIZ", 8 },
{ "CA29.BIZ", 8 },
{ "CA30.BIZ", 8 },
{ "CA31.BIZ", 8 },
{ "CA32.BIZ", 8 },
{ "CA33.BIZ", 8 },
{ "CA34.BIZ", 8 },
{ "CA35.BIZ", 8 },
{ "CA36.BIZ", 8 },
{ "CA37.BIZ", 8 },
{ "CA38.BIZ", 8 },
{ "CA39.BIZ", 8 },
{ "CA40.BIZ", 8 },
{ "CA41.BIZ", 8 },
{ "CA42.BIZ", 8 },
{ "CA43.BIZ", 8 },
{ "CA44.BIZ", 8 },
{ "CA45.BIZ", 8 },
{ "CA46.BIZ", 8 },
{ "CA47.BIZ", 8 },
{ "CA48.BIZ", 8 },
{ "CA49.BIZ", 8 },
{ "CA50.BIZ", 8 },
{ "CA51.BIZ", 8 },
{ "CA52.BIZ", 8 },
{ "CA53.BIZ", 8 },
{ "CA54.BIZ", 8 },
{ "CA55.BIZ", 8 },
{ "CA56.BIZ", 8 },
{ "CA57.BIZ", 8 },
{ "CA58.BIZ", 8 },
{ "CA59.BIZ", 8 },
{ "CA60.BIZ", 8 },
{ "E02.BIZ", 8 },
{ "E03.BIZ", 8 },
{ "E04.BIZ", 8 },
{ "E05.BIZ", 8 },
{ "E06.BIZ", 8 },
{ "E07.BIZ", 8 },
{ "E08.BIZ", 8 },
{ "E09.BIZ", 8 },
{ "E10.BIZ", 8 },
{ "E11.BIZ", 8 },
{ "E12.BIZ", 8 },
{ "E13.BIZ", 8 },
{ "E14.BIZ", 8 },
{ "E15.BIZ", 8 },
{ "E16.BIZ", 8 },
{ "E17.BIZ", 8 },
{ "E18.BIZ", 8 },
{ "END.BIZ", 6 },
{ "IPL.BIZ", 12 },
{ "S01.BIZ", 8 },
{ "S02.BIZ", 8 },
{ "S03.BIZ", 8 },
{ "S04.BIZ", 8 },
{ "S05.BIZ", 8 },
{ "S06.BIZ", 8 },
{ "S07.BIZ", 8 },
{ "S08.BIZ", 8 },
{ "S09.BIZ", 8 },
{ "S10.BIZ", 8 },
{ "S11.BIZ", 8 },
{ "S12.BIZ", 8 },
{ "S13.BIZ", 8 },
{ "S14.BIZ", 8 },
{ "S15.BIZ", 8 },
{ "S16.BIZ", 8 },
{ "S17.BIZ", 8 },
{ "S18.BIZ", 8 },
{ "S19.BIZ", 8 },
{ "S20.BIZ", 8 },
{ "S21.BIZ", 8 },
{ "S22.BIZ", 8 },
{ "S23.BIZ", 8 },
{ "S24.BIZ", 8 },
{ "S25.BIZ", 8 },
{ "S26.BIZ", 8 },
{ "S27.BIZ", 8 },
{ "S28.BIZ", 8 },
{ "S29.BIZ", 8 },
{ "S30.BIZ", 8 },
{ "S31.BIZ", 8 },
{ "S32.BIZ", 8 },
{ "S33.BIZ", 8 },
{ "S34.BIZ", 8 },
{ "S35.BIZ", 8 },
{ "S36.BIZ", 8 },
{ "S37.BIZ", 8 },
{ "S38.BIZ", 8 },
{ "S39.BIZ", 8 },
{ "S40.BIZ", 8 },
{ "S41.BIZ", 8 },
{ "S42.BIZ", 8 },
{ "S43.BIZ", 8 },
{ "S44.BIZ", 8 },
{ "S45.BIZ", 8 },
{ "S46.BIZ", 8 },
{ "S47.BIZ", 8 },
{ "S48.BIZ", 8 },
{ "S49.BIZ", 8 },
{ "T01.BIZ", 8 },
{ "T02.BIZ", 8 },
{ "T03.BIZ", 8 },
{ "WAKU1.BIZ", 8 },
{ "WAKU2.BIZ", 8 },
} } },
};
}
public struct GrpIdentifier
{
public uint GrpSize;
public uint PltSize;
public GrpIdentifier (uint grp_size, uint plt_size)
{
GrpSize = grp_size;
PltSize = plt_size;
}
public override int GetHashCode ()
{
return (int)((GrpSize + 1) * (PltSize + 1));
}
public override bool Equals (object obj)
{
if (null == obj)
return false;
var other = (GrpIdentifier)obj;
return this.GrpSize == other.GrpSize && this.PltSize == other.PltSize;
}
}
internal interface IGrpMapper
{
int GetPaletteIndex (int id, string name);
}
internal class DirectMapper : IGrpMapper
{
public int GetPaletteIndex (int id, string name)
{
return id;
}
}
internal class GrpShiftMapper : IGrpMapper
{
int m_shift;
public GrpShiftMapper (int shift)
{
m_shift = shift;
}
public int GetPaletteIndex (int id, string name)
{
return id + m_shift;
}
}
internal class GrpNameMapper : IGrpMapper
{
public Dictionary<string, int> NameMap;
public int GetPaletteIndex (int id, string name)
{
int index;
if (NameMap.TryGetValue (name, out index))
return index;
return id;
}
}
}

View File

@ -1,6 +1,6 @@
//! \file ImageBIZ.cs
//! \file ImageBIZ2.cs
//! \date 2018 Feb 11
//! \brief Sorciere compressed image.
//! \brief ADVIZ engine compressed image.
//
// Copyright (C) 2018 by morkt
//
@ -29,14 +29,15 @@ using System.Windows.Media;
using GameRes.Compression;
// [000225][Sorciere] Karei
// [011012][Ange] Nyuunyuu
namespace GameRes.Formats.Sorciere
namespace GameRes.Formats.Adviz
{
[Export(typeof(ImageFormat))]
public class BizFormat : ImageFormat
public class Biz2Format : ImageFormat
{
public override string Tag { get { return "BIZ"; } }
public override string Description { get { return "Sorciere compressed image"; } }
public override string Tag { get { return "BIZ/2"; } }
public override string Description { get { return "ADVIZ engine compressed image"; } }
public override uint Signature { get { return 0x325A4942; } } // 'BIZ2'
public override ImageMetaData ReadMetaData (IBinaryStream file)
@ -52,13 +53,34 @@ namespace GameRes.Formats.Sorciere
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
file.Position = 8;
using (var input = new LzssStream (file.AsStream, LzssMode.Decompress, true))
using (var lzss = new LzssStream (file.AsStream, LzssMode.Decompress, true))
using (var input = new BinaryStream (lzss, file.Name))
{
int stride = (int)info.Width * 3;
var pixels = new byte[stride * (int)info.Height];
if (pixels.Length != input.Read (pixels, 0, pixels.Length))
int stride = info.iWidth * 3;
var rgb = new byte[stride * info.Height];
if (rgb.Length != input.Read (rgb, 0, rgb.Length))
throw new InvalidFormatException();
return ImageData.CreateFlipped (info, PixelFormats.Bgr24, null, pixels, stride);
if (input.PeekByte() != -1) // possible alpha channel
{
var alpha = input.ReadBytes (rgb.Length);
if (alpha.Length == rgb.Length)
{
int stride32bpp = info.iWidth * 4;
var rgba = new byte[stride32bpp * info.iHeight];
int src = 0;
int dst = 0;
while (src < rgb.Length)
{
rgba[dst++] = rgb[src ];
rgba[dst++] = rgb[src+1];
rgba[dst++] = rgb[src+2];
rgba[dst++] = alpha[src]; // presumably it's grayscale and R/G/B values are equal
src += 3;
}
return ImageData.CreateFlipped (info, PixelFormats.Bgra32, null, rgba, stride32bpp);
}
}
return ImageData.CreateFlipped (info, PixelFormats.Bgr24, null, rgb, stride);
}
}

309
Legacy/Adviz/ImageGIZ.cs Normal file
View File

@ -0,0 +1,309 @@
//! \file ImageGIZ.cs
//! \date 2023 Oct 02
//! \brief ADVIZ engine image format (PC-98).
//
// 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
// [960830][Ange] Coin
namespace GameRes.Formats.Adviz
{
internal class GizMetaData : ImageMetaData
{
public byte RleCode;
public byte PlaneMap;
public bool HasPalette;
}
[Export(typeof(ImageFormat))]
public class Giz3Format : ImageFormat
{
public override string Tag => "GIZ";
public override string Description => "ADVIZ engine image format";
public override uint Signature => 0x335A4947; // 'GIZ3'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
int xy = header.ToUInt16 (4);
return new GizMetaData {
Width = (uint)header.ToUInt16 (6) << 3,
Height = header.ToUInt16 (8),
OffsetX = (xy % 0x50) << 3,
OffsetY = xy / 0x50,
HasPalette = header[0xC] != 0,
PlaneMap = header[0xE],
BPP = 4,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new Giz3Reader (file, (GizMetaData)info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("Giz3Format.Write not implemented");
}
}
internal class Giz3Reader
{
IBinaryStream m_input;
GizMetaData m_info;
BitmapPalette m_palette;
int m_stride;
int m_output_stride;
public Giz3Reader (IBinaryStream input, GizMetaData info)
{
m_input = input;
m_info = info;
}
byte[] m_buffer;
byte[] m_output;
public ImageData Unpack ()
{
m_input.Position = 0x10;
if (m_info.HasPalette)
m_palette = ReadPalette();
else
m_palette = BitmapPalettes.Gray16; // palette is stored somewhere else
long data_pos = m_input.Position;
ReadHuffmanTree();
data_pos += m_dataOffset;
m_bitCount = 1;
m_stride = m_info.iWidth >> 3;
m_buffer = new byte[0x2000];
m_output_stride = m_info.iWidth >> 1;
m_output = new byte[m_output_stride * m_info.iHeight];
m_input.Position = data_pos;
UnpackBits();
return ImageData.Create (m_info, PixelFormats.Indexed4, m_palette, m_output, m_output_stride);
}
int m_dataOffset;
int m_gizColumn;
int m_outputPos1;
int m_outputPos2;
void UnpackBits () // sub_15426
{
m_gizColumn = 0;
m_outputPos1 = 0;
m_outputPos2 = 0;
int dst = 0;
for (int x = 0; x < m_stride; ++x)
{
int src1 = m_outputPos1;
int src2 = 0;
for (int j = 0; j < 2; ++j)
{
src2 = m_outputPos1;
int plane_mask = 1;
for (int i = 0; i < 4; ++i)
{
if ((m_info.PlaneMap & plane_mask) == 0)
{
UnpackPlane (m_outputPos1);
}
plane_mask <<= 1;
m_outputPos1 += 0x800;
m_outputPos2 += 0x800;
}
m_gizColumn = (m_gizColumn + 1) & 3;
m_outputPos1 = m_gizColumn << 9;
m_outputPos2 = 0;
}
CopyPlanes (src1, src2, dst);
dst += 4;
}
}
void CopyPlanes (int src1, int src2, int dst)
{
for (int y = 0; y < m_info.iHeight; ++y)
{
int b0 = m_buffer[src1+y ] << 4 | m_buffer[src2+y ];
int b1 = m_buffer[src1+y+0x0800] << 4 | m_buffer[src2+y+0x0800];
int b2 = m_buffer[src1+y+0x1000] << 4 | m_buffer[src2+y+0x1000];
int b3 = m_buffer[src1+y+0x1800] << 4 | m_buffer[src2+y+0x1800];
for (int j = 0; j < 8; j += 2)
{
byte px = (byte)((((b0 << j) & 0x80) >> 3)
| (((b1 << j) & 0x80) >> 2)
| (((b2 << j) & 0x80) >> 1)
| (((b3 << j) & 0x80) ));
px |= (byte)((((b0 << j) & 0x40) >> 6)
| (((b1 << j) & 0x40) >> 5)
| (((b2 << j) & 0x40) >> 4)
| (((b3 << j) & 0x40) >> 3));
m_output[dst+j/2] = px;
}
dst += m_output_stride;
}
}
int m_root;
ushort[] m_treeTable;
void ReadHuffmanTree ()
{
m_dataOffset = m_input.ReadUInt16();
m_treeTable = new ushort[(m_dataOffset-2) * 2 / 3 + 1];
int di = 0;
for (int si = 2; si + 2 < m_dataOffset; si += 3)
{
ushort bx = m_input.ReadUInt16();
int ax = bx & 0xFFF;
if ((ax & 0x800) == 0)
ax = (ax - 2) >> 1;
m_treeTable[di++] = (ushort)ax;
ax = m_input.ReadUInt8() << 4;
ax |= bx >> 12;
if ((ax & 0x800) == 0)
ax = (ax - 2) >> 1;
m_treeTable[di++] = (ushort)ax;
}
m_root = di - 2;
}
byte ReadToken ()
{
int token = m_root;
do
{
if (GetNextBit())
++token;
token = m_treeTable[token];
}
while ((token & 0x800) == 0);
return (byte)token;
}
void UnpackPlane (int dst)
{
int y = 0;
while (y < m_info.iHeight)
{
byte ctl = ReadToken();
if (ctl < 0x10)
{
m_buffer[dst++] = ctl;
++y;
}
else
{
int count = ReadToken() + 2;
ctl -= 0x10;
switch (ctl)
{
case 0:
for (int i = 0; i < count; ++i)
m_buffer[dst+i] = 0;
break;
case 1:
for (int i = 0; i < count; ++i)
m_buffer[dst+i] = 0xF;
break;
case 2:
Binary.CopyOverlapped (m_buffer, dst-1, dst, count);
break;
case 3:
Binary.CopyOverlapped (m_buffer, dst-2, dst, count);
break;
case 4:
case 5:
case 6:
{
int off = (ctl - 3) << 11;
Binary.CopyOverlapped (m_buffer, dst-off, dst, count);
break;
}
case 7:
{
int src = dst - m_outputPos1;
int ax = (m_gizColumn - 1) & 3;
src += (ax << 9) + m_outputPos2;
Binary.CopyOverlapped (m_buffer, src, dst, count);
break;
}
case 8:
{
int src = dst - m_outputPos1;
int ax = (m_gizColumn - 2) & 3;
src += (ax << 9) + m_outputPos2;
Binary.CopyOverlapped (m_buffer, src, dst, count);
break;
}
}
dst += count;
y += count;
}
}
}
BitmapPalette ReadPalette ()
{
const int count = 16;
var colors = new Color[count];
for (int i = 0; i < count; ++i)
{
byte b = m_input.ReadUInt8();
byte r = m_input.ReadUInt8();
byte g = m_input.ReadUInt8();
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
}
return new BitmapPalette (colors);
}
int m_bitCount;
int m_bits;
bool GetNextBit ()
{
if (--m_bitCount == 0)
{
m_bits = m_input.ReadUInt16();
m_bitCount = 16;
}
bool bit = (m_bits & 0x8000) != 0;
m_bits <<= 1;
return bit;
}
}
}

231
Legacy/Adviz/ImageGIZ2.cs Normal file
View File

@ -0,0 +1,231 @@
//! \file ImageGIZ2.cs
//! \date 2023 Oct 02
//! \brief ADVIZ engine image format (PC-98).
//
// 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
// [951027][Ange] Leap Toki ni Sarawareta Shoujo
namespace GameRes.Formats.Adviz
{
[Export(typeof(ImageFormat))]
public class GizFormat : ImageFormat
{
public override string Tag => "GIZ/2";
public override string Description => "ADVIZ engine image format";
public override uint Signature => 0x325A4947; // 'GIZ2'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
int xy = header.ToUInt16 (4);
return new GizMetaData {
Width = (uint)header.ToUInt16 (6) << 3,
Height = header.ToUInt16 (8),
OffsetX = (xy % 0x50) << 3,
OffsetY = xy / 0x50,
RleCode = header[0xC],
PlaneMap = header[0xE],
BPP = 4,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new Giz2Reader (file, (GizMetaData)info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("GizFormat.Write not implemented");
}
}
internal class Giz2Reader
{
IBinaryStream m_input;
GizMetaData m_info;
BitmapPalette m_palette;
public BitmapPalette Palette => m_palette;
public Giz2Reader (IBinaryStream input, GizMetaData info)
{
m_input = input;
m_info = info;
}
int m_stride;
byte[][] m_planes;
int m_output_stride;
byte[] m_output;
public ImageData Unpack ()
{
m_palette = BizFormat.ReadPalette (m_input.Name, 0x30, (pal, off) => ReadPalette (pal, off));
if (null == m_palette)
{
// m_palette = BitmapPalettes.Gray16;
throw new FileNotFoundException ("Unable to retrieve palette.");
}
m_input.Position = 0x10;
m_stride = m_info.iWidth >> 3;
int plane_size = m_info.iHeight;
m_planes = new byte[][] {
new byte[plane_size], new byte[plane_size], new byte[plane_size], new byte[plane_size],
};
m_output_stride = m_info.iWidth >> 1;
m_output = new byte[m_output_stride * m_info.iHeight];
int dst = 0;
for (int x = 0; x < m_stride; ++x)
{
int plane_mask = 1;
for (int i = 0; i < 4; ++i)
{
if ((m_info.PlaneMap & plane_mask) == 0)
UnpackPlane (m_planes[i], 0);
plane_mask <<= 1;
}
CopyPlanes (dst);
dst += 4;
}
return ImageData.Create (m_info, PixelFormats.Indexed4, Palette, m_output, m_output_stride);
}
bool UnpackPlane (byte[] output, int dst)
{
for (int y = 0; y < m_info.iHeight; )
{
byte b = m_input.ReadUInt8();
int ctl = (b - m_info.RleCode) & 0xFF;
if (2 == ctl)
{
output[dst++] = m_input.ReadUInt8();
}
else if (ctl < 4)
{
if (0 == ctl)
b = 0;
else if (1 == ctl)
b = 0xFF;
else
b = m_input.ReadUInt8();
int count = ((m_input.ReadUInt8() - 1) & 0xFF) + 1;
y += count;
while (count --> 0)
{
output[dst++] = b;
}
continue;
}
else if (ctl < 7)
{
byte b0 = m_input.ReadUInt8();
byte b1 = m_input.ReadUInt8();
int count;
if (4 == ctl)
{
count = ((b1 - 1) & 0x7F) + 1;
if (b1 < 0x80)
b1 = Binary.RotByteL (b0, 1);
else
b1 = Binary.RotByteR (b0, 1);
}
else if (5 == ctl)
{
count = ((b1 - 1) & 0x7F) + 1;
if (b1 < 0x80)
b1 = Binary.RotByteL (b0, 2);
else
b1 = Binary.RotByteR (b0, 2);
}
else
{
count = ((m_input.ReadUInt8() - 1) & 0xFF) + 1;
count *= 2;
}
y += count;
do
{
output[dst++] = b0;
if (--count <= 0)
break;
output[dst++] = b1;
}
while (--count > 0);
continue;
}
else
{
output[dst++] = b;
}
++y;
}
return true;
}
void CopyPlanes (int dst)
{
for (int y = 0; y < m_info.iHeight; ++y)
{
int b0 = m_planes[0][y];
int b1 = m_planes[1][y];
int b2 = m_planes[2][y];
int b3 = m_planes[3][y];
for (int j = 0; j < 8; j += 2)
{
byte px = (byte)((((b0 << j) & 0x80) >> 3)
| (((b1 << j) & 0x80) >> 2)
| (((b2 << j) & 0x80) >> 1)
| (((b3 << j) & 0x80) ));
px |= (byte)((((b0 << j) & 0x40) >> 6)
| (((b1 << j) & 0x40) >> 5)
| (((b2 << j) & 0x40) >> 4)
| (((b3 << j) & 0x40) >> 3));
m_output[dst+j/2] = px;
}
dst += m_output_stride;
}
}
BitmapPalette ReadPalette (ArcView file, int offset)
{
const int count = 16;
var colors = new Color[count];
for (int i = 0; i < count; ++i)
{
byte b = file.View.ReadByte (offset++);
byte r = file.View.ReadByte (offset++);
byte g = file.View.ReadByte (offset++);
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
}
return new BitmapPalette (colors);
}
}
}

View File

@ -0,0 +1,80 @@
//! \file ImageAN1.cs
//! \date 2023 Oct 05
//! \brief Discovery animation resource (PC-98).
//
// 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.Discovery
{
//[Export(typeof(ImageFormat))]
public class An1Format : Pr1Format
{
public override string Tag => "AN1";
public override string Description => "Discovery animation resource";
public override uint Signature => 0;
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (!file.Name.HasExtension (".AN1"))
return null;
return base.ReadMetaData (file);
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new AnReader (file, (PrMetaData)info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("An1Format.Write not implemented");
}
}
internal class AnReader : PrReader
{
public AnReader (IBinaryStream file, PrMetaData info) : base (file, info)
{
}
public new ImageData Unpack ()
{
UnpackPlanes();
int frame_count = m_planes[0].ToUInt16 (2);
int frame_width = 0x20;
int frame_height = frame_count * 0x20;
int output_stride = frame_width >> 1;
var output = new byte[output_stride * frame_height];
int src = frame_count * 0x16 + 6;
m_plane_size = (output_stride >> 2) * frame_height;
FlattenPlanes (src, output);
Info.Width = (uint)frame_width;
Info.Height = (uint)frame_height;
return ImageData.Create (Info, PixelFormats.Indexed4, m_palette, output, output_stride);
}
}
}

View File

@ -0,0 +1,343 @@
//! \file ImagePR1.cs
//! \date 2023 Oct 04
//! \brief Discovery image format (PC-98).
//
// 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.Discovery
{
internal class PrMetaData : ImageMetaData
{
public byte Flags;
public byte Mask;
public bool IsLeftToRight => (Flags & 1) != 0;
}
[Export(typeof(ImageFormat))]
public class Pr1Format : ImageFormat
{
public override string Tag => "PR1";
public override string Description => "Discovery image format";
public override uint Signature => 0;
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (!file.Name.HasAnyOfExtensions (".PR1", ".AN1"))
return null;
var header = file.ReadHeader (12);
return new PrMetaData {
Width = (uint)header.ToUInt16 (8) << 3,
Height = header.ToUInt16 (0xA),
OffsetX = header.ToUInt16 (2),
OffsetY = header.ToUInt16 (4),
Flags = header[0],
Mask = header[1],
BPP = 4,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new PrReader (file, (PrMetaData)info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("Pr1Format.Write not implemented");
}
}
internal class PrReader
{
IBinaryStream m_input;
PrMetaData m_info;
Action IncrementDest;
Func<bool> IsDone;
public PrMetaData Info => m_info;
public PrReader (IBinaryStream file, PrMetaData info)
{
m_input = file;
m_info = info;
if (m_info.IsLeftToRight)
{
IncrementDest = IncLeftToRight;
IsDone = () => m_dst >= m_plane_size;
}
else
{
IncrementDest = IncTopToBottom;
IsDone = () => m_x >= m_stride;
}
}
protected BitmapPalette m_palette;
protected int m_stride;
protected int m_plane_size;
protected byte[][] m_planes;
int m_dst;
int m_x;
protected void UnpackPlanes ()
{
const int buffer_slice = 0x410;
m_input.Position = 0xC;
m_palette = ReadPalette();
m_stride = m_info.iWidth >> 3;
m_plane_size = m_stride * m_info.iHeight;
m_planes = new byte[][] {
new byte[m_plane_size], new byte[m_plane_size], new byte[m_plane_size], new byte[m_plane_size],
};
var buffer = new byte[buffer_slice * 4];
var buf_count = new byte[4];
var offsets = new int[] { 0, buffer_slice, buffer_slice*2, buffer_slice*3 };
m_dst = 0;
m_x = 0;
while (!IsDone())
{
int ctl = m_input.ReadByte();
if (-1 == ctl)
break;
int count = (ctl & 0x1F) + 1;
bool bit = (ctl & 0x20) != 0;
ctl >>= 6;
if (!bit)
{
if (ctl != 0)
{
int src_pos = ctl;
int src_count2 = 1 << (ctl - 1);
int pos = offsets[ctl];
int count2 = src_count2;
do
{
byte p0 = m_input.ReadUInt8();
byte p1 = m_input.ReadUInt8();
byte p2 = m_input.ReadUInt8();
byte p3 = m_input.ReadUInt8();
PutPixels (p0, p1, p2, p3);
buffer[pos++] = p0;
buffer[pos++] = p1;
buffer[pos++] = p2;
buffer[pos++] = p3;
}
while (--count > 0 && --count2 > 0);
while (count > 0)
{
int si = offsets[src_pos];
for (int i = 0; i < src_count2; ++i)
{
byte p0 = buffer[si++];
byte p1 = buffer[si++];
byte p2 = buffer[si++];
byte p3 = buffer[si++];
PutPixels (p0, p1, p2, p3);
if (--count <= 0)
break;
}
}
offsets[src_pos] += src_count2 * 4;
buf_count[src_pos] += (byte)src_count2;
if (buf_count[src_pos] == 0)
offsets[src_pos] = src_pos * buffer_slice;
}
else
{
while (count --> 0)
{
byte p0 = m_input.ReadUInt8();
byte p1 = m_input.ReadUInt8();
byte p2 = m_input.ReadUInt8();
byte p3 = m_input.ReadUInt8();
PutPixels (p0, p1, p2, p3);
int pos = offsets[0];
buffer[pos++] = p0;
buffer[pos++] = p1;
buffer[pos++] = p2;
buffer[pos++] = p3;
offsets[0] += 4;
buf_count[0]++;
if (0 == buf_count[0])
offsets[0] = 0;
}
}
}
else if (ctl != 0)
{
int count2 = 1 << (ctl - 1);
int off_diff = count2 << 2;
int off_mask = off_diff - 1;
int off = m_input.ReadUInt8() << 2;;
int base_pos = ctl * buffer_slice;
off += base_pos;
int src = off;
while (count > 0)
{
off = src;
for (int i = 0; i < count2; ++i)
{
byte p0 = buffer[off];
byte p1 = buffer[off+1];
byte p2 = buffer[off+2];
byte p3 = buffer[off+3];
PutPixels (p0, p1, p2, p3);
off += 4;
int pos = off - base_pos;
if ((pos & off_mask) == 0)
off -= off_diff;
if (--count <= 0)
break;
}
}
}
else
{
while (count --> 0)
{
int off = m_input.ReadUInt8() << 2;
byte p0 = buffer[off];
byte p1 = buffer[off+1];
byte p2 = buffer[off+2];
byte p3 = buffer[off+3];
PutPixels (p0, p1, p2, p3);
}
}
}
}
public ImageData Unpack ()
{
UnpackPlanes();
int output_stride = m_info.iWidth >> 1;
var output = new byte[output_stride * m_info.iHeight];
FlattenPlanes (0, output);
return ImageData.Create (m_info, PixelFormats.Indexed4, m_palette, output, output_stride);
}
void PutPixels (byte p0, byte p1, byte p2, byte p3)
{
if (0xFF == m_info.Mask || true) // we don't do overlaying here, just single image decoding
{
m_planes[0][m_dst] = p0;
m_planes[1][m_dst] = p1;
m_planes[2][m_dst] = p2;
m_planes[3][m_dst] = p3;
}
else
{
byte v = m_info.Mask;
byte mask = p0;
if ((v & 1) != 0)
mask = (byte)~mask;
if ((v & 2) != 0)
mask |= (byte)~p1;
else
mask |= p1;
if ((v & 4) != 0)
mask |= (byte)~p2;
else
mask |= p2;
if ((v & 8) != 0)
mask |= (byte)~p3;
else
mask |= p3;
p0 &= mask;
p1 &= mask;
p2 &= mask;
p3 &= mask;
mask = (byte)~mask;
m_planes[0][m_dst] &= mask;
m_planes[0][m_dst] |= p0;
m_planes[1][m_dst] &= mask;
m_planes[1][m_dst] |= p1;
m_planes[2][m_dst] &= mask;
m_planes[2][m_dst] |= p2;
m_planes[3][m_dst] &= mask;
m_planes[3][m_dst] |= p3;
}
IncrementDest();
}
void IncLeftToRight ()
{
++m_dst;
++m_x;
if (m_x > m_info.iWidth)
m_x = 0;
}
void IncTopToBottom ()
{
m_dst += m_stride;
if (m_dst >= m_plane_size)
m_dst = ++m_x;
}
internal void FlattenPlanes (int src, byte[] output)
{
int m_dst = 0;
for (; src < m_plane_size; ++src)
{
int b0 = m_planes[0][src];
int b1 = m_planes[1][src];
int b2 = m_planes[2][src];
int b3 = m_planes[3][src];
for (int j = 0; j < 8; j += 2)
{
byte px = (byte)((((b0 << j) & 0x80) >> 3)
| (((b1 << j) & 0x80) >> 2)
| (((b2 << j) & 0x80) >> 1)
| (((b3 << j) & 0x80) ));
px |= (byte)((((b0 << j) & 0x40) >> 6)
| (((b1 << j) & 0x40) >> 5)
| (((b2 << j) & 0x40) >> 4)
| (((b3 << j) & 0x40) >> 3));
output[m_dst++] = px;
}
}
}
BitmapPalette ReadPalette ()
{
const int count = 16;
var colors = new Color[count];
for (int i = 0; i < count; ++i)
{
byte g = m_input.ReadUInt8();
byte r = m_input.ReadUInt8();
byte b = m_input.ReadUInt8();
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
}
return new BitmapPalette (colors);
}
}
}

65
Legacy/Harvest/ArcDAT.cs Normal file
View File

@ -0,0 +1,65 @@
//! \file ArcDAT.cs
//! \date 2023 Sep 26
//! \brief MyHarvest 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;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.MyHarvest
{
[Export(typeof(ArchiveFormat))]
public class DatOpener : ArchiveFormat
{
public override string Tag => "DAT/UNA";
public override string Description => "MyHarvest resource archive";
public override uint Signature => 0x414E55; // 'UNA'
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "001\0"))
return null;
int count = file.View.ReadInt32 (8);
if (!IsSaneCount (count))
return null;
uint index = 0x20;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index, 0x20);
var entry = Create<Entry> (name);
entry.Offset = file.View.ReadUInt32 (index+0x20);
entry.Size = file.View.ReadUInt32 (index+0x24);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index += 0x30;
}
return new ArcFile (file, this, dir);
}
}
}

View File

@ -0,0 +1,56 @@
//! \file AudioBGM.cs
//! \date 2023 Sep 26
//! \brief MyHarvest audio 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.ComponentModel.Composition;
namespace GameRes.Formats.MyHarvest
{
[Export(typeof(AudioFormat))]
public class BgmAudio : AudioFormat
{
public override string Tag => "BGM/HARVEST";
public override string Description => "MyHarvest audio resource";
public override uint Signature => 0x304D4742; // 'BMG0'
public override bool CanWrite => false;
public override SoundInput TryOpen (IBinaryStream file)
{
var header = file.ReadHeader (0x1C);
if (!header.AsciiEqual (0x14, "dar\0"))
return null;
var format = new WaveFormat {
FormatTag = header.ToUInt16 (4),
Channels = header.ToUInt16 (6),
SamplesPerSecond = header.ToUInt32 (8),
AverageBytesPerSecond = header.ToUInt32 (0xC),
BlockAlign = header.ToUInt16 (0x10),
BitsPerSample = header.ToUInt16 (0x12),
};
uint pcm_size = header.ToUInt32 (0x18);
var region = new StreamRegion (file.AsStream, 0x1C, pcm_size);
return new RawPcmInput (region, format);
}
}
}

View File

@ -0,0 +1,61 @@
//! \file AudioSED.cs
//! \date 2023 Sep 26
//! \brief MyHarvest audio 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.ComponentModel.Composition;
namespace GameRes.Formats.MyHarvest
{
[Export(typeof(AudioFormat))]
public class SedAudio : AudioFormat
{
public override string Tag => "SED/HARVEST";
public override string Description => "MyHarvest audio resource";
public override uint Signature => 0x14553; // 'SE'
public override bool CanWrite => false;
public SedAudio ()
{
Signatures = new[] { 0x14553u, 0u };
}
public override SoundInput TryOpen (IBinaryStream file)
{
var header = file.ReadHeader (0x18);
if (!header.AsciiEqual (0, "SE") || !header.AsciiEqual (0x12, "da"))
return null;
var format = new WaveFormat {
FormatTag = header.ToUInt16 (2),
Channels = header.ToUInt16 (4),
SamplesPerSecond = header.ToUInt32 (6),
AverageBytesPerSecond = header.ToUInt32 (0xA),
BlockAlign = header.ToUInt16 (0xE),
BitsPerSample = header.ToUInt16 (0x10),
};
uint pcm_size = header.ToUInt32 (0x14);
var region = new StreamRegion (file.AsStream, 0x18, pcm_size);
return new RawPcmInput (region, format);
}
}
}

View File

@ -0,0 +1,96 @@
//! \file ImageUNH.cs
//! \date 2023 Sep 26
//! \brief MyHarvest 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 System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
// [021206][MyHarvest] Idol Mahjong Final Romance 4
namespace GameRes.Formats.MyHarvest
{
[Export(typeof(ImageFormat))]
public class UnhFormat : ImageFormat
{
public override string Tag => "UNH";
public override string Description => "MyHarvest image format";
public override uint Signature => 0x30484E55; // 'UNH0'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x18);
if (header.ToInt32 (4) != 1)
return null;
return new ImageMetaData {
Width = header.ToUInt32 (0x10),
Height = header.ToUInt32 (0x14),
BPP = 16,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
file.Position = 0x44;
var pixels = new ushort[info.iWidth * info.iHeight];
var frame = new ushort[0x1000];
int frame_pos = 0;
int dst = 0;
byte mask = 0;
int ctl = 0;
while (dst < pixels.Length)
{
mask <<= 1;
if (0 == mask)
{
ctl = file.ReadByte();
if (-1 == ctl)
break;
mask = 1;
}
ushort word = file.ReadUInt16();
if ((ctl & mask) == 0)
{
pixels[dst++] = frame[frame_pos++ & 0xFFF] = word;
}
else
{
int offset = word >> 4;
int count = (word & 0xF) + 2;
while (count --> 0)
{
ushort u = frame[offset++ & 0xFFF];
pixels[dst++] = frame[frame_pos++ & 0xFFF] = u;
}
}
}
return ImageData.Create (info, PixelFormats.Bgr565, null, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("UnhFormat.Write not implemented");
}
}
}

69
Legacy/Jam/ImageHTF.cs Normal file
View File

@ -0,0 +1,69 @@
//! \file ImageHTF.cs
//! \date 2023 Oct 07
//! \brief Huffman-compressed bitmap.
//
// 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.Jam
{
[Export(typeof(ImageFormat))]
public class HtfFormat : ImageFormat
{
public override string Tag => "HTF";
public override string Description => "Huffman-compressed bitmap";
public override uint Signature => 0;
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (!file.Name.HasExtension (".HTF"))
return null;
int unpacked_size = file.ReadInt32();
if (unpacked_size <= 0 || unpacked_size > 0x1000000)
return null;
using (var huff = new HuffmanStream (file.AsStream, true))
using (var input = new BinaryStream (huff, file.Name))
{
return Bmp.ReadMetaData (input);
}
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
file.Position = 4;
using (var input = new HuffmanStream (file.AsStream, true))
{
var decoder = new BmpBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
return new ImageData (decoder.Frames[0], info);
}
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("HtfFormat.Write not implemented");
}
}
}

View File

@ -78,6 +78,9 @@
<Compile Include="AlphaSystem\ArcPAK.cs" />
<Compile Include="AlphaSystem\ImageSFG.cs" />
<Compile Include="Alterna\ArcBIN.cs" />
<Compile Include="Adviz\ImageBIZ.cs" />
<Compile Include="Adviz\ImageGIZ.cs" />
<Compile Include="Adviz\ImageGIZ2.cs" />
<Compile Include="Aos\ArcDAT.cs" />
<Compile Include="Aquarium\ArcAAP.cs" />
<Compile Include="Aquarium\ArcCPA.cs" />
@ -92,9 +95,15 @@
<Compile Include="Blucky\Aliases.cs" />
<Compile Include="Bom\ImageGRP.cs" />
<Compile Include="CottonClub\ImageLMG.cs" />
<Compile Include="Discovery\ImageAN1.cs" />
<Compile Include="Discovery\ImagePR1.cs" />
<Compile Include="Grocer\ImagePIC.cs" />
<Compile Include="Gsx\ArcK5.cs" />
<Compile Include="Gsx\ImageK4.cs" />
<Compile Include="Harvest\ArcDAT.cs" />
<Compile Include="Harvest\AudioBGM.cs" />
<Compile Include="Harvest\AudioSED.cs" />
<Compile Include="Harvest\ImageUNH.cs" />
<Compile Include="Herb\ArcPAK.cs" />
<Compile Include="Herb\ImageGRP.cs" />
<Compile Include="HyperWorks\ImageG.cs" />
@ -118,6 +127,7 @@
<Compile Include="hmp\ImageCBF.cs" />
<Compile Include="HyperWorks\ArcPAK.cs" />
<Compile Include="HyperWorks\ImageI24.cs" />
<Compile Include="Jam\ImageHTF.cs" />
<Compile Include="KApp\ArcASD.cs" />
<Compile Include="KApp\ArcCGD.cs" />
<Compile Include="KApp\ImageCGD.cs" />
@ -138,9 +148,14 @@
<Compile Include="Melonpan\ArcTTD.cs" />
<Compile Include="Mermaid\AudioPWV.cs" />
<Compile Include="Mermaid\ImageGP1.cs" />
<Compile Include="Mina\ArcPAK.cs" />
<Compile Include="Mink\ImageFC.cs" />
<Compile Include="Mink\ImageFD.cs" />
<Compile Include="Mmfass\ArcSDA.cs" />
<Compile Include="Nekotaro\ImageNCG.cs" />
<Compile Include="Ponytail\ArcBND.cs" />
<Compile Include="Ponytail\ImageTCZ.cs" />
<Compile Include="Ponytail\ImageTSZ.cs" />
<Compile Include="Nug\ArcDAT.cs" />
<Compile Include="Nyoken\ArcZLK.cs" />
<Compile Include="Omi\ArcDAT.cs" />
@ -209,8 +224,11 @@
<Compile Include="Rune\ArcYK.cs" />
<Compile Include="Sarang\ImageABC.cs" />
<Compile Include="Sogna\ArcSGS.cs" />
<Compile Include="Sophia\ArcNOR.cs" />
<Compile Include="SplushWave\ArcDAT.cs" />
<Compile Include="SplushWave\ImageSWG.cs" />
<Compile Include="SquadraD\ArcPLA.cs" />
<Compile Include="SquadraD\ArcSDA.cs" />
<Compile Include="StudioEbisu\ArcEP1.cs" />
<Compile Include="StudioFoma\ArcARC.cs" />
<Compile Include="System21\ImageTEX.cs" />
@ -251,7 +269,7 @@
<Compile Include="Mink\ArcMINK.cs" />
<Compile Include="Mutation\ArcDPF.cs" />
<Compile Include="Pinpai\ArcARC.cs" />
<Compile Include="Sorciere\ImageBIZ.cs" />
<Compile Include="Adviz\ImageBIZ2.cs" />
<Compile Include="UMeSoft\ArcBIN.cs" />
<Compile Include="Uncanny\AudioCWV.cs" />
<Compile Include="Uncanny\ImageCII.cs" />

299
Legacy/Mina/ArcPAK.cs Normal file
View File

@ -0,0 +1,299 @@
//! \file ArcPAK.cs
//! \date 2023 Oct 09
//! \brief Mina 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;
using System.Windows.Media;
// [010223][Mina] Storia ~Ouma no Mori no Himegimi-tachi~
namespace GameRes.Formats.Mina
{
[Export(typeof(ArchiveFormat))]
public class BmpPakOpener : ArchiveFormat
{
public override string Tag => "PAK/MINA/BMP";
public override string Description => "Mina bitmap archive";
public override uint Signature => 0;
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
if (!file.Name.HasExtension (".PAK"))
return null;
int pos;
for (pos = 0; pos < 0x10; ++pos)
{
if (0 == file.View.ReadByte (pos))
break;
}
if (pos >= 0x10 || pos <= 4 || !file.View.AsciiEqual (pos-4, ".BMP"))
return null;
using (var input = file.CreateStream())
{
var dir = new List<Entry>();
while (input.PeekByte() != -1)
{
var name = input.ReadCString();
if (name.Length > 0x10)
return null;
var entry = Create<Entry> (name);
entry.Offset = input.Position;
input.Seek (5, SeekOrigin.Current);
uint size = input.ReadUInt32();
entry.Size = size + 9;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
input.Seek (size, SeekOrigin.Current);
}
return new ArcFile (file, this, dir);
}
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{
var input = arc.File.CreateStream (entry.Offset, entry.Size);
return new BitmapDecoder (input);
}
}
[Export(typeof(ArchiveFormat))]
public class WavPakOpener : ArchiveFormat
{
public override string Tag => "PAK/MINA/WAV";
public override string Description => "Mina audio archive";
public override uint Signature => 0;
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
if (!file.Name.HasExtension (".PAK"))
return null;
int pos;
for (pos = 4; pos < 0x14; ++pos)
{
if (0 == file.View.ReadByte (pos))
break;
}
if (pos >= 0x14 || pos <= 8 || !file.View.AsciiEqual (pos-4, ".WAV"))
return null;
using (var input = file.CreateStream())
{
var dir = new List<Entry>();
while (input.PeekByte() != -1)
{
uint data_size = input.ReadUInt32();
var name = input.ReadCString();
if (name.Length > 0x10)
return null;
var entry = Create<Entry> (name);
entry.Offset = input.Position;
uint fmt_size = input.ReadUInt32();
if (fmt_size < 0x10)
return null;
entry.Size = data_size + fmt_size + 4;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
input.Seek (data_size + fmt_size, SeekOrigin.Current);
}
return new ArcFile (file, this, dir);
}
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
uint fmt_size = arc.File.View.ReadUInt32 (entry.Offset);
uint pcm_size = entry.Size - 4 - fmt_size;
using (var mem = new MemoryStream ((int)fmt_size))
{
using (var buffer = new BinaryWriter (mem, Encoding.ASCII, true))
{
buffer.Write (AudioFormat.Wav.Signature);
buffer.Write (entry.Size+0x10);
buffer.Write (0x45564157); // 'WAVE'
buffer.Write (0x20746d66); // 'fmt '
buffer.Write (fmt_size);
var fmt = arc.File.View.ReadBytes (entry.Offset+4, fmt_size);
buffer.Write (fmt, 0, fmt.Length);
buffer.Write (0x61746164); // 'data'
buffer.Write (pcm_size);
}
var header = mem.ToArray();
var data = arc.File.CreateStream (entry.Offset+4+fmt_size, pcm_size);
return new PrefixStream (header, data);
}
}
}
[Export(typeof(ArchiveFormat))]
public class ScriptPakOpener : ArchiveFormat
{
public override string Tag => "PAK/MINA/SPT";
public override string Description => "Mina scripts archive";
public override uint Signature => 0;
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public ScriptPakOpener ()
{
ContainedFormats = new[] { "SCR" };
}
public override ArcFile TryOpen (ArcView file)
{
if (!VFS.IsPathEqualsToFileName (file.Name, "SCRIPT.PAK"))
return null;
using (var input = file.CreateStream())
{
var dir = new List<Entry>();
while (input.PeekByte() != -1)
{
var name = input.ReadCString();
if (name.Length > 0x10)
return null;
var entry = Create<Entry> (name);
entry.Size = input.ReadUInt32();
entry.Offset = input.Position;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
input.Seek (entry.Size, SeekOrigin.Current);
}
return new ArcFile (file, this, dir);
}
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
var mem = new MemoryStream (data.Length);
int pos = 0;
while (pos < data.Length)
{
int len = data[pos]+1;
int num = data.ToUInt16 (1);
pos += 3;
for (int j = 0; j < len; ++j)
data[pos+j] = Binary.RotByteR (data[pos+j], 4);
mem.Write (data, pos, len);
mem.WriteByte (0xD);
mem.WriteByte (0xA);
pos += len;
}
mem.Position = 0;
return mem;
}
}
internal class BmpMetaData : ImageMetaData
{
public byte Flags;
public bool IsCompressed => (Flags & 1) != 0;
}
internal class BitmapDecoder : IImageDecoder
{
IBinaryStream m_input;
BmpMetaData m_info;
ImageData m_image;
public Stream Source => m_input.AsStream;
public ImageFormat SourceFormat => null;
public ImageMetaData Info => m_info;
public ImageData Image => m_image ?? (m_image = Unpack());
public BitmapDecoder (IBinaryStream input)
{
m_input = input;
m_info = new BmpMetaData {
Width = input.ReadUInt16(),
Height = input.ReadUInt16(),
Flags = input.ReadUInt8(),
};
m_info.BPP = m_info.IsCompressed ? 32 : 24;
}
ImageData Unpack ()
{
m_input.Position = 9;
if (m_info.IsCompressed)
{
return RleUnpack();
}
else
{
int bitmap_size = m_info.iWidth * 3 * m_info.iHeight;
var pixels = m_input.ReadBytes (bitmap_size);
return ImageData.Create (m_info, PixelFormats.Rgb24, null, pixels);
}
}
ImageData RleUnpack ()
{
int stride = m_info.iWidth * 4;
var output = new byte[stride * m_info.iHeight];
byte alpha = 0;
int count = 0;
int dst = 0;
while (dst < output.Length)
{
if (--count <= 0)
{
alpha = m_input.ReadUInt8();
count = m_input.ReadUInt8();
}
if (alpha != 0)
{
output[dst+2] = m_input.ReadUInt8();
output[dst+1] = m_input.ReadUInt8();
output[dst ] = m_input.ReadUInt8();
output[dst+3] = alpha;
}
dst += 4;
}
return ImageData.Create (m_info, PixelFormats.Bgra32, null, output, stride);
}
#region IDisposable members
bool m_disposed = false;
public void Dispose ()
{
if (!m_disposed)
{
m_input.Dispose();
m_disposed = true;
}
}
#endregion
}
}

View File

@ -27,6 +27,7 @@ using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
// [991231][Jam] Kakuyuugou Shoujo Ripple-chan
// [000331][Jam] Zetsumetsu King
// [000630][STONE HEADS] Sei Cosplay Gakuen ~Game Bunkou~

View File

@ -24,8 +24,10 @@
//
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GameRes.Utility;
@ -44,6 +46,11 @@ namespace GameRes.Formats.Nekotaro
public override string Description { get { return "Nekotaro Game System image format"; } }
public override uint Signature { get { return 0x706D4347; } } // 'GCmp'
public GCmpFormat ()
{
Extensions = new[] { "GCMP", "AIG" };
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
@ -98,6 +105,8 @@ namespace GameRes.Formats.Nekotaro
Stride = (info.iWidth + 7) / 8;
}
static BitmapPalette LastUsedPalette = null;
public ImageData Unpack ()
{
m_input.Position = 0x10;
@ -106,6 +115,8 @@ namespace GameRes.Formats.Nekotaro
pixels = Unpack24bpp();
else
pixels = Unpack8bpp();
if (8 == Info.BPP)
Palette = LastUsedPalette ?? (LastUsedPalette = RetrievePalette() ?? DefaultPalette);
return ImageData.CreateFlipped (Info, Format, Palette, pixels, Stride);
}
@ -190,14 +201,11 @@ namespace GameRes.Formats.Nekotaro
byte[] Unpack8bpp ()
{
if (8 == Info.BPP)
{
Format = PixelFormats.Indexed8;
Palette = DefaultPalette;
}
else
Format = PixelFormats.BlackWhite;
int pixel_count = Info.iHeight * Stride;
if (m_info.IsCompressed)
if (!m_info.IsCompressed)
return m_input.ReadBytes (pixel_count);
var output = new byte[pixel_count];
@ -272,268 +280,125 @@ namespace GameRes.Formats.Nekotaro
return output;
}
static readonly BitmapPalette DefaultPalette = new BitmapPalette (
/*
new Color[] {
Color.FromRgb (0x00, 0x00, 0x00),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0x22, 0x22, 0x22),
Color.FromRgb (0x44, 0x44, 0x44),
Color.FromRgb (0x55, 0x55, 0x55),
Color.FromRgb (0x66, 0x66, 0x66),
Color.FromRgb (0x77, 0x77, 0x77),
Color.FromRgb (0x88, 0x88, 0x88),
Color.FromRgb (0x99, 0x99, 0x99),
Color.FromRgb (0xAA, 0xAA, 0xAA),
Color.FromRgb (0xBB, 0xBB, 0xBB),
Color.FromRgb (0xCC, 0xCC, 0xCC),
Color.FromRgb (0xDD, 0xDD, 0xDD),
Color.FromRgb (0xEE, 0xEE, 0xEE),
Color.FromRgb (0x00, 0xFF, 0x00),
Color.FromRgb (0x1C, 0x09, 0x05),
Color.FromRgb (0x2F, 0x0A, 0x05),
Color.FromRgb (0x4E, 0x04, 0x02),
Color.FromRgb (0x41, 0x0C, 0x05),
Color.FromRgb (0x29, 0x15, 0x36),
Color.FromRgb (0x24, 0x22, 0x21),
Color.FromRgb (0x6C, 0x07, 0x0D),
Color.FromRgb (0x1F, 0x2D, 0x36),
Color.FromRgb (0x4B, 0x21, 0x18),
Color.FromRgb (0x5D, 0x1B, 0x0D),
Color.FromRgb (0x8B, 0x00, 0x36),
Color.FromRgb (0x8E, 0x06, 0x16),
Color.FromRgb (0x7E, 0x11, 0x0F),
Color.FromRgb (0x09, 0x44, 0x64),
Color.FromRgb (0x48, 0x2C, 0x4B),
Color.FromRgb (0x38, 0x37, 0x3A),
Color.FromRgb (0x3A, 0x24, 0x88),
Color.FromRgb (0x74, 0x23, 0x12),
Color.FromRgb (0x0D, 0x53, 0x29),
Color.FromRgb (0x22, 0x34, 0x86),
Color.FromRgb (0xB1, 0x03, 0x2A),
Color.FromRgb (0x4B, 0x37, 0x28),
Color.FromRgb (0x64, 0x30, 0x28),
Color.FromRgb (0x32, 0x4A, 0x2D),
Color.FromRgb (0x9B, 0x17, 0x20),
Color.FromRgb (0xB0, 0x10, 0x10),
Color.FromRgb (0x3D, 0x19, 0xCC),
Color.FromRgb (0x1B, 0x38, 0xB2),
Color.FromRgb (0x97, 0x25, 0x13),
Color.FromRgb (0x30, 0x4C, 0x5E),
Color.FromRgb (0x77, 0x38, 0x22),
Color.FromRgb (0xD3, 0x0B, 0x1F),
Color.FromRgb (0x01, 0x69, 0x65),
Color.FromRgb (0x5F, 0x46, 0x33),
Color.FromRgb (0x4B, 0x4D, 0x4F),
Color.FromRgb (0xB6, 0x1B, 0x34),
Color.FromRgb (0x0A, 0x74, 0x34),
Color.FromRgb (0xBB, 0x26, 0x11),
Color.FromRgb (0xED, 0x0B, 0x26),
Color.FromRgb (0x2F, 0x52, 0x97),
Color.FromRgb (0x49, 0x20, 0xFB),
Color.FromRgb (0x89, 0x44, 0x15),
Color.FromRgb (0x67, 0x46, 0x65),
Color.FromRgb (0x06, 0x76, 0x72),
Color.FromRgb (0x93, 0x3F, 0x2D),
Color.FromRgb (0x3F, 0x65, 0x49),
Color.FromRgb (0x6D, 0x52, 0x3B),
Color.FromRgb (0x88, 0x4C, 0x38),
Color.FromRgb (0xE5, 0x26, 0x17),
Color.FromRgb (0xA6, 0x47, 0x1D),
Color.FromRgb (0x43, 0x68, 0x7D),
Color.FromRgb (0x23, 0x50, 0xE8),
Color.FromRgb (0xE3, 0x24, 0x43),
Color.FromRgb (0x94, 0x56, 0x1C),
Color.FromRgb (0x60, 0x63, 0x64),
Color.FromRgb (0xBC, 0x3E, 0x49),
Color.FromRgb (0x06, 0x9C, 0x45),
Color.FromRgb (0xC4, 0x44, 0x24),
Color.FromRgb (0xB1, 0x55, 0x2B),
Color.FromRgb (0x8D, 0x60, 0x53),
Color.FromRgb (0x63, 0x46, 0xFB),
Color.FromRgb (0x7B, 0x6C, 0x61),
Color.FromRgb (0x91, 0x57, 0x97),
Color.FromRgb (0xAA, 0x5A, 0x4C),
Color.FromRgb (0x49, 0x7E, 0xA0),
Color.FromRgb (0xF8, 0x3C, 0x29),
Color.FromRgb (0xA9, 0x67, 0x20),
Color.FromRgb (0xC9, 0x56, 0x36),
Color.FromRgb (0xA2, 0x6A, 0x3E),
Color.FromRgb (0xBF, 0x56, 0x6C),
Color.FromRgb (0x77, 0x7A, 0x7B),
Color.FromRgb (0x5D, 0x79, 0xD2),
Color.FromRgb (0xCC, 0x62, 0x44),
Color.FromRgb (0xA3, 0x75, 0x63),
Color.FromRgb (0xDE, 0x60, 0x31),
Color.FromRgb (0xB5, 0x79, 0x23),
Color.FromRgb (0x45, 0x80, 0xF5),
Color.FromRgb (0xFD, 0x56, 0x29),
Color.FromRgb (0xEE, 0x52, 0x64),
Color.FromRgb (0x8C, 0x83, 0x6E),
Color.FromRgb (0xCD, 0x70, 0x2A),
Color.FromRgb (0xC4, 0x6E, 0x53),
Color.FromRgb (0x86, 0x87, 0x87),
Color.FromRgb (0x5E, 0x95, 0xAC),
Color.FromRgb (0x7D, 0x6C, 0xFD),
Color.FromRgb (0x36, 0xC5, 0x22),
Color.FromRgb (0xAC, 0x6F, 0xB2),
Color.FromRgb (0xD9, 0x6E, 0x4E),
Color.FromRgb (0xC1, 0x84, 0x2D),
Color.FromRgb (0xDB, 0x6C, 0x72),
Color.FromRgb (0xEB, 0x6F, 0x42),
Color.FromRgb (0x9F, 0x8B, 0x81),
Color.FromRgb (0x92, 0x94, 0x93),
Color.FromRgb (0x76, 0x90, 0xDB),
Color.FromRgb (0x85, 0x9A, 0x99),
Color.FromRgb (0xE0, 0x79, 0x58),
Color.FromRgb (0xBE, 0x87, 0x6D),
Color.FromRgb (0xD5, 0x7E, 0x62),
Color.FromRgb (0x5B, 0xA7, 0xDF),
Color.FromRgb (0xCC, 0x91, 0x2A),
Color.FromRgb (0xF5, 0x6F, 0x76),
Color.FromRgb (0x7B, 0xA7, 0xA7),
Color.FromRgb (0xF1, 0x7C, 0x54),
Color.FromRgb (0xA1, 0x9C, 0x87),
Color.FromRgb (0xE5, 0x81, 0x61),
Color.FromRgb (0xF2, 0x8A, 0x47),
Color.FromRgb (0xEE, 0x88, 0x67),
Color.FromRgb (0xA1, 0xA3, 0xA3),
Color.FromRgb (0x8A, 0xA0, 0xE5),
Color.FromRgb (0xC4, 0x9A, 0x7F),
Color.FromRgb (0xD9, 0x9F, 0x36),
Color.FromRgb (0x95, 0xAC, 0xAA),
Color.FromRgb (0xEC, 0x88, 0x8B),
Color.FromRgb (0xAE, 0xA7, 0x92),
Color.FromRgb (0xE8, 0x90, 0x70),
Color.FromRgb (0xF5, 0x8F, 0x6F),
Color.FromRgb (0xD5, 0x8B, 0xDC),
Color.FromRgb (0x6A, 0xC2, 0xF7),
Color.FromRgb (0xEE, 0x9A, 0x7A),
Color.FromRgb (0xF7, 0x98, 0x74),
Color.FromRgb (0x8D, 0xBA, 0xDB),
Color.FromRgb (0xBA, 0xB1, 0x9C),
Color.FromRgb (0xB2, 0xB3, 0xB1),
Color.FromRgb (0xD2, 0xA8, 0x9B),
Color.FromRgb (0xA6, 0xBA, 0xBD),
Color.FromRgb (0xEC, 0xB4, 0x3A),
Color.FromRgb (0xFC, 0x98, 0x9F),
Color.FromRgb (0xF7, 0xA1, 0x80),
Color.FromRgb (0xED, 0xA7, 0x85),
Color.FromRgb (0xFA, 0xA9, 0x83),
Color.FromRgb (0xDD, 0xB3, 0xAF),
Color.FromRgb (0xFA, 0xA6, 0xA7),
Color.FromRgb (0xC8, 0xC0, 0xAD),
Color.FromRgb (0xFA, 0xB0, 0x8F),
Color.FromRgb (0x89, 0xD9, 0xFC),
Color.FromRgb (0xA9, 0xCF, 0xE8),
Color.FromRgb (0xBB, 0xCC, 0xCB),
Color.FromRgb (0xFB, 0xB2, 0xB2),
Color.FromRgb (0xFB, 0xB9, 0x97),
Color.FromRgb (0xE2, 0xC2, 0xAF),
Color.FromRgb (0xFC, 0xCA, 0x40),
Color.FromRgb (0xFA, 0xBF, 0x82),
Color.FromRgb (0xC9, 0xCA, 0xC9),
Color.FromRgb (0xF8, 0xAC, 0xF8),
Color.FromRgb (0xD4, 0xCD, 0xC2),
Color.FromRgb (0xFC, 0xC2, 0x9D),
Color.FromRgb (0xFC, 0xBE, 0xBA),
Color.FromRgb (0xD2, 0xD3, 0xD0),
Color.FromRgb (0xEC, 0xC9, 0xC6),
Color.FromRgb (0xCA, 0xD9, 0xD7),
Color.FromRgb (0xFD, 0xCA, 0xA5),
Color.FromRgb (0xFE, 0xDB, 0x5B),
Color.FromRgb (0xD8, 0xD8, 0xD4),
Color.FromRgb (0xFD, 0xCA, 0xC9),
Color.FromRgb (0xC3, 0xDF, 0xF1),
Color.FromRgb (0xFE, 0xD2, 0xB1),
Color.FromRgb (0xFD, 0xD6, 0xA1),
Color.FromRgb (0xEE, 0xD7, 0xCA),
Color.FromRgb (0xFB, 0xCB, 0xF7),
Color.FromRgb (0xFE, 0xDB, 0xB6),
Color.FromRgb (0xFE, 0xF5, 0x2C),
Color.FromRgb (0xFD, 0xD6, 0xD4),
Color.FromRgb (0xE2, 0xE2, 0xDC),
Color.FromRgb (0xFE, 0xEC, 0x74),
Color.FromRgb (0xFE, 0xE1, 0xBE),
Color.FromRgb (0xED, 0xE5, 0xDC),
Color.FromRgb (0xD9, 0xEC, 0xF8),
Color.FromRgb (0xFB, 0xE3, 0xD4),
Color.FromRgb (0xFD, 0xDD, 0xFA),
Color.FromRgb (0xFE, 0xE7, 0xC6),
Color.FromRgb (0xFE, 0xFA, 0x91),
Color.FromRgb (0xFE, 0xEF, 0xCD),
Color.FromRgb (0xFC, 0xEB, 0xEA),
Color.FromRgb (0xFE, 0xF6, 0xDC),
Color.FromRgb (0xFE, 0xFD, 0xE4),
Color.FromRgb (0x35, 0x29, 0x24),
Color.FromRgb (0x1A, 0x43, 0x25),
Color.FromRgb (0x01, 0x49, 0x96),
Color.FromRgb (0x86, 0x27, 0x16),
Color.FromRgb (0x4D, 0x52, 0x3F),
Color.FromRgb (0xEB, 0x0E, 0x0A),
Color.FromRgb (0x00, 0x6A, 0xCC),
Color.FromRgb (0x80, 0x34, 0xC1),
Color.FromRgb (0xFD, 0x00, 0xFF),
Color.FromRgb (0x08, 0x87, 0xEF),
Color.FromRgb (0x76, 0x70, 0x56),
Color.FromRgb (0xB8, 0x55, 0x3F),
Color.FromRgb (0x35, 0x9F, 0xE1),
Color.FromRgb (0xAA, 0x7E, 0x60),
Color.FromRgb (0x01, 0xFD, 0x00),
Color.FromRgb (0xAB, 0x93, 0x8A),
Color.FromRgb (0xD3, 0x8C, 0x56),
Color.FromRgb (0x77, 0xC0, 0xAC),
Color.FromRgb (0xB9, 0xA6, 0x9E),
Color.FromRgb (0xE6, 0xAB, 0x63),
Color.FromRgb (0x9D, 0xCC, 0xA5),
Color.FromRgb (0xD1, 0xB6, 0x91),
Color.FromRgb (0xA6, 0xD9, 0xCF),
Color.FromRgb (0xEA, 0xC9, 0x9D),
Color.FromRgb (0xDF, 0xE2, 0xBC),
Color.FromRgb (0xFC, 0xE8, 0xA2),
Color.FromRgb (0xF9, 0xF2, 0xDE),
Color.FromRgb (0x23, 0x0D, 0x1A),
Color.FromRgb (0x02, 0x58, 0x1A),
Color.FromRgb (0x66, 0x39, 0x13),
Color.FromRgb (0x36, 0x6D, 0x66),
Color.FromRgb (0x90, 0x5F, 0x2A),
Color.FromRgb (0x51, 0x9E, 0x7E),
Color.FromRgb (0xC0, 0x91, 0x52),
Color.FromRgb (0x7F, 0xC3, 0xAE),
Color.FromRgb (0xE0, 0xBF, 0x78),
Color.FromRgb (0xDC, 0xE8, 0xD4),
Color.FromRgb (0x65, 0x39, 0x12),
Color.FromRgb (0x22, 0x69, 0x49),
Color.FromRgb (0x90, 0x5E, 0x2B),
Color.FromRgb (0x36, 0x87, 0x5F),
Color.FromRgb (0x53, 0x9F, 0x81),
Color.FromRgb (0xBB, 0x85, 0x4C),
Color.FromRgb (0xD9, 0xB9, 0x6F),
Color.FromRgb (0x9A, 0xCE, 0xC2),
Color.FromRgb (0xDF, 0xEF, 0xDE),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
Color.FromRgb (0xFF, 0xFF, 0xFF),
static void LzssUnpack (IBinaryStream input, byte[] output)
{
int dst = 0;
int mask = 0;
int ctl = 0;
while (dst < output.Length)
{
mask >>= 1;
if (0 == mask)
{
ctl = input.ReadUInt8();
mask = 0x80;
}
if ((ctl & mask) != 0)
{
int off = input.ReadUInt16();
int count = (off & 0xF) + 3;
off >>= 4;
int src = dst - off - 1;
Binary.CopyOverlapped (output, src, dst, count);
dst += count;
}
else
{
output[dst++] = input.ReadUInt8();
}
}
*/
}
BitmapPalette RetrievePalette ()
{
// find SYSTEM.LZS file, decompress and read it as text file
// find 'P' line that denotes archive name and entry number
// if entry number is zero, then it's just a file (possibly compressed)
// open referenced file and retrieve palette
try
{
string system_name = "SYSTEM.LZS";
if (!File.Exists (system_name))
{
system_name = @"..\SYSTEM.LZS";
if (!File.Exists (system_name))
return null;
}
byte[] system_bin;
using (var input = BinaryStream.FromFile (system_name))
{
int unpacked_size = input.ReadUInt16();
input.ReadUInt16();
system_bin = new byte[unpacked_size];
LzssUnpack (input, system_bin);
}
string line;
using (var mem = new MemoryStream (system_bin))
using (var text = new StreamReader (mem, Encodings.cp932))
{
while ((line = text.ReadLine()) != null)
{
if (line.Length > 3 && line.StartsWith ("P:"))
break;
}
if (null == line)
return null;
}
var match = PLineRe.Match (line);
if (!match.Success)
return null;
int id;
if (!Int32.TryParse (match.Groups[2].Value, out id))
return null;
var arc_name = Path.Combine (Path.GetDirectoryName (system_name), match.Groups[1].Value);
if (0 == id)
{
using (var file = BinaryStream.FromFile (arc_name))
{
Stream pal_stream;
int unpacked_size = file.ReadUInt16();
int packed_size = file.ReadUInt16();
if (packed_size + 4 == file.Length)
{
var pal_data = new byte[unpacked_size];
LzssUnpack (file, pal_data);
pal_stream = new MemoryStream (pal_data);
}
else
{
file.Position = 0;
pal_stream = file.AsStream;
}
int colors = (int)pal_stream.Length / 3;
using (pal_stream)
return ImageFormat.ReadPalette (pal_stream, colors, PaletteFormat.Rgb);
}
}
else
{
using (var file = new ArcView (arc_name))
{
var arc = Nsc.Value.TryOpen (file);
if (null == arc)
return null;
var entry = ((List<Entry>)arc.Dir)[id-1];
using (var input = arc.OpenEntry (entry))
return ImageFormat.ReadPalette (input, 0x100, PaletteFormat.Rgb);
}
}
}
catch
{
return null;
}
}
static readonly Regex PLineRe = new Regex (@"^P:([^,]+),(\d+),(\d+)", RegexOptions.Compiled);
static readonly ResourceInstance<ArchiveFormat> Nsc = new ResourceInstance<ArchiveFormat> ("NSC");
static readonly BitmapPalette DefaultPalette = new BitmapPalette (
// [000317][PIL] Seek -remasters-
#region colors
new Color[] {
Color.FromRgb (0x00, 0x00, 0x00),
Color.FromRgb (0xFF, 0xFF, 0xFF),
@ -792,6 +657,7 @@ namespace GameRes.Formats.Nekotaro
Color.FromRgb (0x00, 0x00, 0x00),
Color.FromRgb (0x00, 0x00, 0x00),
}
#endregion
);
bool m_disposed = false;

293
Legacy/Nekotaro/ImageNCG.cs Normal file
View File

@ -0,0 +1,293 @@
//! \file ImageNCG.cs
//! \date 2023 Oct 10
//! \brief Nekotaro Game System image format (PC-98).
//
// 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;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.Nekotaro
{
[Export(typeof(ImageFormat))]
public class NcgFormat : ImageFormat
{
public override string Tag => "NCG";
public override string Description => "Nekotaro Game System image format";
public override uint Signature => 0;
public NcgFormat ()
{
Signatures = new[] { 0xC8500000u, 0u };
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (4);
int left = header[0] << 3;
int top = header[1] << 1;
int width = header[2] << 3;
int height = header[3] << 1;
int right = left + width;
int bottom = top + height;
if (right > 640 || bottom > 400 || 0 == width || 0 == height)
return null;
return new ImageMetaData {
Width = (uint)width,
Height = (uint)height,
OffsetX = left,
OffsetY = top,
BPP = 4,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new NcgReader (file, info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("NcgFormat.Write not implemented");
}
}
internal class NcgReader
{
IBinaryStream m_input;
ImageMetaData m_info;
public NcgReader (IBinaryStream input, ImageMetaData info)
{
m_input = input;
m_info = info;
}
public ImageData Unpack ()
{
m_input.Position = 4;
var palette = ReadPalette();
int width = m_info.iWidth;
int height = m_info.iHeight;
int output_stride = width;
var pixels = new byte[output_stride * height];
int quart_width = width / 4;
int half_height = height / 2;
var blockmap = new bool[quart_width * half_height];
var bits1 = new byte[8];
var bits2 = new byte[8];
byte ctl;
int dst, pblk;
do
{
for (int i = 0; i < 8; ++i)
bits1[i] = bits2[i] = 0;
for (int shift = 0; shift < 4; ++shift)
{
byte bit = (byte)(1 << shift);
FillBits (bits1, bit);
FillBits (bits2, bit);
}
for (;;)
{
ctl = m_input.ReadUInt8();
if (0xFF == ctl || 0x7F == ctl)
break;
int pos = (ctl & 0x3F) << 8 | m_input.ReadUInt8();
int x = (pos % 80) << 3;
int y = (pos / 80) << 1;
dst = width * y + x;
pblk = x / 4 + quart_width * (y / 2);
switch (ctl >> 6)
{
case 0:
{
int w_count = m_input.ReadUInt8();
int h_count = m_input.ReadUInt8();
int gap = quart_width - 2 * w_count;
while (h_count --> 0)
{
for (int i = 0; i < w_count; ++i)
{
for (int j = 0; j < 8; ++j)
{
pixels[dst+width] = bits2[j];
pixels[dst++] = bits1[j];
}
blockmap[pblk++] = true;
blockmap[pblk++] = true;
}
pblk += gap;
dst += 2 * width - 8 * w_count;
}
break;
}
case 1:
{
int count = m_input.ReadUInt8();
while (count --> 0)
{
for (int j = 0; j < 8; ++j)
{
pixels[dst+width] = bits2[j];
pixels[dst++] = bits1[j];
}
blockmap[pblk++] = true;
blockmap[pblk++] = true;
}
break;
}
case 2:
{
int count = m_input.ReadUInt8();
while (count --> 0)
{
for (int j = 0; j < 8; ++j)
{
pixels[dst+width] = bits2[j];
pixels[dst++] = bits1[j];
}
blockmap[pblk ] = true;
blockmap[pblk+1] = true;
dst += 2 * width - 8;
pblk += quart_width;
}
break;
}
case 3:
{
for (int j = 0; j < 8; ++j)
{
pixels[dst+width] = bits2[j];
pixels[dst++] = bits1[j];
}
blockmap[pblk ] = true;
blockmap[pblk+1] = true;
break;
}
}
}
}
while (ctl != 0xFF);
do
{
for (int i = 0; i < 8; ++i)
bits1[i] = 0;
for (int shift = 0; shift < 4; ++shift)
FillBits (bits1, (byte)(1 << shift));
for (;;)
{
ctl = m_input.ReadUInt8();
if (0xFF == ctl || 0xFE == ctl)
break;
int pos = (ctl & 0x7F) << 8 | m_input.ReadUInt8();
dst = 4 * (pos % 160) + width * 2 * (pos / 160);
pblk = (pos % 160) + quart_width * (pos / 160);
if ((ctl & 0x80) == 0)
{
int count = m_input.ReadUInt8();
while (count --> 0)
{
for (int j = 0; j < 4; ++j)
{
pixels[dst+width] = bits1[j+4];
pixels[dst++] = bits1[j];
}
blockmap[pblk] = true;
pblk += quart_width;
dst += 2 * width - 4;
}
}
else
{
for (int j = 0; j < 4; ++j)
{
pixels[dst+width] = bits1[j+4];
pixels[dst++] = bits1[j];
}
blockmap[pblk] = true;
}
}
}
while (ctl != 0xFF);
dst = 0;
pblk = 0;
for (int y = 0; y < half_height; ++y)
{
for (int x = 0; x < quart_width; ++x)
{
if (blockmap[pblk++])
{
dst += 4;
}
else
{
for (int i = 0; i < 8; ++i)
bits1[i] = 0;
for (int shift = 0; shift < 4; ++shift)
FillBits (bits1, (byte)(1 << shift));
for (int j = 0; j < 4; ++j)
{
pixels[dst+width] = bits1[j+4];
pixels[dst++] = bits1[j];
}
}
}
dst += width;
}
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, pixels, output_stride);
}
void FillBits (byte[] bits, byte bit)
{
sbyte s = m_input.ReadInt8();
for (int i = 0; i < 8; ++i)
{
if (s < 0)
bits[i] |= bit;
s <<= 1;
}
}
static readonly string PaletteKey = "NEKOTARO";
BitmapPalette ReadPalette ()
{
int k = 0;
var colors = new Color[16];
for (int c = 0; c < 16; ++c)
{
int g = m_input.ReadUInt8();
int r = m_input.ReadUInt8();
int b = m_input.ReadUInt8();
b = (~b - PaletteKey[k++ & 7]) & 0xFF;
r = (~r - PaletteKey[k++ & 7]) & 0xFF;
g = (~g - PaletteKey[k++ & 7]) & 0xFF;
colors[c] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
}
return new BitmapPalette (colors);
}
}
}

125
Legacy/Ponytail/ArcBND.cs Normal file
View File

@ -0,0 +1,125 @@
//! \file ArcBND.cs
//! \date 2023 Sep 28
//! \brief Ponytail Adventure 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.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
// [951115][Ponytail Soft] Masuzume Yume
namespace GameRes.Formats.Ponytail
{
[Export(typeof(ArchiveFormat))]
public class BndOpener : ArchiveFormat
{
public override string Tag => "BND/NMI";
public override string Description => "Ponytail Soft resource archive";
public override uint Signature => 0x646E6942; // 'Bind'
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, " ver.0"))
return null;
int count = file.View.ReadInt16 (0xD);
if (!IsSaneCount (count))
return null;
uint index_offset = file.View.ReadUInt32 (0xF);
if (index_offset >= file.MaxOffset)
return null;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset, 8).Trim();
var ext = file.View.ReadString (index_offset+8, 3);
name = name + '.' + ext;
var entry = Create<PackedEntry> (name);
entry.Size = file.View.ReadUInt32 (index_offset+0x0C);
entry.Offset = file.View.ReadUInt32 (index_offset+0x14);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 0x18;
}
foreach (PackedEntry entry in dir.Where (e => e.Name.EndsWith ("Z") && e.Type != "image"))
{
if (file.View.AsciiEqual (entry.Offset, "lz1_"))
{
entry.IsPacked = true;
char last_chr =(char)file.View.ReadByte (entry.Offset+4);
entry.UnpackedSize = file.View.ReadUInt32 (entry.Offset+5);
string name = entry.Name.Remove (entry.Name.Length-1);
entry.Name = name + char.ToUpperInvariant (last_chr);
}
}
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 output = new byte[pent.UnpackedSize];
using (var input = arc.File.CreateStream (pent.Offset+9, pent.Size-9))
Lz1Unpack (input, output);
return new BinMemoryStream (output, pent.Name);
}
internal static void Lz1Unpack (IBinaryStream input, byte[] output)
{
byte mask = 0;
int ctl = 0;
int dst = 0;
while (dst < output.Length)
{
mask <<= 1;
if (0 == mask)
{
ctl = input.ReadUInt8();
if (ctl < 0)
break;
mask = 1;
}
if ((ctl & mask) != 0)
{
output[dst++] = input.ReadUInt8();
}
else
{
int code = input.ReadUInt16();
int offset = (code >> 5) + 1;
int count = Math.Min (3 + (code & 0x1F), output.Length - dst);
Binary.CopyOverlapped (output, dst - offset, dst, count);
dst += count;
}
}
}
}
}

212
Legacy/Ponytail/ImageTCZ.cs Normal file
View File

@ -0,0 +1,212 @@
//! \file ImageTCZ.cs
//! \date 2023 Sep 28
//! \brief Ponytail NMI 2.5 image format (PC-98).
//
// 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;
// Graphics driver version 0.3 95/10/30
// by Y.Nakamura / NARIMI.A
namespace GameRes.Formats.Ponytail
{
[Export(typeof(ImageFormat))]
public class TczFormat : ImageFormat
{
public override string Tag => "TCZ";
public override string Description => "Ponytail Soft NMI image format";
public override uint Signature => 0x20494D4E; // 'NMI '
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual (4, "2.5\0"))
return null;
var info = new ImageMetaData {
Width = header.ToUInt16 (0xC),
Height = header.ToUInt16 (0xE),
OffsetX = header.ToInt16 (0x8),
OffsetY = header.ToInt16 (0xA),
BPP = 4,
};
if (info.Height > TczReader.MaxHeight)
return null;
return info;
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new TczReader (file, info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("TczFormat.Write not implemented");
}
}
internal class TczReader : TszReader
{
internal const int MaxHeight = 0x190;
const int BufferSize = MaxHeight * 0x10;
public TczReader (IBinaryStream input, ImageMetaData info)
{
m_input = input;
m_info = info;
m_stride = m_info.iWidth >> 1;
}
byte[] m_buffer;
static short[] s_offTable0 = { -4, -3, -2, -1, 0, 1, 2, 3 };
static short[] s_offTable1 = { -16, -8, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 6, 8, 10, 16 };
public new ImageData Unpack ()
{
m_input.Position = 0x10;
var palette = ReadPalette();
var output = new byte[m_stride * m_info.iHeight];
FillBuffers();
ResetBitReader();
int dst = 2;
int output_pos = 0;
int x = 0;
while (x < m_stride)
{
int y = 0;
while (y < m_info.iHeight)
{
if (GetNextBit()) // @1@
{
int src = dst;
if (!GetNextBit()) // @2@
{
src += m_lineOffsets1[1];
int off = GetBits (4);
src += s_offTable1[off];
}
else if (!GetNextBit()) // @3@
{
src += GetBits (2) - 4;
}
else
{
if (!GetNextBit()) // @4@
{
src += m_lineOffsets1[2];
}
else if (!GetNextBit()) // @5@
{
src += m_lineOffsets1[4];
}
else
{
src += m_lineOffsets1[8];
}
int off = GetBits (3);
src += s_offTable0[off];
}
src &= 0xFFFF;
int count = GetBitLength() + 1;
Binary.CopyOverlapped (m_buffer, src, dst, count);
y += count;
dst += count;
}
else
{
int bx = m_buffer[dst-2];
bx = (bx << 8 | bx) & 0xF00F;
if (GetNextBit()) // @8@
{
int ax = GetBits (4);
bx = (bx & 0xFF) | ax << 12;
}
if (GetNextBit()) // @9@
{
int ax = GetBits (4);
bx = (bx & 0xFF00) | ax;
}
bx = (bx & 0xFF) | (bx >> 8);
m_buffer[dst++] = (byte)bx;
++y;
}
}
++x;
if ((x & 3) == 0)
{
CopyScanline (m_lineOffsets0[(x - 1) & 0xC], output, output_pos);
output_pos += 4;
}
int z = x & 0xF;
dst = m_lineOffsets0[z];
if (z != 0)
{
m_lineOffsets1[z] -= BufferSize;
}
else
{
for (int i = 1; i < 16; ++i)
{
m_lineOffsets1[i] += BufferSize;
}
}
}
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, output, m_stride);
}
ushort[] m_lineOffsets0 = new ushort[16];
ushort[] m_lineOffsets1 = new ushort[16];
void FillBuffers ()
{
m_buffer = new byte[BufferSize + MaxHeight];
m_buffer[0] = m_buffer[1] = 3;
ushort p = 2;
for (int i = 0; i < 16; ++i)
{
m_lineOffsets1[i] = (ushort)(((16 - i) & 0xF) * MaxHeight);
m_lineOffsets0[i] = p;
p += MaxHeight;
}
}
void CopyScanline (int src, byte[] output, int dst)
{
for (int i = 0; i < m_info.iHeight; ++i)
{
output[dst ] = m_buffer[src + i ];
output[dst+1] = m_buffer[src + i + MaxHeight ];
output[dst+2] = m_buffer[src + i + MaxHeight*2];
output[dst+3] = m_buffer[src + i + MaxHeight*3];
dst += m_stride;
}
}
}
}

330
Legacy/Ponytail/ImageTSZ.cs Normal file
View File

@ -0,0 +1,330 @@
//! \file ImageTSZ.cs
//! \date 2023 Sep 27
//! \brief Ponytail NMI 2.05 image format (PC-98).
//
// 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
// [930413][Ponytail Soft] Yougen Doumu
namespace GameRes.Formats.Ponytail
{
[Export(typeof(ImageFormat))]
public class TszFormat : ImageFormat
{
public override string Tag => "TSZ";
public override string Description => "Ponytail Soft NMI image format";
public override uint Signature => 0x20494D4E; // 'NMI '
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual (4, "2.05"))
return null;
return new ImageMetaData {
Width = (uint)header.ToUInt16 (0xC) << 2,
Height = header.ToUInt16 (0xE),
BPP = 4,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new TszReader (file, info);
return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("TszFormat.Write not implemented");
}
}
internal class TszReader
{
protected IBinaryStream m_input;
protected ImageMetaData m_info;
protected int m_stride;
protected TszReader () { }
public TszReader (IBinaryStream input, ImageMetaData info)
{
m_input = input;
m_info = info;
m_stride = m_info.iWidth >> 1;
}
private int m_previous_row;
private ushort[] m_linebuffer;
private int m_dst;
public ImageData Unpack ()
{
m_input.Position = 0x10;
var palette = ReadPalette();
var output = new byte[m_stride * m_info.iHeight];
m_linebuffer = new ushort[m_info.iHeight * 2];
m_previous_row = m_info.iHeight;
int width = m_info.iWidth >> 2;
int dst_x = 0;
int x = 0;
ResetBitReader();
while (x < width)
{
int y = 0;
m_dst = 0;
if ((x & 1) != 0)
m_dst += m_info.iHeight;
while (y < m_info.iHeight)
{
int ctl = 0;
while (GetNextBit())
{
++ctl;
}
int count; // bx
switch (ctl)
{
case 0: count = CopyMethod0(); break;
case 1: count = CopyMethod1(); break;
case 2:
m_linebuffer[m_dst] = m_input.ReadUInt16();
count = 1;
break;
case 3: count = CopyMethod3(); break;
case 4: count = CopyMethod4(); break;
default: throw new InvalidFormatException();
}
m_dst += count;
y += count;
}
if ((x & 1) != 0)
{
CopyScanline (output, dst_x);
dst_x += 4;
}
m_previous_row = -m_previous_row;
++x;
}
return ImageData.Create (m_info, PixelFormats.Indexed4, palette, output, m_stride);
}
void CopyScanline (byte[] output, int dst)
{
for (int i = 0; i < m_info.iHeight; ++i)
{
ushort px1 = m_linebuffer[i];
ushort px2 = m_linebuffer[i + m_info.iHeight];
// these bytes contain 8 pixels that are being put into 4 planes
int b0 = (px1 << 4) & 0xF0 | (px2 ) & 0x0F;
int b1 = (px1 ) & 0xF0 | (px2 >> 4) & 0x0F;
int b2 = (px1 >> 4) & 0xF0 | (px2 >> 8) & 0x0F;
int b3 = (px1 >> 8) & 0xF0 | (px2 >> 12) & 0x0F;
// repack pixels into flat surface, 2 pixels per byte
for (int j = 0; j < 8; j += 2)
{
byte px = (byte)((((b0 << j) & 0x80) >> 3)
| (((b1 << j) & 0x80) >> 2)
| (((b2 << j) & 0x80) >> 1)
| (((b3 << j) & 0x80) ));
px |= (byte)((((b0 << j) & 0x40) >> 6)
| (((b1 << j) & 0x40) >> 5)
| (((b2 << j) & 0x40) >> 4)
| (((b3 << j) & 0x40) >> 3));
output[dst+j/2] = px;
}
dst += m_stride;
}
}
int CopyMethod0 ()
{
int count = GetBitLength();
int offset = GetBits (4);
offset += m_previous_row - 8;
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
return count;
}
int CopyMethod1 ()
{
int count = GetBitLength();
int offset = m_input.ReadUInt8();
offset += m_previous_row - 0x80;
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
return count;
}
int CopyMethod3 ()
{
int count = GetBitLength();
int offset = GetBits (4);
offset -= 0x10;
CopyOverlapped (m_linebuffer, m_dst + offset, m_dst, count);
return count;
}
int CopyMethod4 ()
{
byte al = m_input.ReadUInt8();
int nibble = al >> 4;
ushort mask1 = s_pattern1[al >> 4];
ushort mask2 = s_pattern2[al & 0xF];
ushort pixel = m_linebuffer[m_dst + m_previous_row];
pixel &= mask1;
for (int i = 0; i < 4; ++i)
{
short carry = (short)(nibble & 1);
nibble >>= 1;
pixel |= (ushort)(-carry & mask2);
pixel = RotU16R (pixel, 1);
}
pixel = RotU16L (pixel, 4);
m_linebuffer[m_dst] = pixel;
return 1;
}
static readonly ushort[] s_pattern1 = new ushort[] {
0xFFFF, 0xEEEE, 0xDDDD, 0xCCCC, 0xBBBB, 0xAAAA, 0x9999, 0x8888,
0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000,
};
static readonly ushort[] s_pattern2 = new ushort[] {
0x0000, 0x0001, 0x0010, 0x0011, 0x0100, 0x0101, 0x0110, 0x0111,
0x1000, 0x1001, 0x1010, 0x1011, 0x1100, 0x1101, 0x1110, 0x1111,
};
ushort m_bits;
int m_bit_count;
protected void ResetBitReader ()
{
m_bits = 0;
m_bit_count = 0;
}
protected int GetBitLength ()
{
if (!GetNextBit())
return 1;
int count = 1;
while (GetNextBit())
{
++count;
}
return GetBits (count) | 1 << count;
}
protected bool GetNextBit ()
{
if (--m_bit_count < 0)
{
m_bits = m_input.ReadUInt16();
m_bit_count = 15;
}
bool bit = (m_bits & 0x8000) != 0;
m_bits <<= 1;
return bit;
}
static readonly ushort[] s_bit_mask = new ushort[] {
0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
};
protected int GetBits (int count)
{
m_bit_count -= count;
if (m_bit_count < 0)
{
m_bits = RotU16L (m_bits, count);
int cl = -m_bit_count;
ushort bits = m_input.ReadUInt16();
bits = RotU16L (bits, cl);
ushort mask = s_bit_mask[cl];
ushort new_bits = (ushort)(bits & ~mask);
bits &= mask;
bits |= m_bits;
m_bits = new_bits;
m_bit_count = 16 - cl;
return bits;
}
else
{
m_bits = RotU16L (m_bits, count);
ushort mask = s_bit_mask[count];
int bits = m_bits & mask;
m_bits &= (ushort)~mask;
return bits;
}
}
protected BitmapPalette ReadPalette ()
{
const int count = 16;
var colors = new Color[count];
for (int i = 0; i < count; ++i)
{
byte r = m_input.ReadUInt8();
byte g = m_input.ReadUInt8();
byte b = m_input.ReadUInt8();
colors[i] = Color.FromRgb ((byte)(r * 0x11), (byte)(g * 0x11), (byte)(b * 0x11));
}
return new BitmapPalette (colors);
}
static internal ushort RotU16L (ushort val, int count)
{
return (ushort)(val << count | val >> (16 - count));
}
static internal ushort RotU16R (ushort val, int count)
{
return (ushort)(val >> count | val << (16 - count));
}
static internal void CopyOverlapped (ushort[] data, int src, int dst, int count)
{
src <<= 1;
dst <<= 1;
count <<= 1;
if (dst > src)
{
while (count > 0)
{
int preceding = Math.Min (dst - src, count);
Buffer.BlockCopy (data, src, data, dst, preceding);
dst += preceding;
count -= preceding;
}
}
else
{
Buffer.BlockCopy (data, src, data, dst, count);
}
}
}
}

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.0.10.208")]
[assembly: AssemblyFileVersion ("1.0.10.208")]
[assembly: AssemblyVersion ("1.0.10.210")]
[assembly: AssemblyFileVersion ("1.0.10.210")]

146
Legacy/Sophia/ArcNOR.cs Normal file
View File

@ -0,0 +1,146 @@
//! \file ArcNOR.cs
//! \date 2023 Sep 26
//! \brief Sophia 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;
// [991210][Sophia] Film Noir
namespace GameRes.Formats.Sophia
{
internal class NorEntry : PackedEntry
{
public int Method;
}
[Export(typeof(ArchiveFormat))]
public class NorOpener : ArchiveFormat
{
public override string Tag => "NOR";
public override string Description => "Sophia resource archive";
public override uint Signature => 0;
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (0);
if (!file.View.AsciiEqual (4, "NRCOMB01\0") || !IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
using (var index = file.CreateStream())
{
index.Position = 0x10;
for (int i = 0; i < count; ++i)
{
uint offset = index.ReadUInt32();
uint size = index.ReadUInt32();
string name = index.ReadCString();
var entry = Create<NorEntry> (name);
entry.Offset = offset;
entry.Size = size;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var nent = (NorEntry)entry;
if (!nent.IsPacked)
{
if (nent.Method == 0x1F4 || nent.Method == 0x67
|| !arc.File.View.AsciiEqual (nent.Offset, "NCMB01"))
return base.OpenEntry (arc, nent);
nent.Method = arc.File.View.ReadInt32 (nent.Offset+0x28);
if (nent.Method == 0x1F4 || nent.Method == 0x67)
{
nent.Size = arc.File.View.ReadUInt32 (nent.Offset+0x24);
nent.Offset += 0x2C;
return base.OpenEntry (arc, nent);
}
nent.IsPacked = true;
nent.UnpackedSize = arc.File.View.ReadUInt32 (nent.Offset+0x24);
nent.Size = arc.File.View.ReadUInt32 (nent.Offset+0x10);
nent.Offset += 0x2C;
}
using (var input = arc.File.CreateStream (nent.Offset, nent.Size))
{
var output = new byte[nent.UnpackedSize];
NcmbDecompress (input, output);
return new BinMemoryStream (output, nent.Name);
}
}
internal static void NcmbDecompress (IBinaryStream input, byte[] output)
{
var dict = new int[0xC00];
int root = input.ReadInt32();
int tree_size = input.ReadInt32();
int unpacked_size = input.ReadInt32();
int count = root + tree_size - 0xFF;
while (count --> 0)
{
int token = 6 * input.ReadInt32();
dict[token ] = input.ReadInt32();
dict[token + 1] = input.ReadInt32();
}
if (unpacked_size > 0)
{
int cur_byte = 0;
int mask = 0;
for (int dst = 0; dst < unpacked_size; ++dst)
{
int token = root;
do
{
if (0 == mask)
{
cur_byte = input.ReadUInt8();
mask = 0x80;
}
if ((cur_byte & mask) != 0)
token = dict[6 * token + 1];
else
token = dict[6 * token];
mask >>= 1;
}
while (dict[6 * token] != -1);
output[dst] = (byte)token;
}
}
}
}
[Export(typeof(ResourceAlias))]
[ExportMetadata("Extension", "M")]
[ExportMetadata("Target", "MP3")]
[ExportMetadata("Type", "audio")]
public class MFormat : ResourceAlias { }
}

114
Legacy/SquadraD/ArcPLA.cs Normal file
View File

@ -0,0 +1,114 @@
//! \file ArcPLA.cs
//! \date 2023 Sep 26
//! \brief Squadra D audio 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;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.SquadraD
{
internal class PlaEntry : PackedEntry
{
public int Id;
public int n1;
public uint SampleRate;
public int Channels;
public byte n2;
public byte n3;
public int[] Data;
}
[Export(typeof(ArchiveFormat))]
public class PlaOpener : ArchiveFormat
{
public override string Tag => "PLA";
public override string Description => "Squadra D audio archive";
public override uint Signature => 0x2E616C50; // 'Pla.'
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public override ArcFile TryOpen (ArcView file)
{
uint arc_size = file.View.ReadUInt32 (4);
if (arc_size != file.MaxOffset || file.View.ReadUInt32 (0x10) != 2)
return null;
uint check = (arc_size & 0xD5555555u) << 1 | arc_size & 0xAAAAAAAAu;
if (check != file.View.ReadUInt32 (8))
return null;
int count = file.View.ReadUInt16 (0xE);
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
using (var index = file.CreateStream())
{
index.Position = 0x14;
for (int i = 0; i < count; ++i)
{
var entry = new PlaEntry {
Id = index.ReadInt32()
};
entry.Name = entry.Id.ToString ("D5");
dir.Add (entry);
}
foreach (PlaEntry entry in dir)
{
entry.n1 = index.ReadInt32();
entry.SampleRate = index.ReadUInt32();
entry.Channels = index.ReadInt32();
entry.n2 = index.ReadUInt8();
entry.n3 = index.ReadUInt8();
index.ReadInt16();
}
foreach (PlaEntry entry in dir)
{
entry.Offset = index.ReadUInt32();
}
foreach (PlaEntry entry in dir)
{
int n = entry.Channels * 2;
entry.Data = new int[n];
for (int j = 0; j < n; ++j)
entry.Data[j] = index.ReadInt32();
}
}
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);
}
/*
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
return base.OpenEntry (arc, entry);
}
*/
}
}

121
Legacy/SquadraD/ArcSDA.cs Normal file
View File

@ -0,0 +1,121 @@
//! \file ArcSDA.cs
//! \date 2023 Sep 26
//! \brief Squadra D resource 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;
namespace GameRes.Formats.SquadraD
{
[Export(typeof(ArchiveFormat))]
public class SdaOpener : ArchiveFormat
{
public override string Tag => "SDA/SD";
public override string Description => "Squadra D resource archive";
public override uint Signature => 0x4153; // 'SA'
public override bool IsHierarchic => false;
public override bool CanWrite => false;
public SdaOpener ()
{
Signatures = new[] { 0x4153u, 0xCC004153u, 0u };
}
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (0, "SA\0"))
return null;
int data_offset = file.View.ReadInt32 (4);
if (data_offset <= 8 || data_offset >= file.MaxOffset)
return null;
int count = (data_offset - 8) / 0x14;
if (!IsSaneCount (count))
return null;
string arc_name = Path.GetFileNameWithoutExtension (file.Name);
bool is_cg = arc_name == "g";
uint index = 8;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index, 0x10).Trim();;
var entry = Create<Entry> (name);
entry.Offset = file.View.ReadUInt32 (index+0xC) + data_offset;
entry.Size = file.View.ReadUInt32 (index+0x10);
if (is_cg)
entry.Type = "image";
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index += 0x14;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
{
var data = LzssDecompress (input);
return new BinMemoryStream (data, entry.Name);
}
}
internal static byte[] LzssDecompress (IBinaryStream input)
{
int unpacked_size = input.ReadInt32();
var output = new byte[unpacked_size];
using (var bits = new LsbBitStream (input.AsStream, true))
{
var frame = new byte[0x1000];
int frame_pos = 0xFC0;
int dst = 0;
while (dst < unpacked_size)
{
if (bits.GetNextBit() == 0)
{
byte b = (byte)bits.GetBits (8);
output[dst++] = frame[frame_pos++ & 0xFFF] = b;
}
else
{
int count_len = 4;
if (bits.GetNextBit() != 0)
count_len = 6;
int offset = bits.GetBits (12);
int count = bits.GetBits (count_len);
count = Math.Min (count + 3, unpacked_size - dst);
while (count --> 0)
{
byte b = frame[offset++ & 0xFFF];
output[dst++] = frame[frame_pos++ & 0xFFF] = b;
}
}
}
return output;
}
}
}
}

View File

@ -74,13 +74,15 @@ namespace GameRes.Formats.WestGate
uint next_offset = file.View.ReadUInt32 (index_offset+0xC);
if (next_offset < data_offset)
return null;
string last_name = null;
var invalid_chars = Path.GetInvalidFileNameChars();
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset, 0xC);
if (string.IsNullOrWhiteSpace (name) || name.IndexOfAny (invalid_chars) != -1)
if (last_name == name || string.IsNullOrWhiteSpace (name) || name.IndexOfAny (invalid_chars) != -1)
return null;
last_name = name;
index_offset += 0x10;
var entry = new Entry { Name = name, Type = entry_type };
entry.Offset = next_offset;