(Legacy): implemented encrypted GRD images.

This commit is contained in:
morkt 2018-10-01 17:53:45 +04:00
parent c4cb8e2489
commit 030a65ac35
2 changed files with 88 additions and 32 deletions

View File

@ -125,7 +125,8 @@
<Compile Include="Rune\ArcYK.cs" />
<Compile Include="Sarang\ImageABC.cs" />
<Compile Include="Sogna\ArcSGS.cs" />
<None Include="Speed\ImageDAT.cs" />
<Compile Include="StudioJikkenshitsu\ImageDAT.cs" />
<Compile Include="StudioJikkenshitsu\ImageGRD.cs" />
<Compile Include="Tako\ArcMPK.cs" />
<Compile Include="Tetratech\ArcBND.cs" />
<Compile Include="Tigerman\ArcCHR.cs" />
@ -216,7 +217,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Bom\" />
<Folder Include="StudioJikkenshitsu\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@ -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
@ -36,6 +40,7 @@ namespace GameRes.Formats.Jikkenshitsu
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 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)
{
DecryptBlock (data, pos + i);
}
}
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;
void DecryptBlock (byte[] data, int pos)
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)
{
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;
}
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, dst_stride);
}
}
}