From 2f9964c1661f0e7c1ed735d3572e888992051489 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 28 Jul 2016 04:54:42 +0400 Subject: [PATCH] implemented DDP archives. --- ArcFormats/DDSystem/ArcDDP.cs | 197 ++++++++++++++++++++++++++++++++++ ArcFormats/SHSystem/ArcHXP.cs | 37 ++++--- supported.html | 17 ++- 3 files changed, 233 insertions(+), 18 deletions(-) create mode 100644 ArcFormats/DDSystem/ArcDDP.cs diff --git a/ArcFormats/DDSystem/ArcDDP.cs b/ArcFormats/DDSystem/ArcDDP.cs new file mode 100644 index 00000000..fce8d793 --- /dev/null +++ b/ArcFormats/DDSystem/ArcDDP.cs @@ -0,0 +1,197 @@ +//! \file ArcDDP.cs +//! \date Wed Jul 27 13:21:01 2016 +//! \brief DDSystem 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; +using System.Linq; +using GameRes.Formats.SHSystem; +using GameRes.Utility; + +namespace GameRes.Formats.DDSystem +{ + [Export(typeof(ArchiveFormat))] + public class Ddp2Opener : ArchiveFormat + { + public override string Tag { get { return "DDP2"; } } + public override string Description { get { return "DDSystem engine resource archive"; } } + public override uint Signature { get { return 0x32504444; } } // 'DDP2' + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public Ddp2Opener () + { + Extensions = new string[] { "dat" }; + } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + + var base_name = Path.GetFileNameWithoutExtension (file.Name); + uint index_offset = 0x20; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var entry = new PackedEntry { + Offset = file.View.ReadUInt32 (index_offset), + UnpackedSize = file.View.ReadUInt32 (index_offset+4), + Size = file.View.ReadUInt32 (index_offset+8), + Name = string.Format ("{0}#{1:D5}", base_name, i), + }; + entry.IsPacked = entry.Size != 0; + if (!entry.IsPacked) + entry.Size = entry.UnpackedSize; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + index_offset += 0x10; + dir.Add (entry); + } + DetectFileTypes (file, dir); + return new ArcFile (file, this, dir); + } + + internal static void DetectFileTypes (ArcView file, List dir) + { + byte[] signature_buffer = new byte[4]; + foreach (PackedEntry entry in dir) + { + uint signature; + if (entry.IsPacked) + { + using (var input = file.CreateStream (entry.Offset, Math.Min (entry.Size, 0x20u))) + using (var reader = new ShsCompression (input)) + { + reader.Unpack (signature_buffer); + signature = LittleEndian.ToUInt32 (signature_buffer, 0); + } + } + else + { + signature = file.View.ReadUInt32 (entry.Offset); + } + if (0x78534444 == signature) // 'DDSx' + { + entry.Type = "script"; + entry.Name = Path.ChangeExtension (entry.Name, "hxb"); + } + else if (0 != signature) + { + IResource res; + if (0x020000 == signature || 0x0A0000 == signature) + res = ImageFormat.Tga; + else + res = AutoEntry.DetectFileType (signature); + if (res != null) + { + entry.Type = res.Type; + var ext = res.Extensions.FirstOrDefault(); + if (!string.IsNullOrEmpty (ext)) + entry.Name = Path.ChangeExtension (entry.Name, ext); + } + } + } + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var input = arc.File.CreateStream (entry.Offset, entry.Size); + var pent = entry as PackedEntry; + if (null == pent || !pent.IsPacked) + return input; + var data = new byte[pent.UnpackedSize]; + using (var reader = new ShsCompression (input)) + { + reader.Unpack (data); + if (data.Length > 16 && Binary.AsciiEqual (data, 0, "DDSxHXB")) + DecryptHxb (data); + return new MemoryStream (data); + } + } + + internal void DecryptHxb (byte[] data) + { + int length = data[8] << 16 | data[9] << 8 | data[10]; + if (length != data.Length) + return; + int key = (((length << 5) ^ 0xA5) * (length + 0x6F349)) ^ 0x34A9B129; + var key_bits = new byte[4]; + LittleEndian.Pack (key, key_bits, 0); + for (int i = 0x10; i < data.Length; ++i) + { + data[i] ^= key_bits[i & 3]; + } + } + } + + [Export(typeof(ArchiveFormat))] + public class Ddp3Opener : Ddp2Opener + { + public override string Tag { get { return "DDP3"; } } + public override string Description { get { return "DDSystem engine resource archive"; } } + public override uint Signature { get { return 0x33504444; } } // 'DDP3' + 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 (4); + if (!IsSaneCount (count)) + return null; + + var index = Him5Opener.ReadIndex (file, 0x20, count); + var dir = new List(); + foreach (var section in index) + { + int index_offset = section.Item1; + for (int section_size = section.Item2; section_size > 0; ) + { + int entry_size = file.View.ReadByte (index_offset); + if (entry_size < 17) + break; + var entry = new PackedEntry { + Offset = file.View.ReadUInt32 (index_offset+1), + UnpackedSize = file.View.ReadUInt32 (index_offset+5), + Size = file.View.ReadUInt32 (index_offset+9), + Name = file.View.ReadString (index_offset+17, (uint)entry_size-17), + }; + entry.IsPacked = entry.Size != 0; + if (!entry.IsPacked) + entry.Size = entry.UnpackedSize; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + index_offset += entry_size; + section_size -= entry_size; + dir.Add (entry); + } + } + DetectFileTypes (file, dir); + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/SHSystem/ArcHXP.cs b/ArcFormats/SHSystem/ArcHXP.cs index 8d9e2e24..17373984 100644 --- a/ArcFormats/SHSystem/ArcHXP.cs +++ b/ArcFormats/SHSystem/ArcHXP.cs @@ -81,9 +81,9 @@ namespace GameRes.Formats.SHSystem { uint packed_size = file.View.ReadUInt32 (entry.Offset); uint unpacked_size = file.View.ReadUInt32 (entry.Offset+4); - if (0 == packed_size) + entry.IsPacked = 0 != packed_size; + if (!entry.IsPacked) packed_size = unpacked_size; - entry.IsPacked = packed_size != unpacked_size; entry.Size = packed_size; entry.UnpackedSize = unpacked_size; entry.Offset += 8; @@ -104,12 +104,10 @@ namespace GameRes.Formats.SHSystem if (0 != signature) { IResource res; - if (0x4D42 == (signature & 0xFFFF)) - res = ImageFormat.Bmp; - else if (0x020000 == signature || 0x0A0000 == signature) + if (0x020000 == signature || 0x0A0000 == signature) res = ImageFormat.Tga; else - res = FormatCatalog.Instance.LookupSignature (signature).FirstOrDefault(); + res = AutoEntry.DetectFileType (signature); if (res != null) { entry.Type = res.Type; @@ -158,20 +156,11 @@ namespace GameRes.Formats.SHSystem if (!IsSaneCount (count)) return null; - int index_offset = 8; - var index = new List> (count); - for (int i = 0; i < count; ++i) - { - int size = file.View.ReadInt32 (index_offset); - int offset = file.View.ReadInt32 (index_offset+4); - index_offset += 8; - if (size != 0) - index.Add (Tuple.Create (offset, size)); - } + var index = ReadIndex (file, 8, count); var dir = new List(); foreach (var section in index) { - index_offset = section.Item1; + int index_offset = section.Item1; for (int section_size = section.Item2; section_size > 0; ) { int entry_size = file.View.ReadByte (index_offset); @@ -191,6 +180,20 @@ namespace GameRes.Formats.SHSystem DetectFileTypes (file, dir); return new ArcFile (file, this, dir); } + + internal static List> ReadIndex (ArcView file, uint index_offset, int count) + { + var index = new List> (count); + for (int i = 0; i < count; ++i) + { + int size = file.View.ReadInt32 (index_offset); + int offset = file.View.ReadInt32 (index_offset+4); + index_offset += 8; + if (size != 0) + index.Add (Tuple.Create (offset, size)); + } + return index; + } } internal class ShsCompression : IDisposable diff --git a/supported.html b/supported.html index da404ae5..12d35b97 100644 --- a/supported.html +++ b/supported.html @@ -68,6 +68,7 @@ Eiyuu x Maou
Wasurenagusa ~Forget-me-Not~
*.gsp-NoGSD +I Cup Bonyuu Kangofu
Natsuiro ☆ Communication ♪
Saimin Gakuen
@@ -456,6 +457,7 @@ Chijoku no Kankei ~Inkou Kyoushi~
Chijoku no Kankei 2
Jii -Nozoki no Houshuu-
Itazura ZERO
+Oira wa Bandai 2
*.kgGCGKNo *.noa
*.datEntis\x1aNoEntis GLS @@ -472,6 +474,7 @@ Santaful☆Summer
Space☆Trouble
Tonarizuma
Yatohime Zankikou
+Zettai Joshi Ryouiki!
*.eri
*.mioEntis\x1aNo *.saf-NoLuneRinkan Gakuen @@ -570,8 +573,9 @@ Toshiue Lesson ~Mama to Oba-san to Sensei to~
Tutorial Summer
*.iksNPSRNoX[iks]Shikkan ~Hazukashimerareta Karada, Oreta Kokoro~ -*.wbpARCFORM3 WBUGNoWild Bug +*.wbpARCFORM3 WBUG
ARCFORM4 WBUGNoWild Bug Happy Planning
+Ryoujoku Hitozuma Club
Yuukyou Gangu 2
*.wbmWPX\x1ABMPNo @@ -965,6 +969,17 @@ Zoku Etsuraku no Tane
*.binOZNoPatisserie Ore no Tamanokoshi ni Notte Kure
+*.arc-NoKISS +xx na Kanojo no Tsukurikata
+ +*.pakIPACNoNounai Kanojo +Osananajimi wa Bed Yakuza!
+ +*.iesIES2No +*.wstWST2No +*.datDDP2
DDP3NoDDSystem +Nukiani!! Sweet Home
+

1 Non-encrypted only