From f502e9578c37e4248ab7a9a319a38a501d0c4823 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 4 Feb 2016 05:58:47 +0400 Subject: [PATCH] implemented CGF and IAF image sets. --- ArcFormats/ArcFormats.csproj | 2 + ArcFormats/Triangle/ArcCGF.cs | 119 ++++++++++++++++++++++++++++++++ ArcFormats/Triangle/ArcIAF.cs | 76 ++++++++++++++++++++ ArcFormats/Triangle/ImageIAF.cs | 100 +++++++++++---------------- supported.html | 9 ++- 5 files changed, 243 insertions(+), 63 deletions(-) create mode 100644 ArcFormats/Triangle/ArcCGF.cs create mode 100644 ArcFormats/Triangle/ArcIAF.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index e04e7c26..f8d2c8aa 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -230,6 +230,8 @@ + + diff --git a/ArcFormats/Triangle/ArcCGF.cs b/ArcFormats/Triangle/ArcCGF.cs new file mode 100644 index 00000000..b6b497fb --- /dev/null +++ b/ArcFormats/Triangle/ArcCGF.cs @@ -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 (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); + } + } +} diff --git a/ArcFormats/Triangle/ArcIAF.cs b/ArcFormats/Triangle/ArcIAF.cs new file mode 100644 index 00000000..c8926bf5 --- /dev/null +++ b/ArcFormats/Triangle/ArcIAF.cs @@ -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(); + 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); + } + } +} diff --git a/ArcFormats/Triangle/ImageIAF.cs b/ArcFormats/Triangle/ImageIAF.cs index 46010b2c..12c86512 100644 --- a/ArcFormats/Triangle/ImageIAF.cs +++ b/ArcFormats/Triangle/ImageIAF.cs @@ -76,46 +76,20 @@ namespace GameRes.Formats.Triangle if (Math.Abs (x) > 4096 || Math.Abs (y) > 4096) return null; int pack_type = (unpacked_size >> 30) & 3; + if (3 == pack_type) + return null; unpacked_size &= (int)~0xC0000000; stream.Position = data_offset; - byte[] bmp; - 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; - } + byte[] bmp = UnpackBitmap (stream, pack_type, packed_size, 0x26); if (bmp[0] != 'B' && bmp[0] != 'C' || bmp[1] != 'M') return null; - int width = LittleEndian.ToInt32 (bmp, 0x12); - int height = LittleEndian.ToInt32 (bmp, 0x16); - int bpp = LittleEndian.ToInt16 (bmp, 0x1c); return new IafMetaData { - Width = (uint)width, - Height = (uint)height, + Width = LittleEndian.ToUInt32 (bmp, 0x12), + Height = LittleEndian.ToUInt32 (bmp, 0x16), OffsetX = x, OffsetY = y, - BPP = bpp, + BPP = LittleEndian.ToInt16 (bmp, 0x1c), DataOffset = data_offset, PackedSize = packed_size, UnpackedSize = unpacked_size, @@ -125,41 +99,16 @@ namespace GameRes.Formats.Triangle public override ImageData Read (Stream stream, ImageMetaData info) { - var meta = info as IafMetaData; - if (null == meta) - throw new ArgumentException ("IafFormat.Read should be supplied with IafMetaData", "info"); - + var meta = (IafMetaData)info; stream.Position = meta.DataOffset; - byte[] bitmap; - 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"); - } + var bitmap = UnpackBitmap (stream, meta.PackType, meta.PackedSize, meta.UnpackedSize); if ('C' == bitmap[0]) { bitmap[0] = (byte)'B'; if (info.BPP > 8) 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 { @@ -172,7 +121,7 @@ namespace GameRes.Formats.Triangle uint alpha_width = LittleEndian.ToUInt32 (bitmap, bmp_size+0x12); uint alpha_height = LittleEndian.ToUInt32 (bitmap, bmp_size+0x16); 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); } + 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) { throw new System.NotImplementedException ("IafFormat.Write not implemented"); diff --git a/supported.html b/supported.html index cc7536ba..6b06a7a5 100644 --- a/supported.html +++ b/supported.html @@ -122,9 +122,10 @@ Sweet Pool
Zoku Satsuriku no Django
*.npa-YesNitro+Steins;Gate -*.pak\002\000\000\000
\003\000\000\000NoNitro+ +*.pak\002\000\000\000
\003\000\000\000NoNitro+
CoreMoreco Hanachirasu
Jingai Makyou
+Chibi Mama
*.nsa
*.sar-Yes1NScripter Binary Pot
@@ -452,11 +453,13 @@ Gokudou no Hanayome
Knight Carnival!
Seal Princess
-*.sud-NoTriangle +*.sud-NoTriangle Kourin Tenshi En Ciel Rena
Mamagoto
+Vampire Crusaders
*.iaf-No +*.cgf-No *.bin+*.pakhedNoelfAdult Video King *.hip
*.hiz
hip
hizNo *.gpk+*.gtb
*.vpk+*.vtb-NoBlack Cyc @@ -612,6 +615,7 @@ AstralAir no Shiroki Towa
*.hzchzc1No *.binESC-ARC1
ESC-ARC2NoEscu:de Otome Renshin Prister
+Suisei Tenshi Primaveil Zwei
Wondering Repair!
*.pac-NoTmr-Hiro ADV System @@ -637,6 +641,7 @@ Ayakashi H
*.arcLINK3NoKaGuYa Dokidoki Onee-san
+Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~
*.alpAP-2No *.anmAN00No