implemented CGF and IAF image sets.

This commit is contained in:
morkt 2016-02-04 05:58:47 +04:00
parent 0fe5d01ed7
commit f502e9578c
5 changed files with 243 additions and 63 deletions

View File

@ -230,6 +230,8 @@
<Compile Include="Tmr-Hiro\ImageGRD.cs" /> <Compile Include="Tmr-Hiro\ImageGRD.cs" />
<Compile Include="TopCat\ArcTCD3.cs" /> <Compile Include="TopCat\ArcTCD3.cs" />
<Compile Include="TopCat\ImageSPD.cs" /> <Compile Include="TopCat\ImageSPD.cs" />
<Compile Include="Triangle\ArcCGF.cs" />
<Compile Include="Triangle\ArcIAF.cs" />
<Compile Include="uGOS\ArcDET.cs" /> <Compile Include="uGOS\ArcDET.cs" />
<Compile Include="uGOS\ImageBMP.cs" /> <Compile Include="uGOS\ImageBMP.cs" />
<Compile Include="UMeSoft\ArcPK.cs" /> <Compile Include="UMeSoft\ArcPK.cs" />

View File

@ -0,0 +1,119 @@
//! \file ArcCGF.cs
//! \date Tue Feb 02 00:47:55 2016
//! \brief route2 engine CG 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;
using GameRes.Compression;
using GameRes.Utility;
namespace GameRes.Formats.Triangle
{
internal class CgfEntry : Entry
{
public uint Flags;
}
[Export(typeof(ArchiveFormat))]
public class CgfOpener : ArchiveFormat
{
public override string Tag { get { return "CGF"; } }
public override string Description { get { return "route2 engine CG archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanCreate { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (0);
if (!IsSaneCount (count) || file.MaxOffset >= ~0xC0000000)
return null;
uint offset1 = file.View.ReadUInt32 (0x14);
uint offset2 = file.View.ReadUInt32 (0x20);
uint entry_size, next_offset;
if (4+(uint)count*0x14 == (offset1 & ~0xC0000000))
{
entry_size = 0x14;
next_offset = offset1;
}
else if (4+(uint)count*0x20 == (offset2 & ~0xC0000000))
{
entry_size = 0x20;
next_offset = offset2;
}
else
return null;
uint index_size = entry_size * (uint)count;
if (index_size > file.View.Reserve (4, index_size))
return null;
uint index_offset = 4;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset, entry_size-4);
uint flags = next_offset >> 30;
Entry entry;
if (1 == flags || name.EndsWith (".iaf", StringComparison.InvariantCultureIgnoreCase))
entry = new Entry();
else
entry = new CgfEntry { Flags = flags };
entry.Name = name;
entry.Type = "image";
entry.Offset = next_offset & ~0xC0000000;
index_offset += entry_size;
next_offset = i+1 == count ? (uint)file.MaxOffset : file.View.ReadUInt32 (index_offset+entry_size-4);
if (next_offset < entry.Offset)
return null;
entry.Size = (next_offset & ~0xC0000000) - (uint)entry.Offset;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var cent = entry as CgfEntry;
if (null == cent)
return base.OpenEntry (arc, entry);
var offset = entry.Offset;
var header = new byte[12];
if (2 == cent.Flags)
{
arc.File.View.Read (offset, header, 0, 8);
offset += 0x10;
}
uint packed_size = arc.File.View.ReadUInt32 (offset);
arc.File.View.Read (offset+4, header, 8, 4);
var input = arc.File.CreateStream (offset+8, packed_size);
return new PrefixStream (header, input);
}
}
}

View File

@ -0,0 +1,76 @@
//! \file ArcIAF.cs
//! \date Thu Feb 04 03:18:26 2016
//! \brief route2 engine multi-frame image.
//
// 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.Triangle
{
[Export(typeof(ArchiveFormat))]
public class IafOpener : ArchiveFormat
{
public override string Tag { get { return "IAF/MULTI"; } }
public override string Description { get { return "route2 engine multi-frame image"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanCreate { get { return false; } }
public IafOpener ()
{
Extensions = new string[] { "iaf" };
}
public override ArcFile TryOpen (ArcView file)
{
if (!file.Name.EndsWith (".iaf", StringComparison.InvariantCultureIgnoreCase)
|| file.MaxOffset < 0x20)
return null;
uint size = file.View.ReadUInt32 (1);
if (size >= file.MaxOffset || size+0x19 >= file.MaxOffset)
return null;
var base_name = Path.GetFileNameWithoutExtension (file.Name);
long current_offset = 0;
var dir = new List<Entry>();
while (current_offset < file.MaxOffset)
{
uint packed_size = file.View.ReadUInt32 (current_offset+1);
if (0 == packed_size || current_offset+packed_size+0x19 > file.MaxOffset)
return null;
var entry = new Entry {
Name = string.Format ("{0}#{1:D3}.IAF", base_name, dir.Count),
Type = "image",
Offset = current_offset,
Size = packed_size + 0x19,
};
dir.Add (entry);
current_offset += entry.Size;
}
return new ArcFile (file, this, dir);
}
}
}

View File

@ -76,46 +76,20 @@ namespace GameRes.Formats.Triangle
if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096) if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096)
return null; return null;
int pack_type = (unpacked_size >> 30) & 3; int pack_type = (unpacked_size >> 30) & 3;
if (3 == pack_type)
return null;
unpacked_size &= (int)~0xC0000000; unpacked_size &= (int)~0xC0000000;
stream.Position = data_offset; stream.Position = data_offset;
byte[] bmp; byte[] bmp = UnpackBitmap (stream, pack_type, packed_size, 0x26);
if (0 == pack_type)
{
using (var reader = new LzssReader (stream, (int)stream.Length-12, 0x26))
{
reader.Unpack();
bmp = reader.Data;
}
}
else if (2 == pack_type)
{
using (var reader = new RleReader (stream, (int)stream.Length-12, 0x26))
{
reader.Unpack();
bmp = reader.Data;
}
}
else if (1 == pack_type)
{
bmp = new byte[0x26];
stream.Read (bmp, 0, bmp.Length);
}
else
{
return null;
}
if (bmp[0] != 'B' && bmp[0] != 'C' || bmp[1] != 'M') if (bmp[0] != 'B' && bmp[0] != 'C' || bmp[1] != 'M')
return null; return null;
int width = LittleEndian.ToInt32 (bmp, 0x12);
int height = LittleEndian.ToInt32 (bmp, 0x16);
int bpp = LittleEndian.ToInt16 (bmp, 0x1c);
return new IafMetaData return new IafMetaData
{ {
Width = (uint)width, Width = LittleEndian.ToUInt32 (bmp, 0x12),
Height = (uint)height, Height = LittleEndian.ToUInt32 (bmp, 0x16),
OffsetX = x, OffsetX = x,
OffsetY = y, OffsetY = y,
BPP = bpp, BPP = LittleEndian.ToInt16 (bmp, 0x1c),
DataOffset = data_offset, DataOffset = data_offset,
PackedSize = packed_size, PackedSize = packed_size,
UnpackedSize = unpacked_size, UnpackedSize = unpacked_size,
@ -125,41 +99,16 @@ namespace GameRes.Formats.Triangle
public override ImageData Read (Stream stream, ImageMetaData info) public override ImageData Read (Stream stream, ImageMetaData info)
{ {
var meta = info as IafMetaData; var meta = (IafMetaData)info;
if (null == meta)
throw new ArgumentException ("IafFormat.Read should be supplied with IafMetaData", "info");
stream.Position = meta.DataOffset; stream.Position = meta.DataOffset;
byte[] bitmap; var bitmap = UnpackBitmap (stream, meta.PackType, meta.PackedSize, meta.UnpackedSize);
if (2 == meta.PackType)
{
using (var reader = new RleReader (stream, meta.PackedSize, meta.UnpackedSize))
{
reader.Unpack();
bitmap = reader.Data;
}
}
else if (0 == meta.PackType)
{
using (var reader = new LzssReader (stream, meta.PackedSize, meta.UnpackedSize))
{
reader.Unpack();
bitmap = reader.Data;
}
}
else
{
bitmap = new byte[meta.UnpackedSize];
if (bitmap.Length != stream.Read (bitmap, 0, bitmap.Length))
throw new InvalidFormatException ("Unexpected end of file");
}
if ('C' == bitmap[0]) if ('C' == bitmap[0])
{ {
bitmap[0] = (byte)'B'; bitmap[0] = (byte)'B';
if (info.BPP > 8) if (info.BPP > 8)
bitmap = ConvertCM (bitmap, (int)info.Width, (int)info.Height, info.BPP); bitmap = ConvertCM (bitmap, (int)info.Width, (int)info.Height, info.BPP);
} }
if (meta.BPP >= 24) // currently alpha channel could be applied to 24+bpp bitmaps only if (info.BPP >= 24) // currently alpha channel could be applied to 24+bpp bitmaps only
{ {
try try
{ {
@ -172,7 +121,7 @@ namespace GameRes.Formats.Triangle
uint alpha_width = LittleEndian.ToUInt32 (bitmap, bmp_size+0x12); uint alpha_width = LittleEndian.ToUInt32 (bitmap, bmp_size+0x12);
uint alpha_height = LittleEndian.ToUInt32 (bitmap, bmp_size+0x16); uint alpha_height = LittleEndian.ToUInt32 (bitmap, bmp_size+0x16);
if (info.Width == alpha_width && info.Height == alpha_height) if (info.Width == alpha_width && info.Height == alpha_height)
return BitmapWithAlphaChannel (meta, bitmap, bmp_size); return BitmapWithAlphaChannel (info, bitmap, bmp_size);
} }
} }
} }
@ -186,6 +135,35 @@ namespace GameRes.Formats.Triangle
return base.Read (bmp, info); return base.Read (bmp, info);
} }
internal static byte[] UnpackBitmap (Stream stream, int pack_type, int packed_size, int unpacked_size)
{
if (2 == pack_type)
{
using (var reader = new RleReader (stream, packed_size, unpacked_size))
{
reader.Unpack();
return reader.Data;
}
}
else if (0 == pack_type)
{
using (var reader = new LzssReader (stream, packed_size, unpacked_size))
{
reader.Unpack();
return reader.Data;
}
}
else if (1 == pack_type)
{
var bitmap = new byte[unpacked_size];
if (bitmap.Length != stream.Read (bitmap, 0, bitmap.Length))
throw new InvalidFormatException ("Unexpected end of file");
return bitmap;
}
else
throw new InvalidFormatException();
}
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
{ {
throw new System.NotImplementedException ("IafFormat.Write not implemented"); throw new System.NotImplementedException ("IafFormat.Write not implemented");

View File

@ -122,9 +122,10 @@ Sweet Pool<br/>
Zoku Satsuriku no Django<br/> Zoku Satsuriku no Django<br/>
</td></tr> </td></tr>
<tr class="odd"><td>*.npa</td><td>-</td><td>Yes</td><td>Nitro+</td><td>Steins;Gate</td></tr> <tr class="odd"><td>*.npa</td><td>-</td><td>Yes</td><td>Nitro+</td><td>Steins;Gate</td></tr>
<tr><td>*.pak</td><td><tt>\002\000\000\000</tt><br/><tt>\003\000\000\000</tt></td><td>No</td><td>Nitro+</td><td> <tr><td>*.pak</td><td><tt>\002\000\000\000</tt><br/><tt>\003\000\000\000</tt></td><td>No</td><td>Nitro+<br/>CoreMoreco</td><td>
Hanachirasu<br/> Hanachirasu<br/>
Jingai Makyou<br/> Jingai Makyou<br/>
Chibi Mama<br/>
</td></tr> </td></tr>
<tr class="odd"><td>*.nsa<br/>*.sar</td><td>-</td><td>Yes<a href="#note-1" class="footnote">1</a></td><td>NScripter</td><td> <tr class="odd"><td>*.nsa<br/>*.sar</td><td>-</td><td>Yes<a href="#note-1" class="footnote">1</a></td><td>NScripter</td><td>
Binary Pot<br/> Binary Pot<br/>
@ -452,11 +453,13 @@ Gokudou no Hanayome<br/>
Knight Carnival!<br/> Knight Carnival!<br/>
Seal Princess<br/> Seal Princess<br/>
</td></tr> </td></tr>
<tr><td>*.sud</td><td>-</td><td>No</td><td rowspan="2">Triangle</td><td rowspan="2"> <tr><td>*.sud</td><td>-</td><td>No</td><td rowspan="3">Triangle</td><td rowspan="3">
Kourin Tenshi En Ciel Rena<br/> Kourin Tenshi En Ciel Rena<br/>
Mamagoto<br/> Mamagoto<br/>
Vampire Crusaders<br/>
</td></tr> </td></tr>
<tr><td>*.iaf</td><td>-</td><td>No</td></tr> <tr><td>*.iaf</td><td>-</td><td>No</td></tr>
<tr><td>*.cgf</td><td>-</td><td>No</td></tr>
<tr class="odd"><td>*.bin+*.pak</td><td><tt>hed</tt></td><td>No</td><td rowspan="2">elf</td><td rowspan="2">Adult Video King</td></tr> <tr class="odd"><td>*.bin+*.pak</td><td><tt>hed</tt></td><td>No</td><td rowspan="2">elf</td><td rowspan="2">Adult Video King</td></tr>
<tr class="odd"><td>*.hip<br/>*.hiz<br/></td><td><tt>hip</tt><br/><tt>hiz</tt></td><td>No</td></tr> <tr class="odd"><td>*.hip<br/>*.hiz<br/></td><td><tt>hip</tt><br/><tt>hiz</tt></td><td>No</td></tr>
<tr><td>*.gpk+*.gtb<br/>*.vpk+*.vtb</td><td>-</td><td>No</td><td rowspan="3">Black Cyc</td><td rowspan="3"> <tr><td>*.gpk+*.gtb<br/>*.vpk+*.vtb</td><td>-</td><td>No</td><td rowspan="3">Black Cyc</td><td rowspan="3">
@ -612,6 +615,7 @@ AstralAir no Shiroki Towa<br/>
<tr><td>*.hzc</td><td><tt>hzc1</tt></td><td>No</td></tr> <tr><td>*.hzc</td><td><tt>hzc1</tt></td><td>No</td></tr>
<tr class="odd"><td>*.bin</td><td><tt>ESC-ARC1</tt><br/><tt>ESC-ARC2</tt></td><td>No</td><td>Escu:de</td><td> <tr class="odd"><td>*.bin</td><td><tt>ESC-ARC1</tt><br/><tt>ESC-ARC2</tt></td><td>No</td><td>Escu:de</td><td>
Otome Renshin Prister<br/> Otome Renshin Prister<br/>
Suisei Tenshi Primaveil Zwei<br/>
Wondering Repair!<br/> Wondering Repair!<br/>
</td></tr> </td></tr>
<tr><td>*.pac</td><td>-</td><td>No</td><td>Tmr-Hiro ADV System</td><td> <tr><td>*.pac</td><td>-</td><td>No</td><td>Tmr-Hiro ADV System</td><td>
@ -637,6 +641,7 @@ Ayakashi H<br/>
</td></tr> </td></tr>
<tr class="odd"><td>*.arc</td><td><tt>LINK3</tt></td><td>No</td><td rowspan="3">KaGuYa</td><td rowspan="3"> <tr class="odd"><td>*.arc</td><td><tt>LINK3</tt></td><td>No</td><td rowspan="3">KaGuYa</td><td rowspan="3">
Dokidoki Onee-san<br/> Dokidoki Onee-san<br/>
Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~<br/>
</td></tr> </td></tr>
<tr class="odd"><td>*.alp</td><td><tt>AP-2</tt></td><td>No</td></tr> <tr class="odd"><td>*.alp</td><td><tt>AP-2</tt></td><td>No</td></tr>
<tr class="odd"><td>*.anm</td><td><tt>AN00</tt></td><td>No</td></tr> <tr class="odd"><td>*.anm</td><td><tt>AN00</tt></td><td>No</td></tr>