diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 6c1df55b..d09fd8bb 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -86,6 +86,7 @@ + diff --git a/ArcFormats/BlueGale/ImageZBM.cs b/ArcFormats/BlueGale/ImageZBM.cs index e936f297..dd588f48 100644 --- a/ArcFormats/BlueGale/ImageZBM.cs +++ b/ArcFormats/BlueGale/ImageZBM.cs @@ -63,6 +63,7 @@ namespace GameRes.Formats.BlueGale var header = new byte[0x20]; stream.Position = data_offset; Unpack (stream, header); + Decrypt (header); if ('B' != header[0] || 'M' != header[1]) return null; return new ZbmMetaData @@ -82,8 +83,9 @@ namespace GameRes.Formats.BlueGale var data = new byte[meta.UnpackedSize]; stream.Position = meta.DataOffset; Unpack (stream, data); + Decrypt (data); using (var bmp = new MemoryStream (data)) - return ImageFormat.Bmp.Read (bmp, info); + return Bmp.Read (bmp, info); } public override void Write (Stream file, ImageData image) @@ -91,20 +93,18 @@ namespace GameRes.Formats.BlueGale throw new System.NotImplementedException ("ZbmFormat.Write not implemented"); } - static void Unpack (Stream input, byte[] output) + internal static void Unpack (Stream input, byte[] output, int dst = 0) { using (var bits = new MsbBitStream (input, true)) { bits.GetNextBit(); - int dst = 0; while (dst < output.Length) { int count = bits.GetBits (8); if (-1 == count) - throw new EndOfStreamException(); + break; if (count > 0x7F) { - count &= 0x7F; int offset = bits.GetBits (10); if (-1 == offset) throw new EndOfStreamException(); @@ -114,6 +114,8 @@ namespace GameRes.Formats.BlueGale } else { + if (0 == count) + break; for (int i = 0 ; i < count && dst < output.Length; i++) { int v = bits.GetBits (8); @@ -124,7 +126,6 @@ namespace GameRes.Formats.BlueGale } } } - Decrypt (output); } static void Decrypt (byte[] data) diff --git a/ArcFormats/BlueGale/VideoAMV.cs b/ArcFormats/BlueGale/VideoAMV.cs new file mode 100644 index 00000000..084b9b5d --- /dev/null +++ b/ArcFormats/BlueGale/VideoAMV.cs @@ -0,0 +1,96 @@ +//! \file VideoAMV.cs +//! \date Sat Aug 13 08:31:51 2016 +//! \brief Blue Gale animation format. +// +// 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.Utility; + +namespace GameRes.Formats.BlueGale +{ + [Export(typeof(ArchiveFormat))] + public class AmvOpener : ArchiveFormat + { + public override string Tag { get { return "AMPV"; } } + public override string Description { get { return "BlueGale animation format"; } } + public override uint Signature { get { return 0x56706D61; } } // 'ampV' + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public AmvOpener () + { + Extensions = new string[] { "amv" }; + } + + public override ArcFile TryOpen (ArcView file) + { + if (file.View.ReadInt16 (4) != 1) + return null; + uint unpacked_size = file.View.ReadUInt32 (0x16); + uint width = file.View.ReadUInt32 (0x1A); + uint height = file.View.ReadUInt32 (0x1E); + int count = file.View.ReadInt32 (0x2A); + if (!IsSaneCount (count)) + return null; + var base_name = Path.GetFileNameWithoutExtension (file.Name); + uint offset = 0x32; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + uint size = file.View.ReadUInt32 (offset); + var entry = new PackedEntry + { + Name = string.Format ("{0}#{1:D4}.bmp", base_name, i), + Type = "image", + Offset = offset + 4, + Size = size, + IsPacked = true, + UnpackedSize = unpacked_size + 0x36, + }; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + offset += 4 + size; + } + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var pent = (PackedEntry)entry; + var output = new byte[pent.UnpackedSize]; + using (var input = arc.File.CreateStream (entry.Offset, entry.Size)) + ZbmFormat.Unpack (input, output, 0xE); + + output[0] = (byte)'B'; + output[1] = (byte)'M'; + LittleEndian.Pack (pent.UnpackedSize, output, 2); + int header_size = LittleEndian.ToInt32 (output, 0xE); + LittleEndian.Pack (header_size+0xE, output, 0xA); + return new MemoryStream (output); + } + } +} diff --git a/supported.html b/supported.html index 13f9ae53..78fce72b 100644 --- a/supported.html +++ b/supported.html @@ -829,13 +829,15 @@ Hitozuma Sakunyuu Hyakkaten
Mai Miko Moe
Paimega
-*.snn+*.inx-NoBlueGale +*.snn+*.inx-NoBlueGale +Bifronte ~Kugaitou Kitan~
Cafe Junkie
Immoral
Majidashi! Royale ~Finish wa Watashi no Naka de~
MILK Junkies
*.zbmamp_No +*.amvampVNo *.vfsVFNoAoi Alfred Gakuen Mamono Daitai
Brown Doori Sanbanme
@@ -987,12 +989,14 @@ Kara no Shoujo 2
data.NN
ArcNN.dat-NoCyberworks Aniyome Kyouka-san to Sono Haha Chikako-san
Aru Kazoku no Kankeizu
+Chou no Yume ~Futari no Chou~
Cosplay Ecchi ~Layer Kana no Yuuutsu~
Gakkou Yarashii Kaidan
Hanamaru! 2
Hime Kami 1/2
In'youchuu Goku ~Ryoujoku Jigoku Taimaroku~
In'youchuu Rei ~Ryoujoku Shiro Taima Emaki~
+Koitsuma Biyori ~Yukino-san wa Hitozuma Kanrinin~
Kuraibito
Naze ka Kanojo ga Boku ni Ecchi o Sematte Kuru Ken
Onna Kyoushi Suzune