mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-24 20:04:13 +08:00
(IafFormat): new file layout variant.
recognize bitmaps with appended alpha-channel.
This commit is contained in:
parent
8dabf68291
commit
ca42a0cfa4
@ -26,12 +26,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Windows.Media;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.Triangle
|
namespace GameRes.Formats.Triangle
|
||||||
{
|
{
|
||||||
internal class IafMetaData : ImageMetaData
|
internal class IafMetaData : ImageMetaData
|
||||||
{
|
{
|
||||||
|
public int DataOffset;
|
||||||
|
public int PackedSize;
|
||||||
public int UnpackedSize;
|
public int UnpackedSize;
|
||||||
public int PackType;
|
public int PackType;
|
||||||
}
|
}
|
||||||
@ -45,16 +48,35 @@ namespace GameRes.Formats.Triangle
|
|||||||
|
|
||||||
public override ImageMetaData ReadMetaData (Stream stream)
|
public override ImageMetaData ReadMetaData (Stream stream)
|
||||||
{
|
{
|
||||||
var header = new byte[12];
|
var header = new byte[0x14];
|
||||||
if (header.Length != stream.Read (header, 0, header.Length))
|
if (12 != stream.Read (header, 0, 12))
|
||||||
return null;
|
return null;
|
||||||
int x = LittleEndian.ToInt32 (header, 0);
|
int data_offset;
|
||||||
int y = LittleEndian.ToInt32 (header, 4);
|
int packed_size = LittleEndian.ToInt32 (header, 1);
|
||||||
|
int x, y, unpacked_size;
|
||||||
|
if (5+packed_size+0x14 == stream.Length)
|
||||||
|
{
|
||||||
|
data_offset = 5;
|
||||||
|
stream.Seek (-0x14, SeekOrigin.End);
|
||||||
|
if (0x14 != stream.Read (header, 0, 0x14))
|
||||||
|
return null;
|
||||||
|
x = LittleEndian.ToInt32 (header, 0);
|
||||||
|
y = LittleEndian.ToInt32 (header, 4);
|
||||||
|
unpacked_size = LittleEndian.ToInt32 (header, 0x10);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data_offset = 12;
|
||||||
|
x = LittleEndian.ToInt32 (header, 0);
|
||||||
|
y = LittleEndian.ToInt32 (header, 4);
|
||||||
|
unpacked_size = LittleEndian.ToInt32 (header, 8);
|
||||||
|
packed_size = (int)stream.Length-12;
|
||||||
|
}
|
||||||
if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096)
|
if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096)
|
||||||
return null;
|
return null;
|
||||||
int unpacked_size = LittleEndian.ToInt32 (header, 8);
|
|
||||||
int pack_type = (unpacked_size >> 30) & 3;
|
int pack_type = (unpacked_size >> 30) & 3;
|
||||||
unpacked_size &= (int)~0xC0000000;
|
unpacked_size &= (int)~0xC0000000;
|
||||||
|
stream.Position = data_offset;
|
||||||
byte[] bmp;
|
byte[] bmp;
|
||||||
if (0 == pack_type)
|
if (0 == pack_type)
|
||||||
{
|
{
|
||||||
@ -93,6 +115,8 @@ namespace GameRes.Formats.Triangle
|
|||||||
OffsetX = x,
|
OffsetX = x,
|
||||||
OffsetY = y,
|
OffsetY = y,
|
||||||
BPP = bpp,
|
BPP = bpp,
|
||||||
|
DataOffset = data_offset,
|
||||||
|
PackedSize = packed_size,
|
||||||
UnpackedSize = unpacked_size,
|
UnpackedSize = unpacked_size,
|
||||||
PackType = pack_type,
|
PackType = pack_type,
|
||||||
};
|
};
|
||||||
@ -104,38 +128,60 @@ namespace GameRes.Formats.Triangle
|
|||||||
if (null == meta)
|
if (null == meta)
|
||||||
throw new ArgumentException ("IafFormat.Read should be supplied with IafMetaData", "info");
|
throw new ArgumentException ("IafFormat.Read should be supplied with IafMetaData", "info");
|
||||||
|
|
||||||
stream.Position = 12;
|
stream.Position = meta.DataOffset;
|
||||||
int packed_size = (int)stream.Length-12;
|
byte[] bitmap;
|
||||||
byte[] pixels;
|
|
||||||
if (2 == meta.PackType)
|
if (2 == meta.PackType)
|
||||||
{
|
{
|
||||||
using (var reader = new RleReader (stream, packed_size, meta.UnpackedSize))
|
using (var reader = new RleReader (stream, meta.PackedSize, meta.UnpackedSize))
|
||||||
{
|
{
|
||||||
reader.Unpack();
|
reader.Unpack();
|
||||||
pixels = reader.Data;
|
bitmap = reader.Data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (0 == meta.PackType)
|
else if (0 == meta.PackType)
|
||||||
{
|
{
|
||||||
using (var reader = new LzssReader (stream, packed_size, meta.UnpackedSize))
|
using (var reader = new LzssReader (stream, meta.PackedSize, meta.UnpackedSize))
|
||||||
{
|
{
|
||||||
reader.Unpack();
|
reader.Unpack();
|
||||||
pixels = reader.Data;
|
bitmap = reader.Data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pixels = new byte[meta.UnpackedSize];
|
bitmap = new byte[meta.UnpackedSize];
|
||||||
if (pixels.Length != stream.Read (pixels, 0, pixels.Length))
|
if (bitmap.Length != stream.Read (bitmap, 0, bitmap.Length))
|
||||||
throw new InvalidFormatException ("Unexpected end of file");
|
throw new InvalidFormatException ("Unexpected end of file");
|
||||||
}
|
}
|
||||||
if ('C' == pixels[0])
|
if ('C' == bitmap[0])
|
||||||
{
|
{
|
||||||
pixels[0] = (byte)'B';
|
bitmap[0] = (byte)'B';
|
||||||
if (info.BPP > 8)
|
if (info.BPP > 8)
|
||||||
pixels = ConvertCM (pixels, (int)info.Width, (int)info.Height, info.BPP);
|
bitmap = ConvertCM (bitmap, (int)info.Width, (int)info.Height, info.BPP);
|
||||||
}
|
}
|
||||||
using (var bmp = new MemoryStream (pixels))
|
if (meta.BPP >= 24) // currently alpha channel could be applied to 24+bpp bitmaps only
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int bmp_size = LittleEndian.ToInt32 (bitmap, 2);
|
||||||
|
if (bitmap.Length - bmp_size > 0x36) // size of bmp header
|
||||||
|
{
|
||||||
|
if ('B' == bitmap[bmp_size] && 'M' == bitmap[bmp_size+1] &&
|
||||||
|
8 == bitmap[bmp_size+0x1c]) // 8bpp
|
||||||
|
{
|
||||||
|
uint alpha_width = LittleEndian.ToUInt32 (bitmap, bmp_size+0x12);
|
||||||
|
uint alpha_height = LittleEndian.ToUInt32 (bitmap, bmp_size+0x16);
|
||||||
|
if (info.Width == alpha_width && info.Height == alpha_height)
|
||||||
|
return BitmapWithAlphaChannel (meta, bitmap, bmp_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore any errors occured during alpha-channel read attempt,
|
||||||
|
// fallback to a plain bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using (var bmp = new MemoryStream (bitmap))
|
||||||
return base.Read (bmp, info);
|
return base.Read (bmp, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,6 +190,30 @@ namespace GameRes.Formats.Triangle
|
|||||||
throw new System.NotImplementedException ("IafFormat.Write not implemented");
|
throw new System.NotImplementedException ("IafFormat.Write not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ImageData BitmapWithAlphaChannel (ImageMetaData info, byte[] bitmap, int alpha_offset)
|
||||||
|
{
|
||||||
|
int src_pixel_size = info.BPP/8;
|
||||||
|
int src_stride = (int)info.Width*src_pixel_size;
|
||||||
|
int src_pixels = LittleEndian.ToInt32 (bitmap, 0x0A);
|
||||||
|
int src_alpha = alpha_offset + LittleEndian.ToInt32 (bitmap, alpha_offset+0x0A);
|
||||||
|
var pixels = new byte[info.Width * info.Height * 4];
|
||||||
|
int dst = 0;
|
||||||
|
for (int y = (int)info.Height-1; y >= 0; --y)
|
||||||
|
{
|
||||||
|
int src = src_pixels + y*src_stride;
|
||||||
|
int alpha = src_alpha + y*(int)info.Width;
|
||||||
|
for (uint x = 0; x < info.Width; ++x)
|
||||||
|
{
|
||||||
|
pixels[dst++] = bitmap[src];
|
||||||
|
pixels[dst++] = bitmap[src+1];
|
||||||
|
pixels[dst++] = bitmap[src+2];
|
||||||
|
pixels[dst++] = (byte)~bitmap[alpha++];
|
||||||
|
src += src_pixel_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
static byte[] ConvertCM (byte[] input, int width, int height, int bpp)
|
static byte[] ConvertCM (byte[] input, int width, int height, int bpp)
|
||||||
{
|
{
|
||||||
int src = LittleEndian.ToInt32 (input, 0x0a);
|
int src = LittleEndian.ToInt32 (input, 0x0a);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user