(Legacy): CGD images and ASD archives.

This commit is contained in:
morkt 2019-01-11 18:16:44 +04:00
parent 8f9cc46dba
commit ae7630b5be
3 changed files with 392 additions and 2 deletions

125
Legacy/KApp/ArcASD.cs Normal file
View File

@ -0,0 +1,125 @@
//! \file ArcASD.cs
//! \date 2019 Jan 11
//! \brief Spiel resource archive.
//
// Copyright (C) 2019 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.KApp
{
[Export(typeof(ArchiveFormat))]
public class AsdOpener : ArchiveFormat
{
public override string Tag { get { return "ASD/KTOOL"; } }
public override string Description { get { return "KApp engine resource archive"; } }
public override uint Signature { get { return 0x6F6F746B; } } // 'ktool210'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (8);
if (!IsSaneCount (count))
return null;
var base_name = Path.GetFileNameWithoutExtension (file.Name);
uint index_pos = 0x10;
uint next_offset = file.View.ReadUInt32 (index_pos);
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
index_pos += 4;
var entry = new Entry {
Name = string.Format ("{0}#{1:D4}", base_name, i),
Offset = next_offset,
};
next_offset = file.View.ReadUInt32 (index_pos);
entry.Size = (uint)(next_offset - entry.Offset);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
DetectFileTypes (file, dir);
return new ArcFile (file, this, dir);
}
void DetectFileTypes (ArcView file, List<Entry> dir)
{
foreach (var entry in dir)
{
var type = file.View.ReadUInt32 (entry.Offset+0xC);
switch (type)
{
case 0xB713E4: entry.Type = "audio"; break;
case 0xB29EA4:
case 0x973768: entry.Type = "image"; break;
}
}
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
uint id = arc.File.View.ReadUInt32 (entry.Offset+0xC);
if (id != 0xB713E4)
return base.OpenEntry (arc, entry);
return OpenAudio (arc, entry);
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{
var input = arc.File.CreateStream (entry.Offset, entry.Size);
var info = CgdMetaData.FromStream (input, 0);
input.Position = 0;
if (null == info)
return ImageFormatDecoder.Create (input);
return new CgdDecoder (input, info);
}
Stream OpenAudio (ArcFile arc, Entry entry)
{
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
{
var header = input.ReadHeader (0x20);
int header_size = header.ToUInt16 (10);
var format = new WaveFormat {
FormatTag = header.ToUInt16 (0x10),
Channels = header.ToUInt16 (0x12),
SamplesPerSecond = header.ToUInt32 (0x14),
AverageBytesPerSecond = header.ToUInt32 (0x18),
BlockAlign = header.ToUInt16 (0x1C),
BitsPerSample = header.ToUInt16 (0x1E),
};
input.Position = header_size + 0x10;
var data = new byte[header.ToInt32 (0)];
KTool.Unpack (input, data, header[8]);
var output = new MemoryStream (data.Length);
WaveAudio.WriteRiffHeader (output, format, (uint)data.Length);
output.Write (data, 0, data.Length);
output.Position = 0;
return output;
}
}
}
}

261
Legacy/KApp/ImageCGD.cs Normal file
View File

@ -0,0 +1,261 @@
//! \file ImageCGD.cs
//! \date 2019 Jan 10
//! \brief Spiel compressed image.
//
// Copyright (C) 2019 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
// [030228][spiel] The Black Box
namespace GameRes.Formats.KApp
{
[Export(typeof(ImageFormat))]
public class CgdFormat : ImageFormat
{
public override string Tag { get { return "CGD/KTOOL"; } }
public override string Description { get { return "KApp compressed image format"; } }
public override uint Signature { get { return 0x6F6F746B; } } // 'ktool210'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x18);
if (!header.AsciiEqual ("ktool210") || header.ToInt32 (8) != 1)
return null;
uint offset = header.ToUInt32 (0x10) & 0x7FFFFFFF;
return CgdMetaData.FromStream (file, offset);
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new CgdDecoder (file, (CgdMetaData)info);
return reader.Image;
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("CgdFormat.Write not implemented");
}
}
internal class CgdMetaData : ImageMetaData
{
public uint DataOffset;
public int UnpackedSize;
public byte Compression;
internal static CgdMetaData FromStream (IBinaryStream file, uint offset)
{
file.Position = offset;
int unpacked_size = file.ReadInt32();
file.ReadInt32();
byte compression = (byte)file.ReadUInt16();
uint header_size = file.ReadUInt16();
uint id = file.ReadUInt32();
if (header_size < 0x10 || (id != 0x973768 && id != 0xB29EA4))
return null;
ushort width = file.ReadUInt16();
ushort height = file.ReadUInt16();
ushort bpp = file.ReadUInt16();
return new CgdMetaData {
Width = width,
Height = height,
BPP = bpp,
DataOffset = offset + 0x10 + header_size,
UnpackedSize = unpacked_size,
Compression = compression,
};
}
}
internal class KTool
{
public static void Unpack (IBinaryStream input, byte[] output, byte method)
{
switch (method)
{
case 0: input.Read (output, 0, output.Length); break;
case 1: DecompressRle (input, output, 1); break;
case 2: DecompressRle (input, output, 2); break;
case 3: DecompressRle (input, output, 3); break;
case 4: DecompressRle (input, output, 4); break;
case 0x10: DecompressHuffman (input, output); break;
default:
throw new InvalidFormatException();
}
}
internal static void DecompressRle (IBinaryStream input, byte[] output, int step)
{
for (int i = 0; i < step; ++i)
{
sbyte ctl = input.ReadInt8();
int dst = i;
while (ctl != 0)
{
if (ctl < 0)
{
int count = -ctl;
while (count --> 0)
{
output[dst] = input.ReadUInt8();
dst += step;
}
}
else
{
byte v = input.ReadUInt8();
int count = ctl;
while (count --> 0)
{
output[dst] = v;
dst += step;
}
}
ctl = input.ReadInt8();
}
}
}
internal static void DecompressHuffman (IBinaryStream input, byte[] output)
{
var decomp = new HuffmanDecoder (input);
decomp.Unpack (output);
}
struct HuffmanNode
{
public ushort Code;
public ushort LNode;
public ushort RNode;
}
class HuffmanDecoder
{
IBinaryStream m_input;
HuffmanNode[] m_tree = new HuffmanNode[514];
public HuffmanDecoder (IBinaryStream input)
{
m_input = input;
}
public void Unpack (byte[] output)
{
ReadDict();
var root = BuildTree();
int dst = 0;
int bits = 0;
byte mask = 0;
while (dst < output.Length)
{
var token = root;
while (token > 0x100)
{
if (0 == mask)
{
bits = m_input.ReadByte();
if (-1 == bits)
return;
mask = 0x80;
}
if ((bits & mask) != 0)
token = m_tree[token].RNode;
else
token = m_tree[token].LNode;
mask >>= 1;
}
output[dst++] = (byte)token;
}
}
void ReadDict ()
{
var dict = new byte[256];
DecompressRle (m_input, dict, 1);
for (int i = 0; i < 256; ++i)
{
m_tree[i].Code = dict[i];
}
m_tree[256].Code = 1;
}
ushort BuildTree ()
{
m_tree[513].Code = ushort.MaxValue;
ushort root = 257;
while (root > 0)
{
ushort rhs = 513;
ushort lhs = 513;
ushort node = 0;
for (ushort i = 0; i < root; ++i)
{
var code = m_tree[node].Code;
if (code != 0)
{
if (code < m_tree[lhs].Code)
{
rhs = lhs;
lhs = i;
}
else if (code < m_tree[rhs].Code)
{
rhs = i;
}
}
++node;
}
if (rhs == 513)
break;
m_tree[root].Code = (ushort)(m_tree[rhs].Code + m_tree[lhs].Code);
m_tree[root].LNode = lhs;
m_tree[root].RNode = rhs;
m_tree[lhs].Code = 0;
m_tree[rhs].Code = 0;
++root;
}
return (ushort)(root - 1);
}
}
}
internal class CgdDecoder : BinaryImageDecoder
{
public CgdDecoder (IBinaryStream input, CgdMetaData info) : base (input, info)
{
}
protected override ImageData GetImageData ()
{
var meta = (CgdMetaData)Info;
m_input.Position = meta.DataOffset;
var pixels = new byte[meta.UnpackedSize];
KTool.Unpack (m_input, pixels, meta.Compression);
PixelFormat format = 24 == meta.BPP ? PixelFormats.Rgb24 : PixelFormats.Bgra32;
return ImageData.Create (meta, format, null, pixels);
}
}
}

View File

@ -44,9 +44,9 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="ICSharpCode.SharpZipLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL"> <Reference Include="ICSharpCode.SharpZipLib, Version=1.0.0.999, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath> <HintPath>..\packages\SharpZipLib.1.1.0\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference> </Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="System" /> <Reference Include="System" />
@ -89,6 +89,8 @@
<Compile Include="DigitalMonkey\ArcDM.cs" /> <Compile Include="DigitalMonkey\ArcDM.cs" />
<Compile Include="DigitalMonkey\ImagePKT.cs" /> <Compile Include="DigitalMonkey\ImagePKT.cs" />
<Compile Include="Electriciteit\ArcPKK.cs" /> <Compile Include="Electriciteit\ArcPKK.cs" />
<Compile Include="KApp\ArcASD.cs" />
<Compile Include="KApp\ImageCGD.cs" />
<Compile Include="Kasane\ArcAR2.cs" /> <Compile Include="Kasane\ArcAR2.cs" />
<Compile Include="KeroQ\ArcDAT.cs" /> <Compile Include="KeroQ\ArcDAT.cs" />
<Compile Include="KeroQ\ImageCBM.cs" /> <Compile Include="KeroQ\ImageCBM.cs" />
@ -97,6 +99,8 @@
<Compile Include="Lazycrew\ArcDAT.cs" /> <Compile Include="Lazycrew\ArcDAT.cs" />
<Compile Include="Lazycrew\ImageDAT.cs" /> <Compile Include="Lazycrew\ImageDAT.cs" />
<Compile Include="Melonpan\ArcTTD.cs" /> <Compile Include="Melonpan\ArcTTD.cs" />
<Compile Include="Mermaid\AudioPWV.cs" />
<Compile Include="Mermaid\ImageGP1.cs" />
<Compile Include="Mmfass\ArcSDA.cs" /> <Compile Include="Mmfass\ArcSDA.cs" />
<Compile Include="Nyoken\ArcZLK.cs" /> <Compile Include="Nyoken\ArcZLK.cs" />
<Compile Include="PenguinWorks\ArcPAC.cs" /> <Compile Include="PenguinWorks\ArcPAC.cs" />