implemented DSK archives.

This commit is contained in:
morkt 2016-10-29 02:01:31 +04:00
parent 02e1ee11dd
commit 60827a2a5f
5 changed files with 556 additions and 0 deletions

View File

@ -0,0 +1,92 @@
//! \file ArcDSK.cs
//! \date Tue Oct 18 20:19:49 2016
//! \brief AbogadoPowers resource archive.
//
// Copyright (C) 2016 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.Abogado
{
[Export(typeof(ArchiveFormat))]
public class DskOpener : ArchiveFormat
{
public override string Tag { get { return "DSK/PFT"; } }
public override string Description { get { return "AbogadoPowers resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
static readonly IDictionary<string, string> ExtensionMap = new Dictionary<string, string> (StringComparer.InvariantCultureIgnoreCase)
{
{ "BACK", "KG" },
{ "BUST", "KG" },
{ "EVENT", "KG" },
{ "SYSTEM", "KG" },
{ "VISUAL", "KG" },
{ "SETTEI", "KG" },
{ "SCENE", "SCF" },
{ "SOUND", "ADP" },
{ "PCM1", "ADP" },
{ "PCM2", "ADP" },
};
public override ArcFile TryOpen (ArcView file)
{
var pft_name = Path.ChangeExtension (file.Name, "pft");
if (file.Name.Equals (pft_name, StringComparison.InvariantCultureIgnoreCase)
|| !VFS.FileExists (pft_name))
return null;
using (var pft_view = VFS.OpenView (pft_name))
using (var pft = pft_view.CreateStream())
{
var arc_name = Path.GetFileNameWithoutExtension (file.Name);
string ext = "";
ExtensionMap.TryGetValue (arc_name, out ext);
uint header_size = pft.ReadUInt16();
uint cluster_size = pft.ReadUInt16();
int count = pft.ReadInt32();
if (!IsSaneCount (count))
return null;
pft.Position = header_size;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = pft.ReadCString (8);
if (!string.IsNullOrEmpty (ext))
name = Path.ChangeExtension (name, ext);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = cluster_size * (long)pft.ReadUInt32();
entry.Size = pft.ReadUInt32();
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
return new ArcFile (file, this, dir);
}
}
}
}

View File

@ -0,0 +1,134 @@
//! \file AudioADP.cs
//! \date Thu Oct 20 09:29:26 2016
//! \brief AbogadoPowers audio format.
//
// Copyright (C) 2016 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 GameRes.Utility;
namespace GameRes.Formats.Abogado
{
[Export(typeof(AudioFormat))]
public class AdpAudio : AudioFormat
{
public override string Tag { get { return "ADP"; } }
public override string Description { get { return "AbogadoPowers audio format"; } }
public override uint Signature { get { return 0; } }
public override bool CanWrite { get { return false; } }
public AdpAudio ()
{
Signatures = new uint[] { 0x5622, 0xAC44, 0 };
}
public override SoundInput TryOpen (IBinaryStream file)
{
var header = file.ReadHeader (0xC4);
uint sample_rate = header.ToUInt32 (0);
if (sample_rate < 8000 || sample_rate > 96000)
return null;
ushort channels = header.ToUInt16 (4);
if (channels != 1 && channels != 2)
return null;
int samples = header.ToInt32 (0xBC) * channels;
int start_pos = header.ToInt32 (0xC0);
if (samples <= 0 || start_pos >= file.Length)
return null;
var output = new byte[2 * samples];
var first = new AdpDecoder();
var second = first;
if (channels > 1)
second = new AdpDecoder();
file.Position = start_pos;
int dst = 0;
while (samples > 0)
{
byte v = file.ReadUInt8();
LittleEndian.Pack (first.DecodeSample (v >> 4), output, dst);
if (0 == --samples)
break;
dst += 2;
LittleEndian.Pack (second.DecodeSample (v), output, dst);
dst += 2;
--samples;
}
var format = new WaveFormat();
format.FormatTag = 1;
format.Channels = channels;
format.SamplesPerSecond = sample_rate;
format.AverageBytesPerSecond = 2u * channels * sample_rate;
format.BlockAlign = (ushort)(2 * channels);
format.BitsPerSample = 0x10;
var pcm = new MemoryStream (output);
var sound = new RawPcmInput (pcm, format);
file.Dispose();
return sound;
}
}
internal class AdpDecoder
{
int prev_sample = 0;
int quant_idx = 0;
public short DecodeSample (int sample)
{
sample &= 0xF;
var quant = QuantizeTable[quant_idx];
quant_idx += IncrementTable[sample];
if (quant_idx < 0)
quant_idx = 0;
else if (quant_idx > 0x58)
quant_idx = 0x58;
if (sample < 8)
{
sample = Math.Min (0x7FFF, prev_sample + (((2 * sample + 1) * quant) >> 3));
}
else
{
sample = Math.Max (-32768, prev_sample - ((2 * (sample & 7) + 1) * quant >> 3));
}
prev_sample = sample;
return (short)sample;
}
static readonly ushort[] QuantizeTable = {
0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F,
0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042,
0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F,
0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133,
0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292,
0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583,
0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0,
0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954,
0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B,
0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF,
};
static int[] IncrementTable = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 };
}
}

View File

@ -0,0 +1,319 @@
//! \file ImageKG.cs
//! \date Wed Oct 19 15:51:18 2016
//! \brief AbogadoPowers image format.
//
// Copyright (C) 2016 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;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes.Formats.Abogado
{
internal class KgMetaData : ImageMetaData
{
public int PaletteOffset;
public int DataOffset;
public int AlphaOffset;
}
[Export(typeof(ImageFormat))]
public class KgFormat : ImageFormat
{
public override string Tag { get { return "KG/ABOGADO"; } }
public override string Description { get { return "AbogadoPowers image format"; } }
public override uint Signature { get { return 0; } }
public KgFormat ()
{
Signatures = new uint[] { 0x0202474B, 0x0102474B };
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x30);
return new KgMetaData
{
Width = header.ToUInt16 (4),
Height = header.ToUInt16 (6),
BPP = header[3] == 2 ? 24 : 8,
PaletteOffset = header.ToInt32 (0xC),
DataOffset = header.ToInt32 (0x10),
AlphaOffset = header.ToInt32 (0x2C),
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
using (var reader = new KgReader (file, (KgMetaData)info))
{
reader.Unpack();
return ImageData.CreateFlipped (info, reader.Format, reader.Palette, reader.Pixels, reader.Stride);
}
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("KgFormat.Write not implemented");
}
}
internal sealed class KgReader : IDisposable
{
IBinaryStream m_input;
MsbBitStream m_bits;
KgMetaData m_info;
byte[] m_output;
int m_pixel_size;
int m_stride;
public PixelFormat Format { get; private set; }
public BitmapPalette Palette { get; private set; }
public byte[] Pixels { get { return m_output; } }
public int Stride { get { return m_stride; } }
public KgReader (IBinaryStream input, KgMetaData info)
{
m_input = input;
m_bits = new MsbBitStream (input.AsStream, true);
m_info = info;
m_pixel_size = m_info.BPP / 8;
m_stride = m_pixel_size * (int)m_info.Width;
m_output = new byte[m_stride * (int)m_info.Height];
if (0 != m_info.AlphaOffset)
Format = PixelFormats.Bgra32;
else if (24 == m_info.BPP)
Format = PixelFormats.Bgr24;
else
Format = PixelFormats.Indexed8;
}
public void Unpack ()
{
if (8 == m_info.BPP)
{
m_input.Position = m_info.PaletteOffset;
ReadPalette();
}
m_bits.Input.Position = m_info.DataOffset;
ResetDict();
UnpackChannel (0);
if (m_pixel_size > 1)
{
UnpackChannel (1);
UnpackChannel (2);
}
if (m_info.AlphaOffset != 0)
{
ConvertToBgr32();
try
{
m_bits.Input.Position = m_info.AlphaOffset;
m_bits.Reset();
ResetDict();
UnpackChannel (3);
}
catch
{
Format = PixelFormats.Bgr32;
}
}
}
byte[] m_dict = new byte[0x800];
void ResetDict ()
{
for (int i = 0; i < 0x800; ++i)
m_dict[i] = (byte)(i & 7);
}
void ConvertToBgr32 ()
{
m_stride = (int)m_info.Width * 4;
var pixels = new byte[m_stride * (int)m_info.Height];
int dst = 0;
if (1 == m_pixel_size)
{
var colors = Palette.Colors;
for (int src = 0; src < m_output.Length; ++src)
{
var pixel = colors[m_output[src]];
pixels[dst] = pixel.B;
pixels[dst+1] = pixel.G;
pixels[dst+2] = pixel.R;
dst += 4;
}
}
else
{
for (int src = 0; src < m_output.Length; src += m_pixel_size)
{
pixels[dst] = m_output[src];
pixels[dst+1] = m_output[src+1];
pixels[dst+2] = m_output[src+2];
dst += 4;
}
}
m_output = pixels;
m_pixel_size = 4;
}
void ReadPalette ()
{
var palette_data = m_input.ReadBytes (0x400);
if (palette_data.Length != 0x400)
throw new EndOfStreamException();
var palette = new Color[0x100];
for (int i = 0; i < 0x100; ++i)
{
int c = i * 4;
palette[i] = Color.FromRgb (palette_data[c+2], palette_data[c+1], palette_data[c]);
}
Palette = new BitmapPalette (palette);
}
void UnpackChannel (int dst)
{
m_output[dst] = (byte)m_bits.GetBits (8);
dst += m_pixel_size;
m_output[dst] = (byte)m_bits.GetBits (8);
dst += m_pixel_size;
while (dst < m_output.Length)
{
int ctl = m_bits.GetBits (1);
if (-1 == ctl)
throw new EndOfStreamException();
if (0 == ctl)
{
byte b = GetPixel (dst);
m_output[dst] = b;
UpdateDict (b, m_output[dst - m_pixel_size]);
dst += m_pixel_size;
continue;
}
if (0 != m_bits.GetBits (1))
ctl = m_bits.GetBits (2);
else
ctl = 4;
int offset;
switch (ctl)
{
case 0:
offset = m_stride;
break;
case 1:
offset = m_stride - m_pixel_size;
break;
case 2:
offset = m_stride + m_pixel_size;
break;
case 3:
offset = 2 * m_pixel_size;
break;
default:
offset = m_pixel_size;
break;
}
int count = GetCount();
int src = dst - offset;
for (int i = 0; i < count; ++i)
{
m_output[dst] = m_output[src];
dst += m_pixel_size;
src += m_pixel_size;
}
}
}
byte GetPixel (int dst)
{
if (1 == m_bits.GetBits (1))
{
return (byte)m_bits.GetBits (8);
}
else
{
int n = 8 * m_output[dst - m_pixel_size];
return m_dict[n + m_bits.GetBits (3)];
}
}
void UpdateDict (byte b, byte prev)
{
int s = 8 * prev;
int i;
for (i = 0; i < 8; ++i)
{
if (m_dict[s + i] == b)
break;
}
if (i != 0)
{
if (8 == i)
i = 7;
Buffer.BlockCopy (m_dict, s, m_dict, s+1, i);
m_dict[s] = b;
}
}
int GetCount ()
{
int count = m_bits.GetBits (2);
if (0 == count)
{
count = m_bits.GetBits (4);
if (0 != count)
{
count += 3;
}
else
{
count = m_bits.GetBits (8);
if (0 == count)
{
count = m_bits.GetBits (16);
if (0 == count)
{
count = m_bits.GetBits (16) << 16;
count |= m_bits.GetBits (16);
}
}
}
}
return count;
}
bool _disposed = false;
public void Dispose ()
{
if (!_disposed)
{
m_bits.Dispose();
_disposed = true;
}
}
}
}

View File

@ -72,6 +72,9 @@
<ItemGroup>
<Compile Include="Abel\ArcARC.cs" />
<Compile Include="Abel\ImageGPS.cs" />
<Compile Include="Abogado\ArcDSK.cs" />
<Compile Include="Abogado\AudioADP.cs" />
<Compile Include="Abogado\ImageKG.cs" />
<Compile Include="Actgs\ArcDAT.cs" />
<Compile Include="AdvSys\ArcAdvSys3.cs" />
<Compile Include="AdvSys\ImageGWD.cs" />

View File

@ -463,6 +463,7 @@ Zansho Omimai Moushiagemasu<span class="footnote">ShiinaRio v2.41</span><br/>
<tr class="odd"><td>*</td><td><tt>ARC2</tt><br/><tt>ARC1</tt></td><td>No</td><td>AST</td><td>
Bokura wa Piacere<br/>
Gakuen Tengoku ~Kyonyuu de Ikimakuri~<br/>
Gokujou Chikan Densha<br/>
Hokenshitsu no Sensei ~Onedari Bakunyuu Lesson~<br/>
Jokyoushi wo Kurau<br/>
Lovers Collection<br/>
@ -731,6 +732,7 @@ Houou Senki Maimu<br/>
Kango Sentai Nurse Ranger<br/>
Megami Taisen<br/>
Shouryuu Senki Tenmu<br/>
Wizard Girl Ambitious<br/>
</td></tr>
<tr class="odd last"><td>*.abm</td><td><tt>BM</tt></td><td>No</td></tr>
<tr><td>*.arc<br/>*.xarc<br/>*.bin</td><td><tt>MIKO</tt><br/><tt>KOTORI</tt><br/><tt>XARC</tt></td><td>No</td><td rowspan="2">Xuse<br/>ETERNAL</td><td rowspan="2">
@ -1047,6 +1049,7 @@ Tsujidou-san no Jun'ai Road<br/>
Tsujidou-san no Virgin Road<br/>
</td></tr>
<tr><td>*.pak</td><td><tt>PACK</tt></td><td>No</td><td>Studio Nekopunch</td><td>
Colorfull!!<br/>
Uso x Mote ~Usonko Motemote-shon~<br/>
</td></tr>
<tr class="odd"><td>*.syg</td><td><tt>$SYG</tt></td><td>No</td><td><a title="Risa game platform system">Risa</a></td><td>
@ -1226,6 +1229,11 @@ Tain Shoukougun<br/>
<tr><td>*.dat</td><td><tt>tskforce</tt></td><td>No</td><td>Taskforce</td><td>
Musumaker<br/>
</td></tr>
<tr class="odd"><td>*.dsk+*.pft</td><td>-</td><td>No</td><td rowspan="3">Abogado Powers</td><td rowspan="3">
Pigeon Blood<br/>
</td></tr>
<tr class="odd"><td>*.kg</td><td><tt>KG</tt></td><td>No</td></tr>
<tr class="odd last"><td>*.adp</td><td>-</td><td>No</td></tr>
</table>
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
</body>