From 7838fc36f6982ef58afdd999b4d84e8fc2562600 Mon Sep 17 00:00:00 2001 From: morkt Date: Sat, 1 Aug 2015 00:22:06 +0400 Subject: [PATCH] implemented elf AV King resources. *.bin+*.pak archives, HIZ and HIP images. --- ArcFormats/ArcFormats.csproj | 2 + ArcFormats/ArcHED.cs | 203 +++++++++++++++++++++++++++++++++++ ArcFormats/ImageHIZ.cs | 151 ++++++++++++++++++++++++++ supported.html | 8 ++ 4 files changed, 364 insertions(+) create mode 100644 ArcFormats/ArcHED.cs create mode 100644 ArcFormats/ImageHIZ.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 9f491937..3fc0f578 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -88,6 +88,7 @@ + @@ -122,6 +123,7 @@ + diff --git a/ArcFormats/ArcHED.cs b/ArcFormats/ArcHED.cs new file mode 100644 index 00000000..2d6d9418 --- /dev/null +++ b/ArcFormats/ArcHED.cs @@ -0,0 +1,203 @@ +//! \file ArcHED.cs +//! \date Fri Jul 31 19:51:15 2015 +//! \brief elf AV king archive. +// +// Copyright (C) 2015 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.Text; +using System.Text.RegularExpressions; +using GameRes.Utility; + +namespace GameRes.Formats.Elf +{ + [Export(typeof(ArchiveFormat))] + public class PakOpener : ArchiveFormat + { + public override string Tag { get { return "BIN/HED"; } } + public override string Description { get { return "elf AV King resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return true; } } + public override bool CanCreate { get { return false; } } + + public PakOpener () + { + Extensions = new string[] { "bin" }; + } + + public override ArcFile TryOpen (ArcView file) + { + string pak_name = Path.ChangeExtension (file.Name, "pak"); + if (!File.Exists (pak_name)) + return null; + var file_map = GetFileMap (pak_name); + if (null == file_map) + return null; + string base_name = Path.GetFileNameWithoutExtension (pak_name); + + using (var pak = new ArcView (pak_name)) + { + if (0x00646568 != pak.View.ReadUInt32 (0)) + return null; + int count = pak.View.ReadInt32 (4); + if (count != file_map.Count) + return null; + List dir; + if ("cg" == base_name) + dir = ReadCgPak (pak, file_map); + else + dir = ReadVoicePak (pak, file_map); + if (null == dir) + return null; + return new ArcFile (file, this, dir); + } + } + + List ReadCgPak (ArcView pak, List file_map) + { + uint index_offset = 8; + uint index_size = (uint)file_map.Count * 8u; + if (index_size > pak.View.Reserve (index_offset, index_size)) + return null; + var dir = new List (file_map.Count); + for (int i = 0; i < file_map.Count; ++i) + { + var entry = FormatCatalog.Instance.CreateEntry (file_map[i]); + entry.Offset = pak.View.ReadUInt32 (index_offset); + entry.Size = pak.View.ReadUInt32 (index_offset + 4); + dir.Add (entry); + index_offset += 8; + } + return dir; + } + + List ReadVoicePak (ArcView pak, List file_map) + { + uint index_offset = 8; + uint index_size = (uint)file_map.Count * 0x18u; + if (index_size > pak.View.Reserve (index_offset, index_size)) + return null; + var dir = new List (file_map.Count); + for (int i = 0; i < file_map.Count; ++i) + { + var entry = FormatCatalog.Instance.CreateEntry (file_map[i]); + entry.Offset = pak.View.ReadUInt32 (index_offset); + entry.Size = pak.View.ReadUInt32 (index_offset + 4); + dir.Add (entry); + index_offset += 0x18; + } + return dir; + } + + private List CgMap { get; set; } + private List VoiceMap { get; set; } + private string CurrentMapName { get; set; } + + private List GetFileMap (string pak_name, string map_name = "avking.map") + { + string base_name = Path.GetFileNameWithoutExtension (pak_name); + List map; + if ("cg" == base_name) + map = CgMap; + else if ("voice" == base_name) + map = VoiceMap; + else + return null; + if (null != map && File.Exists (CurrentMapName)) + return map; + CgMap = null; + VoiceMap = null; + string dir_name = Path.GetDirectoryName (pak_name); + if (string.IsNullOrEmpty (dir_name)) + dir_name = "."; + while (!string.IsNullOrEmpty (dir_name)) + { + string map_file = Path.Combine (dir_name, map_name); + if (File.Exists (map_file)) + { + if (!ReadMap (map_file)) + return null; + CurrentMapName = map_file; + if ("cg" == base_name) + return CgMap; + if ("voice" == base_name) + return VoiceMap; + } + dir_name = Path.GetDirectoryName (dir_name); + } + return null; + } + + static readonly Regex FilesTypeRe = new Regex (@"^//([A-Z]+) FILES = (\d+)"); + + private bool ReadMap (string map_file) + { + try + { + using (var map = File.OpenRead (map_file)) + using (var input = new StreamReader (map, Encoding.ASCII)) + { + var cg = new List(); + var voice = new List(); + List current_list = null; + for (;;) + { + string line = input.ReadLine(); + if (null == line) + break; + var match = FilesTypeRe.Match (line); + if (!match.Success) + return false; + string type = match.Groups[1].Value; + if ("BG" == type || "CHR" == type) + current_list = cg; + else if ("VOICE" == type) + current_list = voice; + else + current_list = null; + int count = UInt16.Parse (match.Groups[2].Value); + for (int i = 0; i < count; ++i) + { + line = input.ReadLine(); + if (null == line) + break; + if (null != current_list) + current_list.Add (line.TrimEnd ('\0')); + } + } + CgMap = cg; + VoiceMap = voice; + return cg.Count > 0 || voice.Count > 0; + } + } + catch + { + return false; + } + } + } +} + + diff --git a/ArcFormats/ImageHIZ.cs b/ArcFormats/ImageHIZ.cs new file mode 100644 index 00000000..031ce9b1 --- /dev/null +++ b/ArcFormats/ImageHIZ.cs @@ -0,0 +1,151 @@ +//! \file ImageHIZ.cs +//! \date Fri Jul 31 19:07:24 2015 +//! \brief elf HIZ bitmap format. +// +// Copyright (C) 2015 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 GameRes.Compression; +using GameRes.Utility; + +namespace GameRes.Formats.Elf +{ + internal class HizMetaData : ImageMetaData + { + public bool IsPacked; + public uint DataOffset; + public uint UnpackedSize; + } + + [Export(typeof(ImageFormat))] + public class HizFormat : ImageFormat + { + public override string Tag { get { return "HIZ"; } } + public override string Description { get { return "elf bitmap format"; } } + public override uint Signature { get { return 0x007A6968; } } // 'hiz' + + public override ImageMetaData ReadMetaData (Stream stream) + { + using (var input = new ArcView.Reader (stream)) // sub_4BF900 + { + input.ReadInt32(); + int n = input.ReadInt32(); + if (100 != n) + return null; + uint right = input.ReadUInt32() ^ 0xAA5A5A5A; + uint bottom = input.ReadUInt32() ^ 0xAC9326AF; + int unknown1 = input.ReadInt32(); // @0x10 + if (unknown1 == 0x375A8436) + return null; + uint unpacked_size = input.ReadUInt32() ^ 0x19739D6A; // @0x14 + if (unpacked_size != right*bottom*4) + return null; + return new HizMetaData + { + Width = right, + Height = bottom, + BPP = 32, + IsPacked = true, + DataOffset = 0x4c, + UnpackedSize = unpacked_size, + }; + } + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = info as HizMetaData; + if (null == meta) + throw new ArgumentException ("HizFormat.Read should be supplied with HizMetaData", "info"); + + var pixels = new byte[meta.UnpackedSize]; + stream.Position = meta.DataOffset; + using (var lzss = new LzssStream (stream, LzssMode.Decompress, true)) + { + var channel = new byte[info.Width*info.Height]; + for (int p = 0; p < 4; ++p) + { + if (channel.Length != lzss.Read (channel, 0, channel.Length)) + throw new InvalidFormatException ("Unexpected end of file"); + int src = 0; + for (int i = p; i < pixels.Length; i += 4) + pixels[i] = channel[src++]; + } + return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("HizFormat.Write not implemented"); + } + } + + [Export(typeof(ImageFormat))] + public class HipFormat : HizFormat + { + public override string Tag { get { return "HIP"; } } + public override string Description { get { return "elf composite image format"; } } + public override uint Signature { get { return 0x00706968; } } // 'hip' + + public override ImageMetaData ReadMetaData (Stream stream) + { + byte[] header = new byte[0x18]; + if (0x18 != stream.Read (header, 0, 0x18)) + return null; + int index_offset = 0xC; + uint first_offset = LittleEndian.ToUInt32 (header, index_offset); + if (0 == first_offset) + { + index_offset += 4; + first_offset = LittleEndian.ToUInt32 (header, index_offset); + if (0 == first_offset) + return null; + } + index_offset += 4; + + long first_length; + uint second_offset = LittleEndian.ToUInt32 (header, index_offset); + if (0 == second_offset) + first_length = stream.Length - first_offset; + else if (second_offset < first_offset) + return null; + else + first_length = second_offset - first_offset; + + using (var hiz = new StreamRegion (stream, first_offset, first_length, true)) + { + var info = base.ReadMetaData (hiz); + (info as HizMetaData).DataOffset += 0x18; + return info; + } + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("HipFormat.Write not implemented"); + } + } +} diff --git a/supported.html b/supported.html index 06939acb..d5f76c45 100644 --- a/supported.html +++ b/supported.html @@ -136,6 +136,7 @@ Chikatetsu Fuusa Jiken
Eien no Owari ni
Ikusa Otome Valkyrie
Sakura Machizaka Stories vol.1
+Sakura Machizaka Stories vol.2
*.prsYBNo *.wayWADYNo @@ -164,13 +165,18 @@ Shouhei-kun no Hani-Kami Life☆
*\x00\x00\x04\x00No *.warWARC 1.7
WARC 1.5
WARC 1.3NoShiina Rio Classmate no Okaa-san ShiinaRio v2.37
+Chikan Circle ShiinaRio v2.46
+Chikan Circle 2 ShiinaRio v2.47
Enkaku Sousa
Helter Skelter ShiinaRio v2.40
Hitozuma Onna Kyoushi Reika ShiinaRio v2.39
Itsuka, Dokoka de ~Ano Ameoto no Kioku~
Mahou Shoujo no Taisetsu na Koto
Nagagutsu wo Haita Deco ShiinaRio v2.39
+Najimi no Oba-chan ShiinaRio v2.47
Otome Juurin Yuugi ShiinaRio v2.37
+Ran→Sem ShiinaRio v2.47
+Rin×Sen ShiinaRio v2.47
Tantei Shounen A
*.s25S25No @@ -278,6 +284,8 @@ Kourin Tenshi En Ciel Rena
Mamagoto
*.iaf-No +*.bin+*.pakhedNoelfAdult Video King +*.hip
*.hiz
hip
hizNo

1 Non-encrypted only