mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-27 07:34:00 +08:00
(GrpOpener): implemented TPW compression.
This commit is contained in:
parent
e006b898e2
commit
ecdb6d147f
@ -34,23 +34,23 @@ namespace GameRes.Formats.Ankh
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class GrpOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "GRP/ANKH"; } }
|
||||
public override string Description { get { return "Ankh resource archive"; } }
|
||||
public override string Tag { get { return "GRP/ICE"; } }
|
||||
public override string Description { get { return "Ice Soft resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public GrpOpener ()
|
||||
{
|
||||
Extensions = new string[] { "grp" };
|
||||
Extensions = new string[] { "grp", "bin" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint first_offset = file.View.ReadUInt32 (0);
|
||||
if (first_offset < 8 || first_offset >= file.MaxOffset)
|
||||
if (first_offset < 8 || first_offset >= file.MaxOffset || 0 != (first_offset & 3))
|
||||
return null;
|
||||
int count = (int)(first_offset - 8) / 4;
|
||||
int count = (int)(first_offset - 4) / 4;
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
@ -58,7 +58,7 @@ namespace GameRes.Formats.Ankh
|
||||
uint index_offset = 0;
|
||||
uint next_offset = first_offset;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
for (int i = 0; i < count && next_offset < file.MaxOffset; ++i)
|
||||
{
|
||||
var entry = new PackedEntry { Offset = next_offset };
|
||||
index_offset += 4;
|
||||
@ -77,57 +77,106 @@ namespace GameRes.Formats.Ankh
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
DetectFileTypes (file, dir);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
void DetectFileTypes (ArcView file, List<Entry> dir)
|
||||
{
|
||||
foreach (PackedEntry entry in dir)
|
||||
{
|
||||
if (entry.Size < 4)
|
||||
if (entry.Size <= 8)
|
||||
continue;
|
||||
uint unpacked_size = file.View.ReadUInt32 (entry.Offset);
|
||||
if (entry.Size > 8 && file.View.AsciiEqual (entry.Offset+4, "HDJ\0"))
|
||||
if (file.View.AsciiEqual (entry.Offset, "TPW"))
|
||||
{
|
||||
entry.IsPacked = file.View.ReadByte (entry.Offset+3) != 0;
|
||||
long start_offset = entry.Offset+4;
|
||||
if (entry.IsPacked)
|
||||
{
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (start_offset);
|
||||
start_offset = entry.Offset+11;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Offset = start_offset;
|
||||
entry.Size -= 4;
|
||||
}
|
||||
if (file.View.AsciiEqual (start_offset, "BM"))
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "bmp");
|
||||
entry.Type = "image";
|
||||
}
|
||||
}
|
||||
else if (file.View.AsciiEqual (entry.Offset+4, "HDJ\0"))
|
||||
{
|
||||
if (file.View.AsciiEqual (entry.Offset+12, "BM"))
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "bmp");
|
||||
entry.Type = "image";
|
||||
}
|
||||
entry.UnpackedSize = unpacked_size;
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (entry.Offset);
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
else if (file.View.AsciiEqual (entry.Offset+4, "OggS"))
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "ogg");
|
||||
entry.Type = "audio";
|
||||
entry.Offset += 4;
|
||||
entry.Size -= 4;
|
||||
}
|
||||
else if (entry.Size > 12 && file.View.AsciiEqual (entry.Offset+8, "RIFF"))
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "wav");
|
||||
entry.Type = "audio";
|
||||
entry.UnpackedSize = unpacked_size;
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (entry.Offset);
|
||||
entry.IsPacked = true;
|
||||
}
|
||||
else if (0x4D42 == (unpacked_size & 0xFFFF))
|
||||
else if (file.View.AsciiEqual (entry.Offset, "BM"))
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "bmp");
|
||||
entry.Type = "image";
|
||||
}
|
||||
else if (entry.Size > 0x16 && IsAudioEntry (file, entry))
|
||||
{
|
||||
entry.Type = "audio";
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
bool IsAudioEntry (ArcView file, Entry entry)
|
||||
{
|
||||
uint signature = file.View.ReadUInt32 (entry.Offset);
|
||||
if (signature != 0x010001 && signature != 0x020001)
|
||||
return false;
|
||||
int extra = file.View.ReadUInt16 (entry.Offset+0x10);
|
||||
if (extra != 0)
|
||||
return false;
|
||||
uint size = file.View.ReadUInt32 (entry.Offset+0x12);
|
||||
return 0x16 + size == entry.Size;
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Size > 8 && arc.File.View.AsciiEqual (entry.Offset+4, "HDJ\0"))
|
||||
return OpenImage (arc, entry);
|
||||
else if (entry.Size > 12 && 'W' == arc.File.View.ReadByte (entry.Offset+4)
|
||||
&& arc.File.View.AsciiEqual (entry.Offset+8, "RIFF"))
|
||||
return OpenAudio (arc, entry);
|
||||
else
|
||||
return base.OpenEntry (arc, entry);
|
||||
var pent = entry as PackedEntry;
|
||||
if (pent != null && pent.IsPacked && pent.Size > 8)
|
||||
{
|
||||
if (arc.File.View.AsciiEqual (entry.Offset, "TPW"))
|
||||
return OpenTpw (arc, pent);
|
||||
if (arc.File.View.AsciiEqual (entry.Offset+4, "HDJ\0"))
|
||||
return OpenImage (arc, pent);
|
||||
if (entry.Size > 12 && 'W' == arc.File.View.ReadByte (entry.Offset+4)
|
||||
&& arc.File.View.AsciiEqual (entry.Offset+8, "RIFF"))
|
||||
return OpenAudio (arc, entry);
|
||||
}
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
|
||||
Stream OpenImage (ArcFile arc, Entry entry)
|
||||
Stream OpenImage (ArcFile arc, PackedEntry entry)
|
||||
{
|
||||
int unpacked_size = arc.File.View.ReadInt32 (entry.Offset);
|
||||
if (unpacked_size <= 0)
|
||||
return base.OpenEntry (arc, entry);
|
||||
using (var packed = arc.File.CreateStream (entry.Offset+8, entry.Size-8))
|
||||
using (var reader = new GrpUnpacker (packed))
|
||||
{
|
||||
var unpacked = new byte[unpacked_size];
|
||||
var unpacked = new byte[entry.UnpackedSize];
|
||||
reader.UnpackHDJ (unpacked, 0);
|
||||
return new MemoryStream (unpacked);
|
||||
}
|
||||
@ -155,6 +204,81 @@ namespace GameRes.Formats.Ankh
|
||||
return new MemoryStream (unpacked);
|
||||
}
|
||||
}
|
||||
|
||||
Stream OpenTpw (ArcFile arc, PackedEntry entry)
|
||||
{
|
||||
var output = new byte[entry.UnpackedSize];
|
||||
using (var file = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
using (var input = new BinaryReader (file))
|
||||
{
|
||||
file.Position = 8;
|
||||
var offsets = new int[4];
|
||||
offsets[0] = input.ReadUInt16();
|
||||
offsets[1] = offsets[0] * 2;
|
||||
offsets[2] = offsets[0] * 3;
|
||||
offsets[3] = offsets[0] * 4;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
byte ctl = input.ReadByte();
|
||||
if (0 == ctl)
|
||||
break;
|
||||
int count;
|
||||
if (ctl < 0x40)
|
||||
{
|
||||
input.Read (output, dst, ctl);
|
||||
dst += ctl;
|
||||
}
|
||||
else if (ctl <= 0x6F)
|
||||
{
|
||||
if (0x6F == ctl)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = (ctl + 0xC3) & 0xFF;
|
||||
byte v = input.ReadByte();
|
||||
while (count --> 0)
|
||||
output[dst++] = v;
|
||||
}
|
||||
else if (ctl <= 0x9F)
|
||||
{
|
||||
if (ctl == 0x9F)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = (ctl + 0x92) & 0xFF;
|
||||
byte v1 = input.ReadByte();
|
||||
byte v2 = input.ReadByte();
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst++] = v1;
|
||||
output[dst++] = v2;
|
||||
}
|
||||
}
|
||||
else if (ctl <= 0xBF)
|
||||
{
|
||||
if (ctl == 0xBF)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = ((ctl + 0x62) & 0xFF);
|
||||
input.Read (output, dst, 3);
|
||||
if (count > 0)
|
||||
{
|
||||
count *= 3;
|
||||
Binary.CopyOverlapped (output, dst, dst+3, count-3);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (ctl & 0x3F) + 3;
|
||||
int offset = input.ReadByte();
|
||||
offset = (offset & 0x3F) - offsets[offset >> 6];
|
||||
Binary.CopyOverlapped (output, dst+offset, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
return new MemoryStream (output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GrpUnpacker : IDisposable
|
||||
|
74
ArcFormats/Ankh/AudioPCM.cs
Normal file
74
ArcFormats/Ankh/AudioPCM.cs
Normal file
@ -0,0 +1,74 @@
|
||||
//! \file AudioPCM.cs
|
||||
//! \date Mon Oct 03 22:21:15 2016
|
||||
//! \brief Ice Soft PCM audio file.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Ice
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class IceAudio : AudioFormat
|
||||
{
|
||||
public override string Tag { get { return "PCM/ICE"; } }
|
||||
public override string Description { get { return "Ice Soft PCM audio"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public IceAudio ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
Signatures = new uint[] { 0x010001, 0x020001 };
|
||||
}
|
||||
|
||||
public override SoundInput TryOpen (Stream file)
|
||||
{
|
||||
var header = new byte[0x16];
|
||||
if (header.Length != file.Read (header, 0, header.Length))
|
||||
return null;
|
||||
ushort tag = LittleEndian.ToUInt16 (header, 0);
|
||||
if (tag != 1)
|
||||
return null;
|
||||
int extra_size = LittleEndian.ToUInt16 (header, 0x10);
|
||||
if (0 != extra_size)
|
||||
return null;
|
||||
uint pcm_size = LittleEndian.ToUInt32 (header, 0x12);
|
||||
if (pcm_size + 0x16 != file.Length)
|
||||
return null;
|
||||
var format = new WaveFormat { FormatTag = tag };
|
||||
format.Channels = LittleEndian.ToUInt16 (header, 2);
|
||||
if (format.Channels != 1 && format.Channels != 2)
|
||||
return null;
|
||||
format.SamplesPerSecond = LittleEndian.ToUInt32 (header, 4);
|
||||
format.AverageBytesPerSecond = LittleEndian.ToUInt32 (header, 8);
|
||||
format.BlockAlign = LittleEndian.ToUInt16 (header, 0xC);
|
||||
format.BitsPerSample = LittleEndian.ToUInt16 (header, 0xE);
|
||||
if (0 == format.AverageBytesPerSecond
|
||||
|| format.SamplesPerSecond * format.BlockAlign != format.AverageBytesPerSecond)
|
||||
return null;
|
||||
var pcm = new StreamRegion (file, 0x16, pcm_size);
|
||||
return new RawPcmInput (pcm, format);
|
||||
}
|
||||
}
|
||||
}
|
@ -179,6 +179,7 @@ Binary Pot<br/>
|
||||
Chou Gedou Yuusha<br/>
|
||||
Shinshoku ~Inma no Ikenie~<br/>
|
||||
Shinshoku 2 ~Inma no Ikenie~<br/>
|
||||
Shokusou Tenshi Serika<br/>
|
||||
Tsukihime<br/>
|
||||
Umineko<br/>
|
||||
</td></tr>
|
||||
@ -265,6 +266,7 @@ Kurenai no Tsuki<br/>
|
||||
LOVELY x CATION<br/>
|
||||
Mayoeru Futari to Sekai no Subete<br/>
|
||||
Mahoutsukai no Yoru<br/>
|
||||
Mizukoi<br/>
|
||||
Nakadashi Hara Maid series<br/>
|
||||
Natsupochi<br/>
|
||||
Natsuzora Kanata<br/>
|
||||
@ -411,6 +413,7 @@ Chikan Circle 2 <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Chuuchuu Nurse <span class="footnote">ShiinaRio v2.45</span><br/>
|
||||
Classmate no Okaa-san <span class="footnote">ShiinaRio v2.37</span><br/>
|
||||
Cleavage <span class="footnote">ShiinaRio v2.33</span><br/>
|
||||
Danchi Wife ~Hitozuma Nude Model no Kenshin~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Doushite Daite Kurenai no!?<span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
Draculius <span class="footnote">ShiinaRio v2.38</span><br/>
|
||||
Enkaku Sousa <span class="footnote">2.36 or 2.37</span><br/>
|
||||
@ -832,6 +835,7 @@ Trouble Trap Laboratory<br/>
|
||||
<tr class="odd last"><td>*.ngp</td><td><tt>NGP</tt></td><td>No</td></tr>
|
||||
<tr><td>*.hxp</td><td><tt>Him4</tt><br/><tt>Him5</tt><br/><tt>SHS6</tt><br/><tt>SHS7</tt></td><td>No</td><td>SH System</td><td>
|
||||
Natsumero<br/>
|
||||
Sumeragi Ryouko no Bitch na 1 nichi<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.det<br/>+*.nme<br/>+*.atm</td><td>-</td><td>No</td><td rowspan="2">μ-GameOperationSystem</td><td rowspan="2">
|
||||
Ippai Shimasho<br/>
|
||||
@ -981,7 +985,8 @@ Hitomi no Rakuin ~Inbaku no Mesu Dorei~<br/>
|
||||
Jokujima<br/>
|
||||
Saiin Haramase Keyword<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.grp</td><td>-</td><td>No</td><td>Ankh</td><td>
|
||||
<tr class="odd"><td>*.grp<br/>*.bin</td><td>-<br/><tt>TPW</tt></td><td>No</td><td>Tirol<br/>Ankh</td><td>
|
||||
Aigan Shoujo<br/>
|
||||
Mozu<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.gpc</td><td><tt>Gpc7</tt></td><td>No</td><td>Super NekoX</td><td>
|
||||
@ -1105,6 +1110,7 @@ Soushinjutsu 2<br/>
|
||||
Rakuin Hime Runed Princess<br/>
|
||||
Shinshoku -Zero- ~Inma no Ikenie~<br/>
|
||||
Shinshoku 3 ~Inma no Ikenie~<br/>
|
||||
Shokusou Tenshi Serika 2<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.dat</td><td>-</td><td>No</td><td>NekoSDK</td><td>
|
||||
Elevator Panic ~Misshitsu no Inkou~<br/>
|
||||
|
Loading…
Reference in New Issue
Block a user