mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-06 13:48:57 +08:00
144 lines
5.2 KiB
C#
144 lines
5.2 KiB
C#
using System;
|
|
using System.ComponentModel.Composition;
|
|
using System.IO;
|
|
using System.Linq; // Added for list operations if necessary
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using GameRes.Utility;
|
|
|
|
namespace GameRes.Formats.Broccoli
|
|
{
|
|
[Export(typeof(ImageFormat))]
|
|
public class GrpFormat : ImageFormat
|
|
{
|
|
public override string Tag { get { return "GRP/BROCCOLI"; } }
|
|
public override string Description { get { return "Broccoli engine image"; } }
|
|
public override uint Signature { get { return 0; } }
|
|
|
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
|
{
|
|
// We need to decompress to know the real size
|
|
using (var data = DecompressData(file))
|
|
{
|
|
if (data == null) return null;
|
|
|
|
var size = data.Length;
|
|
uint width = 0, height = 0;
|
|
int bpp = 32;
|
|
|
|
// Same resolution logic as your Python script
|
|
if (size == 1920000) { width = 800; height = 600; }
|
|
else if (size == 1228800) { width = 640; height = 480; }
|
|
else if (size == 1536000) { width = 800; height = 480; }
|
|
else if (size == 768000) { width = 640; height = 400; }
|
|
else if (size == 307200) { width = 640; height = 480; bpp = 8; }
|
|
else if (size == 96000) { width = 200; height = 120; }
|
|
else
|
|
{
|
|
// Fallback: Try to read 4-byte header (Width/Height)
|
|
if (size > 4)
|
|
{
|
|
data.Position = 0;
|
|
var reader = new BinaryReader(data);
|
|
ushort w = reader.ReadUInt16();
|
|
ushort h = reader.ReadUInt16();
|
|
if (w * h * 4 == size - 4)
|
|
{
|
|
width = w;
|
|
height = h;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (width == 0) return null;
|
|
|
|
return new ImageMetaData
|
|
{
|
|
Width = width,
|
|
Height = height,
|
|
BPP = bpp,
|
|
};
|
|
}
|
|
}
|
|
|
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
|
{
|
|
using (var stream = DecompressData(file))
|
|
{
|
|
if (stream == null) throw new InvalidFormatException();
|
|
|
|
// Skip 4-byte header if fallback logic was used
|
|
int headerSkip = 0;
|
|
if (stream.Length != info.Width * info.Height * (info.BPP / 8))
|
|
{
|
|
if (stream.Length == (info.Width * info.Height * 4) + 4)
|
|
headerSkip = 4;
|
|
}
|
|
|
|
stream.Position = headerSkip;
|
|
|
|
// Case 1: Isolated Mask (Grayscale)
|
|
if (info.BPP == 8)
|
|
{
|
|
var gray = new byte[info.Width * info.Height];
|
|
stream.Read(gray, 0, gray.Length);
|
|
return ImageData.Create(info, PixelFormats.Gray8, null, gray);
|
|
}
|
|
|
|
// Case 2: Colored Image (BGRX)
|
|
// Due to API limitations, we won't load the mask (_m) here.
|
|
// GARbro will show the image with a black/solid background.
|
|
// Use your Python script to combine with the mask later.
|
|
|
|
var pixels = new byte[info.Width * info.Height * 4];
|
|
stream.Read(pixels, 0, pixels.Length);
|
|
|
|
// The format is usually Bgr32 (the 4th byte is garbage/padding, not real alpha)
|
|
return ImageData.Create(info, PixelFormats.Bgr32, null, pixels);
|
|
}
|
|
}
|
|
|
|
public override void Write (Stream file, ImageData image)
|
|
{
|
|
throw new System.NotImplementedException("Use repack.py");
|
|
}
|
|
|
|
private MemoryStream DecompressData(IBinaryStream input)
|
|
{
|
|
input.Position = 0;
|
|
byte[] buffer = input.ReadBytes(2048);
|
|
int zlibOffset = -1;
|
|
|
|
// Search for ZLIB signature (78 9C, etc)
|
|
for (int i = 0; i < buffer.Length - 1; i++)
|
|
{
|
|
if (buffer[i] == 0x78 &&
|
|
(buffer[i+1] == 0x9C || buffer[i+1] == 0xDA || buffer[i+1] == 0x01))
|
|
{
|
|
zlibOffset = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (zlibOffset == -1) return null;
|
|
|
|
try
|
|
{
|
|
// Skip ZLIB header (2 bytes) to use standard DeflateStream
|
|
input.Position = zlibOffset + 2;
|
|
|
|
using (var zStream = new System.IO.Compression.DeflateStream(input.AsStream, System.IO.Compression.CompressionMode.Decompress, true))
|
|
{
|
|
var output = new MemoryStream();
|
|
zStream.CopyTo(output);
|
|
output.Position = 0;
|
|
return output;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
} |