diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj
index 8defd977..8c9ce15a 100644
--- a/Legacy/Legacy.csproj
+++ b/Legacy/Legacy.csproj
@@ -125,7 +125,8 @@
-
+
+
@@ -216,7 +217,6 @@
-
diff --git a/Legacy/StudioJikkenshitsu/ImageGRD.cs b/Legacy/StudioJikkenshitsu/ImageGRD.cs
index 3e79f1c3..28545222 100644
--- a/Legacy/StudioJikkenshitsu/ImageGRD.cs
+++ b/Legacy/StudioJikkenshitsu/ImageGRD.cs
@@ -23,9 +23,13 @@
// IN THE SOFTWARE.
//
+using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using GameRes.Compression;
+using GameRes.Utility;
// [030411][Studio Jikkenshitsu] Giin Oyako
@@ -33,9 +37,10 @@ namespace GameRes.Formats.Jikkenshitsu
{
internal class GrdMetaData : ImageMetaData
{
- public int PackedLength;
- public int AlphaLength;
- public bool IsEncrypted;
+ public int PackedLength;
+ public int AlphaLength;
+ public bool IsEncrypted;
+ public byte[] Key;
}
[Export(typeof(ImageFormat))]
@@ -45,6 +50,9 @@ namespace GameRes.Formats.Jikkenshitsu
public override string Description { get { return "Studio Jikkenshitsu image format"; } }
public override uint Signature { get { return 0x20445247; } } // 'GRD '
+ // Giin Oyako
+ static readonly byte[] DefaultKey = { 0xF, 0, 1, 2, 8, 5, 0xA, 0xB, 5, 9, 0xE, 0xD, 1, 8, 0, 6 };
+
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x18);
@@ -53,16 +61,16 @@ namespace GameRes.Formats.Jikkenshitsu
Height = header.ToUInt16 (8),
BPP = header[4],
PackedLength = header.ToInt32 (0xC),
- AlphaLength = header.ToInt32 (0x10),
+ AlphaLength = header.ToInt32 (0x14),
IsEncrypted = (header[5] & 0x80) != 0,
+ Key = DefaultKey,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new GrdReader (file, (GrdMetaData)info);
- var pixels = reader.Unpack();
- return ImageData.Create (info, reader.Format, null, pixels);
+ return reader.Unpack();
}
public override void Write (Stream file, ImageData image)
@@ -77,54 +85,102 @@ namespace GameRes.Formats.Jikkenshitsu
GrdMetaData m_info;
byte[] m_output;
- public PixelFormat Format { get; private set; }
+ public BitmapPalette Palette { get; private set; }
+ public PixelFormat Format { get; private set; }
+ public int Stride { get; private set; }
public GrdReader (IBinaryStream input, GrdMetaData info)
{
m_input = input;
m_info = info;
if (8 == m_info.BPP)
- Format = PixelFormats.Gray8;
+ Format = PixelFormats.Indexed8;
else if (24 == m_info.BPP)
Format = PixelFormats.Bgr24;
else
throw new InvalidFormatException();
- int image_size = (int)m_info.Width * (int)m_info.Height;
- if (m_info.BPP >= 8)
- image_size *= (m_info.BPP + 1) / 8;
- else
- image_size = image_size * m_info.BPP / 8;
- m_output = new byte[image_size];
+ int width = (int)m_info.Width;
+ if (m_info.BPP <= 8)
+ width = (width + 3) & ~3;
+ Stride = width * m_info.BPP / 8;
+ m_output = new byte[Stride * (int)m_info.Height];
}
- public byte[] Unpack ()
+ public ImageData Unpack ()
{
- m_input.Position = 0x18;
- var input = m_input.ReadBytes (m_info.PackedLength);
+ Stream input = new StreamRegion (m_input.AsStream, 0x18, m_info.PackedLength, true);
if (m_info.IsEncrypted)
- DecryptData (input, 0, input.Length & -8);
- using (var mem = new MemoryStream (input))
- using (var lzss = new LzssStream (mem))
- lzss.Read (m_output, 0, m_output.Length);
- if (m_info.AlphaLength > 0)
+ input = new InputCryptoStream (input, new SjTransform (m_info.Key));
+ using (input = new LzssStream (input))
{
+ var header = new byte[0x28];
+ input.Read (header, 0, header.Length);
+ if (8 == m_info.BPP)
+ Palette = ImageFormat.ReadPalette (input);
+ input.Read (m_output, 0, m_output.Length);
+ }
+ if (m_info.AlphaLength > 0 && m_info.BPP == 8)
+ {
+ m_input.Position = 0x18 + m_info.PackedLength;
var alpha = new byte[m_info.AlphaLength];
using (var lzss = new LzssStream (m_input.AsStream, LzssMode.Decompress, true))
lzss.Read (alpha, 0, alpha.Length);
+ return ApplyAlpha (alpha);
}
- return m_output;
+ return ImageData.CreateFlipped (m_info, Format, Palette, m_output, Stride);
}
- void DecryptData (byte[] data, int pos, int length)
+ ImageData ApplyAlpha (byte[] alpha)
{
- for (int i = 0; i < length; i += 8)
+ int width = (int)m_info.Width;
+ int height = (int)m_info.Height;
+ int dst_stride = width * 4;
+ var pixels = new byte[dst_stride * height];
+ var colors = Palette.Colors;
+
+ var row_table = new ushort[height];
+ int row_table_size = row_table.Length * sizeof(ushort);
+ Buffer.BlockCopy (alpha, 0, row_table, 0, row_table_size);
+ var lines = new int[height];
+ int lines_size = lines.Length * sizeof(int);
+ Buffer.BlockCopy (alpha, row_table_size, lines, 0, lines_size);
+
+ int alpha_pos = row_table_size + lines_size;
+ int src = m_output.Length - Stride;
+ int dst_row = 0;
+ for (int y = 0; y < height; ++y)
{
- DecryptBlock (data, pos + i);
+ int dst = dst_row;
+ for (int x = 0; x < width; ++x)
+ {
+ byte code = m_output[src+x];
+ var color = colors[code];
+ pixels[dst++] = color.B;
+ pixels[dst++] = color.G;
+ pixels[dst++] = color.R;
+ pixels[dst++] = (byte)(code != 0 ? 0xFF : 0);
+ }
+ int asrc = alpha_pos + 4 * lines[y];
+ for (int i = 0; i < row_table[y]; ++i)
+ {
+ byte a = Math.Min (alpha[asrc+3], (byte)0xF);
+ dst = dst_row + LittleEndian.ToUInt16 (alpha, asrc) * 4;
+ if (a > 0)
+ {
+ var color = colors[alpha[asrc+2]];
+ pixels[dst ] = color.B;
+ pixels[dst+1] = color.G;
+ pixels[dst+2] = color.R;
+ pixels[dst+3] = (byte)(a * 0x11);
+ }
+ else
+ pixels[dst+3] = 0;
+ asrc += 4;
+ }
+ dst_row += dst_stride;
+ src -= Stride;
}
- }
-
- void DecryptBlock (byte[] data, int pos)
- {
+ return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, dst_stride);
}
}
}