(ISG): moved to Ism folder.

This commit is contained in:
morkt 2023-09-07 11:59:51 +04:00
parent 4e8a4edd4a
commit 68dae3e40a

View File

@ -41,6 +41,7 @@ namespace GameRes.Formats.ISM
public int Colors; public int Colors;
public uint Packed; public uint Packed;
public uint Unpacked; public uint Unpacked;
public int RecursionDepth;
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]
@ -78,88 +79,46 @@ namespace GameRes.Formats.ISM
public override ImageData Read (IBinaryStream stream, ImageMetaData info) public override ImageData Read (IBinaryStream stream, ImageMetaData info)
{ {
var meta = (IsgMetaData)info; var meta = (IsgMetaData)info;
if (0x21 != meta.Type && 0x10 != meta.Type) if (0x21 != meta.Type && 0x10 != meta.Type && 0x34 != meta.Type)
throw new InvalidFormatException ("Unsupported ISM image type"); throw new InvalidFormatException ("Unsupported ISM image type");
stream.Position = 0x30; var input = new Reader (stream, meta);
using (var input = new Reader (stream, meta)) var pixels = input.Unpack (this);
{
if (0x21 == meta.Type)
input.Unpack21();
else
input.Unpack10();
var palette = new BitmapPalette (input.Palette); var palette = new BitmapPalette (input.Palette);
return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, input.Data, (int)info.Width); return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, input.Data, info.iWidth);
}
} }
internal class Reader : IDisposable internal class Reader
{ {
IBinaryStream m_input; IBinaryStream m_input;
byte[] m_data; byte[] m_data;
Color[] m_palette;
int m_input_size; int m_input_size;
IsgMetaData m_info;
public Color[] Palette { get { return m_palette; } } public const int RecursionLimit = 32; // have seen 18 deep
public Color[] Palette { get; private set; }
public byte[] Data { get { return m_data; } } public byte[] Data { get { return m_data; } }
public Reader (IBinaryStream file, IsgMetaData info) public Reader (IBinaryStream file, IsgMetaData info)
{ {
int palette_size = (int)info.Colors*4;
var palette_data = new byte[Math.Max (0x400, palette_size)];
if (palette_size != file.Read (palette_data, 0, palette_size))
throw new InvalidFormatException();
m_palette = new Color[0x100];
for (int i = 0; i < m_palette.Length; ++i)
{
m_palette[i] = Color.FromRgb (palette_data[i*4+2], palette_data[i*4+1], palette_data[i*4]);
}
m_input = file; m_input = file;
m_input_size = (int)info.Packed; m_input_size = (int)info.Packed;
m_data = new byte[info.Width * info.Height]; m_info = info;
} }
public void Unpack21 () public byte[] Unpack (IsgFormat isg)
{ {
int dst = 0; m_input.Position = 0x30;
var frame = new byte[2048]; if (0x34 == m_info.Type)
int frame_pos = 2039; return Unpack34(isg);
int remaining = m_input_size; Palette = ImageFormat.ReadColorMap (m_input.AsStream);
byte ctl = m_input.ReadUInt8(); m_data = new byte[m_info.Width * m_info.Height];
--remaining; if (0x21 == m_info.Type)
int bit = 0x80; DecompressLzss (m_input_size, m_data);
while (remaining > 0)
{
if (0 != (ctl & bit))
{
byte hi = m_input.ReadUInt8();
byte lo = m_input.ReadUInt8();
remaining -= 2;
int offset = (hi & 7) << 8 | lo;
for (int count = (hi >> 3) + 3; count > 0; --count)
{
byte p = frame[offset];
frame[frame_pos] = p;
m_data[dst++] = p;
offset = (offset + 1) & 0x7ff;
frame_pos = (frame_pos + 1) & 0x7ff;
}
}
else else
{ Unpack10();
byte p = m_input.ReadUInt8(); return m_data;
--remaining;
m_data[dst++] = p;
frame[frame_pos] = p;
frame_pos = (frame_pos + 1) & 0x7ff;
}
if (0 == (bit >>= 1))
{
ctl = m_input.ReadUInt8();
--remaining;
bit = 0x80;
}
}
} }
public void Unpack10 () public void Unpack10 ()
@ -192,12 +151,112 @@ namespace GameRes.Formats.ISM
} }
} }
#region IDisposable Members public byte[] Unpack34 (IsgFormat isg)
public void Dispose ()
{ {
GC.SuppressFinalize (this); if (m_info.RecursionDepth >= RecursionLimit)
throw new InvalidFormatException ("Recursion limit reached for ISG image.");
var base_name = m_input.ReadCString (0x10);
var base_image = ReadBaseImage (base_name, isg);
if (null == base_image)
throw new InvalidFormatException ("Unable to read baseline ISG image.");
int count = m_input.ReadInt32();
int packed_size = m_input.ReadInt32();
int ovl_ctl_size = m_info.iWidth * m_info.iHeight / 128;
var overlay_info = m_input.ReadBytes (ovl_ctl_size);
var overlay_data = new byte[count * 32 + 8];
DecompressLzss (packed_size, overlay_data);
int bit_count = 0;
int ctl_src = 0;
int data_src = 0;
for (int y = 0; y < m_info.iHeight; y += 4)
for (int x = 0; x < m_info.iWidth; x += 4)
{
if (((1 << bit_count) & overlay_info[ctl_src]) != 0)
{
int dst = y * m_info.iWidth + x;
for (int r = 0; r < 4; ++r)
{
base_image[dst ] = overlay_data[data_src++];
base_image[dst+1] = overlay_data[data_src++];
base_image[dst+2] = overlay_data[data_src++];
base_image[dst+3] = overlay_data[data_src++];
dst += m_info.iWidth;
}
}
if (++bit_count == 8)
{
bit_count = 0;
++ctl_src;
}
}
return m_data = base_image;
}
internal void DecompressLzss (int remaining, byte[] output)
{
int dst = 0;
var frame = new byte[2048];
int frame_pos = 2039;
byte ctl = m_input.ReadUInt8();
--remaining;
int bit = 0x80;
while (remaining > 0)
{
if (0 != (ctl & bit))
{
byte hi = m_input.ReadUInt8();
byte lo = m_input.ReadUInt8();
remaining -= 2;
int offset = (hi & 7) << 8 | lo;
for (int count = (hi >> 3) + 3; count > 0; --count)
{
byte p = frame[offset];
frame[frame_pos] = p;
output[dst++] = p;
offset = (offset + 1) & 0x7ff;
frame_pos = (frame_pos + 1) & 0x7ff;
}
}
else
{
byte p = m_input.ReadUInt8();
--remaining;
output[dst++] = p;
frame[frame_pos] = p;
frame_pos = (frame_pos + 1) & 0x7ff;
}
if (0 == (bit >>= 1))
{
ctl = m_input.ReadUInt8();
--remaining;
bit = 0x80;
}
}
}
internal byte[] ReadBaseImage (string name, IsgFormat isg)
{
if (!VFS.FileExists (name))
{
if (name.Length <= 12)
return null;
name = name.Substring (0, 12);
if (!VFS.FileExists (name))
return null;
}
using (var base_file = VFS.OpenBinaryStream (name))
{
var base_info = isg.ReadMetaData (base_file) as IsgMetaData;
if (null == base_info || base_info.Width != m_info.Width || base_info.Height != m_info.Height)
throw new InvalidFormatException ("Invalid baseline ISG image.");
base_info.RecursionDepth = m_info.RecursionDepth + 1;
base_info.FileName = name;
var reader = new Reader (base_file, base_info);
var pixels = reader.Unpack (isg);
this.Palette = reader.Palette;
return pixels;
}
} }
#endregion
} }
} }
} }