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

View File

@ -23,9 +23,13 @@
// IN THE SOFTWARE. // IN THE SOFTWARE.
// //
using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging;
using GameRes.Compression;
using GameRes.Utility;
// [030411][Studio Jikkenshitsu] Giin Oyako // [030411][Studio Jikkenshitsu] Giin Oyako
@ -33,9 +37,10 @@ namespace GameRes.Formats.Jikkenshitsu
{ {
internal class GrdMetaData : ImageMetaData internal class GrdMetaData : ImageMetaData
{ {
public int PackedLength; public int PackedLength;
public int AlphaLength; public int AlphaLength;
public bool IsEncrypted; public bool IsEncrypted;
public byte[] Key;
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]
@ -45,6 +50,9 @@ namespace GameRes.Formats.Jikkenshitsu
public override string Description { get { return "Studio Jikkenshitsu image format"; } } public override string Description { get { return "Studio Jikkenshitsu image format"; } }
public override uint Signature { get { return 0x20445247; } } // 'GRD ' 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) public override ImageMetaData ReadMetaData (IBinaryStream file)
{ {
var header = file.ReadHeader (0x18); var header = file.ReadHeader (0x18);
@ -53,16 +61,16 @@ namespace GameRes.Formats.Jikkenshitsu
Height = header.ToUInt16 (8), Height = header.ToUInt16 (8),
BPP = header[4], BPP = header[4],
PackedLength = header.ToInt32 (0xC), PackedLength = header.ToInt32 (0xC),
AlphaLength = header.ToInt32 (0x10), AlphaLength = header.ToInt32 (0x14),
IsEncrypted = (header[5] & 0x80) != 0, IsEncrypted = (header[5] & 0x80) != 0,
Key = DefaultKey,
}; };
} }
public override ImageData Read (IBinaryStream file, ImageMetaData info) public override ImageData Read (IBinaryStream file, ImageMetaData info)
{ {
var reader = new GrdReader (file, (GrdMetaData)info); var reader = new GrdReader (file, (GrdMetaData)info);
var pixels = reader.Unpack(); return reader.Unpack();
return ImageData.Create (info, reader.Format, null, pixels);
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
@ -77,54 +85,102 @@ namespace GameRes.Formats.Jikkenshitsu
GrdMetaData m_info; GrdMetaData m_info;
byte[] m_output; 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) public GrdReader (IBinaryStream input, GrdMetaData info)
{ {
m_input = input; m_input = input;
m_info = info; m_info = info;
if (8 == m_info.BPP) if (8 == m_info.BPP)
Format = PixelFormats.Gray8; Format = PixelFormats.Indexed8;
else if (24 == m_info.BPP) else if (24 == m_info.BPP)
Format = PixelFormats.Bgr24; Format = PixelFormats.Bgr24;
else else
throw new InvalidFormatException(); throw new InvalidFormatException();
int image_size = (int)m_info.Width * (int)m_info.Height; int width = (int)m_info.Width;
if (m_info.BPP >= 8) if (m_info.BPP <= 8)
image_size *= (m_info.BPP + 1) / 8; width = (width + 3) & ~3;
else Stride = width * m_info.BPP / 8;
image_size = image_size * m_info.BPP / 8; m_output = new byte[Stride * (int)m_info.Height];
m_output = new byte[image_size];
} }
public byte[] Unpack () public ImageData Unpack ()
{ {
m_input.Position = 0x18; Stream input = new StreamRegion (m_input.AsStream, 0x18, m_info.PackedLength, true);
var input = m_input.ReadBytes (m_info.PackedLength);
if (m_info.IsEncrypted) if (m_info.IsEncrypted)
DecryptData (input, 0, input.Length & -8); input = new InputCryptoStream (input, new SjTransform (m_info.Key));
using (var mem = new MemoryStream (input)) using (input = new LzssStream (input))
using (var lzss = new LzssStream (mem))
lzss.Read (m_output, 0, m_output.Length);
if (m_info.AlphaLength > 0)
{ {
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]; var alpha = new byte[m_info.AlphaLength];
using (var lzss = new LzssStream (m_input.AsStream, LzssMode.Decompress, true)) using (var lzss = new LzssStream (m_input.AsStream, LzssMode.Decompress, true))
lzss.Read (alpha, 0, alpha.Length); 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;
} }
} return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, dst_stride);
void DecryptBlock (byte[] data, int pos)
{
} }
} }
} }