From 6b4ee7a1469b68de0a92955d3afd77a9a4930d2a Mon Sep 17 00:00:00 2001 From: morkt Date: Mon, 11 May 2015 20:23:32 +0400 Subject: [PATCH] implemented MEGU engine resources. --- ArcFormats/ArcFormats.csproj | 2 + ArcFormats/ArcMGD.cs | 184 ++++++++++++ ArcFormats/ImageAG.cs | 391 ++++++++++++++++++++++++++ ArcFormats/Properties/AssemblyInfo.cs | 4 +- 4 files changed, 579 insertions(+), 2 deletions(-) create mode 100644 ArcFormats/ArcMGD.cs create mode 100644 ArcFormats/ImageAG.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 085fe86d..f3109e5d 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -93,6 +93,7 @@ + @@ -145,6 +146,7 @@ CreateYPFWidget.xaml + diff --git a/ArcFormats/ArcMGD.cs b/ArcFormats/ArcMGD.cs new file mode 100644 index 00000000..1ed3220f --- /dev/null +++ b/ArcFormats/ArcMGD.cs @@ -0,0 +1,184 @@ +//! \file ArcMGD.cs +//! \date Sun May 10 18:11:01 2015 +//! \brief MEGU archives implementation. +// +// 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 GameRes.Utility; + +// MEGU +// Masys Enhanced Game Unit + +namespace GameRes.Formats.Megu +{ + [Export(typeof(ArchiveFormat))] + public class MgdOpener : ArchiveFormat + { + public override string Tag { get { return "MGD"; } } + public override string Description { get { return "Masys resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + internal static readonly string Key = "Powerd by Masys"; + + public override ArcFile TryOpen (ArcView file) + { + uint signature = file.View.ReadUInt32 (0); + if (0x44474d != (signature & 0xffffff)) // 'MGD' + return null; + int count = file.View.ReadInt16 (0x20); + if (count <= 0) + return null; + int flag = file.View.ReadUInt16 (3); + var dir = new List (count); + int index_offset = 0x22; + byte[] name_buf = new byte[16]; + for (uint i = 0; i < count; ++i) + { + int name_size = file.View.ReadByte (index_offset+1); + if (0 == name_size) + return null; + if (name_size > name_buf.Length) + Array.Resize (ref name_buf, name_size); + file.View.Read (index_offset+2, name_buf, 0, (uint)name_size); + if (100 == flag) + Decrypt (name_buf, 0, name_size); + string name = Encodings.cp932.GetString (name_buf, 0, name_size); + index_offset += 2 + name_size; + + uint offset = file.View.ReadUInt32 (index_offset+4); + var entry = AutoEntry.Create (file, offset, name); + entry.Size = file.View.ReadUInt32 (index_offset); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 8; + } + return new ArcFile (file, this, dir); + } + + internal static void Decrypt (byte[] buffer, int offset, int length) + { + for (int i = 0; i < length; ++i) + { + buffer[offset+i] ^= (byte)Key[i%0xf]; + } + } + } + + internal class MgsEntry : Entry + { + public ushort Channels; + public uint SamplesPerSecond; + public ushort BitsPerSample; + } + + [Export(typeof(ArchiveFormat))] + public class MgsOpener : ArchiveFormat + { + public override string Tag { get { return "MGS"; } } + public override string Description { get { return "Masys audio resources 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) + { + uint signature = file.View.ReadUInt32 (0); + if (0x53474d != (signature & 0xffffff)) // 'MGS' + return null; + int count = file.View.ReadInt16 (0x20); + if (count <= 0) + return null; + int flag = file.View.ReadUInt16 (3); + var dir = new List (count); + int index_offset = 0x22; + byte[] name_buf = new byte[16]; + for (uint i = 0; i < count; ++i) + { + ushort channels = file.View.ReadUInt16 (index_offset+1); + uint rate = file.View.ReadUInt32 (index_offset+3); + ushort bits = file.View.ReadUInt16 (index_offset+7); + int name_size = file.View.ReadByte (index_offset+9); + if (0 == name_size) + return null; + if (name_size > name_buf.Length) + Array.Resize (ref name_buf, name_size); + file.View.Read (index_offset+10, name_buf, 0, (uint)name_size); + if (100 == flag) + MgdOpener.Decrypt (name_buf, 0, name_size); + index_offset += 10 + name_size; + + var entry = new MgsEntry + { + Name = Encodings.cp932.GetString (name_buf, 0, name_size) + ".wav", + Type = "audio", + Size = file.View.ReadUInt32 (index_offset), + Offset = file.View.ReadUInt32 (index_offset + 4), + Channels = channels, + SamplesPerSecond = rate, + BitsPerSample = bits, + }; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 8; + } + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var went = entry as MgsEntry; + if (null == went) + return arc.File.CreateStream (entry.Offset, entry.Size); + var riff = new byte[0x2c]; + using (var buf = new MemoryStream (riff)) + using (var wav = new BinaryWriter (buf)) + { + wav.Write (0x46464952); // 'RIFF' + uint total_size = 0x2c - 8 + entry.Size; + wav.Write (total_size); + wav.Write (0x45564157); // 'WAVE' + wav.Write (0x20746d66); // 'fmt ' + wav.Write (0x10); + wav.Write ((ushort)1); + wav.Write (went.Channels); + wav.Write (went.SamplesPerSecond); + uint bps = went.SamplesPerSecond * went.Channels * went.BitsPerSample / 8; + wav.Write (bps); + wav.Write ((ushort)2); + wav.Write (went.BitsPerSample); + wav.Write (0x61746164); // 'data' + wav.Write (went.Size); + wav.Flush (); + var input = arc.File.CreateStream (entry.Offset, entry.Size); + return new PrefixStream (riff, input); + } + } + } +} diff --git a/ArcFormats/ImageAG.cs b/ArcFormats/ImageAG.cs new file mode 100644 index 00000000..315d5178 --- /dev/null +++ b/ArcFormats/ImageAG.cs @@ -0,0 +1,391 @@ +//! \file ImageAG.cs +//! \date Sun May 10 23:53:34 2015 +//! \brief Masys Enhanced Game Unit image 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.Windows.Media; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.Megu +{ + [Export(typeof(ImageFormat))] + public class AgFormat : ImageFormat + { + public override string Tag { get { return "ACG"; } } + public override string Description { get { return "Masys image format"; } } + public override uint Signature { get { return 0x00644741u; } } // 'AGd' + + public override ImageMetaData ReadMetaData (Stream stream) + { + using (var file = new ArcView.Reader (stream)) + { + file.ReadUInt32(); + var info = new ImageMetaData(); + info.Width = file.ReadUInt32(); + info.Height = file.ReadUInt32(); + file.BaseStream.Position = 0x38; + int alpha_size = file.ReadInt32(); + info.BPP = 0 == alpha_size ? 24 : 32; + return info; + } + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var reader = new AgReader (stream, info); + reader.Unpack(); + return ImageData.Create (info, reader.Format, null, reader.Data); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("AgFormat.Write not implemented"); + } + } + + internal class AgReader + { + byte[] in1; + byte[] in2; + byte[] in3; + byte[] in4; + byte[] in5; + byte[] m_alpha; + byte[] m_output; + int m_width; + int m_height; + int m_pixel_size; + byte[] m_first = new byte[3]; + + public byte[] Data { get { return m_output; } } + public PixelFormat Format { get; private set; } + + public AgReader (Stream stream, ImageMetaData info) + { + m_width = (int)info.Width; + m_height = (int)info.Height; + stream.Position = 0x0c; + using (var input = new ArcView.Reader (stream)) + { + uint offset1 = input.ReadUInt32(); + int size1 = input.ReadInt32(); + uint offset2 = input.ReadUInt32(); + int size2 = input.ReadInt32(); + uint offset3 = input.ReadUInt32(); + int size3 = input.ReadInt32(); + uint offset4 = input.ReadUInt32(); + int size4 = input.ReadInt32(); + uint offset5 = input.ReadUInt32(); + int size5 = input.ReadInt32(); + uint offset6 = input.ReadUInt32(); + int size6 = input.ReadInt32(); + input.Read (m_first, 0, 3); + if (size1 != 0) + in1 = ReadSection (stream, offset1, size1); + if (size2 != 0) + in2 = ReadSection (stream, offset2, size2); + if (size3 != 0) + in3 = ReadSection (stream, offset3, size3); + if (size4 != 0) + in4 = ReadSection (stream, offset4, size4); + if (size5 != 0) + in5 = ReadSection (stream, offset5, size5); + if (size6 != 0) + { + input.BaseStream.Position = offset6; + m_alpha = new byte[m_height*m_width]; + RleDecode (input, m_alpha); + Format = PixelFormats.Bgra32; + m_pixel_size = 4; + } + else + { + Format = PixelFormats.Bgr24; + m_pixel_size = 3; + } + m_output = new byte[m_width*m_height*m_pixel_size]; + } + } + + static private byte[] ReadSection (Stream input, long offset, int size) + { + input.Position = offset; + var buf = new byte[size + 4]; + if (size != input.Read (buf, 0, size)) + throw new InvalidFormatException ("Unexpected end of file"); + return buf; + } + + public void Unpack () + { + int v20 = 1; + int src1 = 0; + uint v25 = LittleEndian.ToUInt32 (in1, src1); + src1 += 4; + int v5 = 1; + int src2 = 0; + uint v24 = LittleEndian.ToUInt32 (in2, src2); + src2 += 4; + int v4 = 1; + int src3 = 0; + uint v23 = LittleEndian.ToUInt32 (in3, src3); + src3 += 4; + int v3 = 0; + int src4 = 0; + uint v22 = LittleEndian.ToUInt32 (in4, src4); + src4 += 4; + int v19 = 0; + int src5 = 0; + uint v21 = LittleEndian.ToUInt32 (in5, src5); + src5 += 4; + + byte B; + byte G; + byte R; + + int stride = m_width * m_pixel_size; + for (int y = 0; y < m_height; ++y) + { + int dst = y * stride; + for (int x = 0; x < stride; x += m_pixel_size) + { + if (0 != (v20 & v25)) + { + B = (byte)(v21 >> v19); + v19 += 8; + if (32 == v19) + { + v21 = LittleEndian.ToUInt32 (in5, src5); + src5 += 4; + v19 = 0; + } + } + else + { + if (0 != (v4 & v23)) + { + B = m_first[0]; + } + else + { + B = (byte)((v22 >> v3) & 0xF); + v3 += 4; + if (32 == v3) + { + v22 = LittleEndian.ToUInt32 (in4, src4); + src4 += 4; + v3 = 0; + } + ++B; + if (0 != (v5 & v24)) + B = (byte)(m_first[0] - B); + else + B += m_first[0]; + v5 <<= 1; + if (0 == v5) + { + v5 = 1; + v24 = LittleEndian.ToUInt32 (in2, src2); + src2 += 4; + } + } + v4 <<= 1; + if (0 == v4) + { + v4 = 1; + v23 = LittleEndian.ToUInt32 (in3, src3); + src3 += 4; + } + } + v20 <<= 1; + if (0 == v20) + { + v25 = LittleEndian.ToUInt32 (in1, src1); + src1 += 4; + v20 = 1; + } + if (0 != (v20 & v25)) + { + G = (byte)(v21 >> v19); + v19 += 8; + if (32 == v19) + { + v21 = LittleEndian.ToUInt32 (in5, src5); + src5 += 4; + v19 = 0; + } + } + else + { + if (0 != (v4 & v23)) + { + G = m_first[1]; + } + else + { + G = (byte)((v22 >> v3) & 0xF); + v3 += 4; + if (32 == v3) + { + v22 = LittleEndian.ToUInt32 (in4, src4); + src4 += 4; + v3 = 0; + } + ++G; + if (0 != (v5 & v24)) + G = (byte)(m_first[1] - G); + else + G += m_first[1]; + v5 <<= 1; + if (0 == v5) + { + v5 = 1; + v24 = LittleEndian.ToUInt32 (in2, src2); + src2 += 4; + } + } + v4 <<= 1; + if (0 == v4) + { + v4 = 1; + v23 = LittleEndian.ToUInt32 (in3, src3); + src3 += 4; + } + } + v20 <<= 1; + if (0 == v20) + { + v25 = LittleEndian.ToUInt32 (in1, src1); + src1 += 4; + v20 = 1; + } + if (0 != (v20 & v25)) + { + R = (byte)(v21 >> v19); + v19 += 8; + if (32 == v19) + { + v21 = LittleEndian.ToUInt32 (in5, src5); + src5 += 4; + v19 = 0; + } + } + else + { + if (0 != (v4 & v23)) + { + R = m_first[2]; + } + else + { + R = (byte)((v22 >> v3) & 0xF); + v3 += 4; + if (32 == v3) + { + v22 = LittleEndian.ToUInt32 (in4, src4); + src4 += 4; + v3 = 0; + } + ++R; + if (0 != (v5 & v24)) + R = (byte)(m_first[2] - R); + else + R += m_first[2]; + v5 <<= 1; + if (0 == v5) + { + v5 = 1; + v24 = LittleEndian.ToUInt32 (in2, src2); + src2 += 4; + } + } + v4 <<= 1; + if (0 == v4) + { + v4 = 1; + v23 = LittleEndian.ToUInt32 (in3, src3); + src3 += 4; + } + } + v20 <<= 1; + if (0 == v20) + { + v25 = LittleEndian.ToUInt32 (in1, src1); + src1 += 4; + v20 = 1; + } + m_output[dst+x] = B; + m_output[dst+x+1] = G; + m_output[dst+x+2] = R; + m_first[0] = B; + m_first[1] = G; + m_first[2] = R; + } + m_first[0] = m_output[dst]; + m_first[1] = m_output[dst+1]; + m_first[2] = m_output[dst+2]; + } + if (m_alpha != null) + ApplyAlpha(); + } + + private void ApplyAlpha () + { + int src = 0; + for (int i = 3; i < m_output.Length; i += 4) + { + int alpha = Math.Min (m_alpha[src++]*0xff/0x40, 0xff); + m_output[i] = (byte)alpha; + } + } + + private static void RleDecode (BinaryReader src, byte[] dst_buf) + { + int remaining = dst_buf.Length; + int dst = 0; + while (remaining > 0) + { + byte v = src.ReadByte(); + int count; + if (0 != (v & 0x80)) + { + v &= 0x7F; + count = src.ReadUInt16(); + for (int j = 0; j < count; ++j) + { + dst_buf[dst++] = v; + } + } + else + { + dst_buf[dst++] = v; + count = 1; + } + remaining -= count; + } + } + } +} diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs index 84c5a12c..1e012171 100644 --- a/ArcFormats/Properties/AssemblyInfo.cs +++ b/ArcFormats/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.0.4.48")] -[assembly: AssemblyFileVersion ("1.0.4.48")] +[assembly: AssemblyVersion ("1.0.4.49")] +[assembly: AssemblyFileVersion ("1.0.4.49")]