diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 276820fa..96addff7 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -81,6 +81,8 @@ + + diff --git a/ArcFormats/Slg/AudioVOI.cs b/ArcFormats/Slg/AudioVOI.cs new file mode 100644 index 00000000..41b60a18 --- /dev/null +++ b/ArcFormats/Slg/AudioVOI.cs @@ -0,0 +1,51 @@ +//! \file AudioVOI.cs +//! \date Fri Mar 11 23:55:31 2016 +//! \brief SLG system obfuscated OGG file. +// +// 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.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.Slg +{ + [Export(typeof(AudioFormat))] + public class VoiAudio : AudioFormat + { + public override string Tag { get { return "VOI"; } } + public override string Description { get { return "SLG system obfuscated Ogg audio"; } } + public override uint Signature { get { return 0; } } // 'OggS' + + public override SoundInput TryOpen (Stream file) + { + file.Position = 0x1E; + int offset = file.ReadByte(); + if (offset <= 0) + return null; + file.Position = 0x20 + offset; + if (!(file.ReadByte() == 'O' && file.ReadByte() == 'g' && + file.ReadByte() == 'g' && file.ReadByte() == 'S')) + return null; + return new OggInput (new StreamRegion (file, 0x20+offset)); + } + } +} diff --git a/ArcFormats/Slg/ImageALB.cs b/ArcFormats/Slg/ImageALB.cs new file mode 100644 index 00000000..f1660f66 --- /dev/null +++ b/ArcFormats/Slg/ImageALB.cs @@ -0,0 +1,265 @@ +//! \file ImageALB.cs +//! \date Fri Mar 11 18:27:42 2016 +//! \brief SLG system obfuscated image 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.Formats.Cri; +using GameRes.Utility; + +namespace GameRes.Formats.Slg +{ + internal class AlbMetaData : ImageMetaData + { + public int UnpackedSize; + public ImageFormat Format; + public ImageMetaData Info; + } + + [Export(typeof(ImageFormat))] + public class AlbFormat : ImageFormat + { + public override string Tag { get { return "ALB"; } } + public override string Description { get { return "SLG system image format"; } } + public override uint Signature { get { return 0x31424C41; } } // 'ALB1' + + static readonly Lazy Dds = new Lazy (() => ImageFormat.FindByTag ("DDS")); + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x10]; + if (0x10 != stream.Read (header, 0, 0x10)) + return null; + int unpacked_size = LittleEndian.ToInt32 (header, 8); + using (var alb = new AlbStream (stream, unpacked_size)) + using (var file = new SeekableStream (alb)) + { + uint signature = FormatCatalog.ReadSignature (file); + file.Position = 0; + ImageFormat format; + if (ImageFormat.Png.Signature == signature) + format = ImageFormat.Png; + else if (Dds.Value.Signature == signature) + format = Dds.Value; + else + return null; + var info = format.ReadMetaData (file); + if (null == info) + return null; + return new AlbMetaData + { + Width = info.Width, + Height = info.Height, + OffsetX = info.OffsetX, + OffsetY = info.OffsetY, + BPP = info.BPP, + Format = format, + Info = info, + UnpackedSize = unpacked_size, + }; + } + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = (AlbMetaData)info; + stream.Position = 0x10; + using (var alb = new AlbStream (stream, meta.UnpackedSize)) + using (var file = new SeekableStream (alb)) + return meta.Format.Read (file, meta.Info); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("AlbFormat.Write not implemented"); + } + } + + internal class AlbStream : Stream + { + BinaryReader m_input; + int m_unpacked_size; + IEnumerator m_iterator; + byte[] m_output; + int m_output_pos; + int m_output_end; + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return false; } } + + public AlbStream (Stream source, int unpacked_size) + { + m_input = new ArcView.Reader (source); + m_unpacked_size = unpacked_size; + m_iterator = ReadSeq(); + } + + public override int Read (byte[] buffer, int offset, int count) + { + m_output = buffer; + m_output_pos = offset; + m_output_end = offset + count; + if (!m_iterator.MoveNext()) + return 0; + return m_iterator.Current - offset; + } + + byte[,] m_dict = new byte[256,2]; + + IEnumerator ReadSeq () + { + var stack = new byte[256]; + while (m_input.PeekChar() != -1) + { + int packed_size = UnpackDict(); + int src = 0; + int stack_pos = 0; + for (;;) + { + byte s; + if (stack_pos > 0) + { + s = stack[--stack_pos]; + } + else if (src < packed_size) + { + s = m_input.ReadByte(); + src++; + } + else + { + break; + } + if (m_dict[s,0] == s) + { + while (m_output_pos == m_output_end) + yield return m_output_pos; + m_output[m_output_pos++] = s; + } + else + { + stack[stack_pos++] = m_dict[s,1]; + stack[stack_pos++] = m_dict[s,0]; + } + } + } + yield return m_output_pos; + } + + int UnpackDict () + { + if (m_input.ReadInt16() != 0x4850) // 'PH' + throw new InvalidFormatException(); + int table_size = m_input.ReadUInt16(); + int packed_size = m_input.ReadUInt16(); + bool is_packed = m_input.ReadByte() != 0; + byte marker = m_input.ReadByte(); + if (is_packed) + { + for (int i = 0; i < 256; ) + { + byte b = m_input.ReadByte(); + if (marker == b) + { + int count = m_input.ReadByte(); + for (int j = 0; j < count; ++j) + { + m_dict[i,0] = (byte)i; + m_dict[i,1] = 0; + ++i; + } + } + else + { + m_dict[i,0] = b; + m_dict[i,1] = m_input.ReadByte(); + ++i; + } + } + } + else + { + for (int i = 0; i < 256; ++i) + { + m_dict[i,0] = m_input.ReadByte(); + m_dict[i,1] = m_input.ReadByte(); + } + } + return packed_size; + } + + #region IO.Stream Members + public override long Length { get { return m_unpacked_size; } } + public override long Position + { + get { throw new NotSupportedException ("AlbStream.Position not supported"); } + set { throw new NotSupportedException ("AlbStream.Position not supported"); } + } + + public override void Flush() + { + } + + public override long Seek (long offset, SeekOrigin origin) + { + throw new NotSupportedException ("AlbStream.Seek method is not supported"); + } + + public override void SetLength (long length) + { + throw new NotSupportedException ("AlbStream.SetLength method is not supported"); + } + + public override void Write (byte[] buffer, int offset, int count) + { + throw new NotSupportedException ("AlbStream.Write method is not supported"); + } + + public override void WriteByte (byte value) + { + throw new NotSupportedException ("AlbStream.WriteByte method is not supported"); + } + #endregion + + #region IDisposable Members + bool _alb_disposed = false; + protected override void Dispose (bool disposing) + { + if (!_alb_disposed) + { + if (disposing) + { + m_input.Dispose(); + m_iterator.Dispose(); + } + _alb_disposed = true; + } + base.Dispose (disposing); + } + #endregion + } +} diff --git a/supported.html b/supported.html index 205d7c56..1628cec5 100644 --- a/supported.html +++ b/supported.html @@ -118,6 +118,7 @@ Nostradamus ni Kiitemiro♪
Chokotto☆Vampire!
Clover Point
Setsuei
+Tsuki to Mahou to Taiyo to
*.npaNPAYesNitro+ Axanael
@@ -731,6 +732,10 @@ Tokyo Necro
Thanatos no Koi ~In Ane Otouto Soukan~
*.gpsGPSNo +*.albALB1.21NoSLG system +Shihen 69 ~Shinen no Messiah~
+ +*.voi-No

1 Non-encrypted only