diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 02694a65..2f2be9ba 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -72,6 +72,7 @@ + diff --git a/ArcFormats/Banana/ArcPK.cs b/ArcFormats/Banana/ArcPK.cs index 654324bd..243398f6 100644 --- a/ArcFormats/Banana/ArcPK.cs +++ b/ArcFormats/Banana/ArcPK.cs @@ -43,7 +43,7 @@ namespace GameRes.Formats.Banana // namespace is arbitrary, actual format source public PkOpener () { - Extensions = new string[] { "pk" }; + Extensions = new string[] { "pk", "dat" }; } public override ArcFile TryOpen (ArcView file) diff --git a/ArcFormats/Banana/ImageGEC.cs b/ArcFormats/Banana/ImageGEC.cs new file mode 100644 index 00000000..941036b8 --- /dev/null +++ b/ArcFormats/Banana/ImageGEC.cs @@ -0,0 +1,340 @@ +//! \file ImageGEC.cs +//! \date Mon Jun 20 15:45:46 2016 +//! \brief Yellow Pig 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.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using GameRes.Utility; + +namespace GameRes.Formats.YellowPig +{ + internal class GecMetaData : ImageMetaData + { + public byte Type; + public int DataOffset; + public int AlphaOffset; + } + + [Export(typeof(ImageFormat))] + public class GecFormat : ImageFormat + { + public override string Tag { get { return "GEC"; } } + public override string Description { get { return "Yellow Pig image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x11]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + byte type = header[0]; + if (type != 0 && type != 1) + return null; + var info = new GecMetaData + { + Type = type, + OffsetX = LittleEndian.ToInt16 (header, 1), + OffsetY = LittleEndian.ToInt16 (header, 3), + Width = LittleEndian.ToUInt16 (header, 5), + Height = LittleEndian.ToUInt16 (header, 7), + BPP = 0 == type ? 24 : 32, + AlphaOffset = LittleEndian.ToInt32 (header, 9), + DataOffset = LittleEndian.ToInt32 (header, 0xD), + }; + if (info.OffsetX < 0 || info.OffsetY < 0 || info.Width <= 0 || info.Height <= 0 + || info.DataOffset < 0 || info.DataOffset > stream.Length) + return null; + if (1 == type && info.AlphaOffset <= 0) + return null; + return info; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var reader = new GecReader (stream, (GecMetaData)info); + reader.Unpack(); + return ImageData.CreateFlipped (info, reader.Format, null, reader.Data, reader.Stride); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("GecFormat.Write not implemented"); + } + } + + internal sealed class GecReader + { + byte[] m_input; + byte[] m_output; + GecMetaData m_info; + + public PixelFormat Format { get; private set; } + public int Stride { get; private set; } + public byte[] Data { get { return m_output; } } + + public GecReader (Stream input, GecMetaData info) + { + m_input = new byte[input.Length]; + input.Read (m_input, 0, m_input.Length); + m_info = info; + } + + int m_bits = 0; + int m_bits_src = 0; + int m_bits_count = 0; + + public void Unpack () + { + if (0 == m_info.Type) + { + UnpackPixels (0x11); + Format = PixelFormats.Bgr24; + Stride = (int)m_info.Width * 3; + } + else + { + UnpackPixels (0x1D); + int bits = 0x1D + m_info.AlphaOffset; + m_alpha_width = LittleEndian.ToUInt16 (m_input, 0x15); + m_alpha_height = LittleEndian.ToUInt16 (m_input, 0x17); + int data = bits + LittleEndian.ToInt32 (m_input, 0x19); + var alpha = UnpackAlpha (bits, data); + ApplyAlpha (alpha); + Format = PixelFormats.Bgra32; + Stride = (int)m_info.Width * 4; + } + } + + int m_alpha_width; + int m_alpha_height; + + void ApplyAlpha (byte[] alpha) + { + var image = new byte[m_info.Width * m_info.Height * 4]; + int src = 0; + int a_y = m_alpha_height - (int)m_info.Height - m_info.OffsetY; + int a_src = a_y * m_alpha_width + m_info.OffsetX; + int dst = 0; + for (uint y = 0; y < m_info.Height; ++y) + { + for (uint x = 0; x < m_info.Width; ++x) + { + image[dst++] = m_output[src++]; + image[dst++] = m_output[src++]; + image[dst++] = m_output[src++]; + image[dst++] = alpha[a_src+x]; + } + a_src += m_alpha_width; + } + m_output = image; + } + + int m_dst; + byte[] m_table = new byte[0x100]; + + void UnpackPixels (int bits_src) + { + m_bits_src = bits_src; + m_bits_count = 0; + m_output = new byte[(int)m_info.Width * (int)m_info.Height * 3]; + int src = bits_src + m_info.DataOffset; + for (int j = 0; j < 0x100; ++j) + m_table[j] = (byte)j; + byte[] frame1 = new byte[0x10002]; + byte[] frame2 = new byte[0x10002]; + m_dst = 0; + while (m_dst < m_output.Length) + { + int count = Math.Min (m_output.Length - m_dst, 0xFFFF); + if (GetNextBit() != 0) + { + ReadFrame (frame1, count + 2); + UnpackFrame1 (frame1, frame2, count + 2); + UnpackFrame2 (frame2, 2, count, LittleEndian.ToUInt16 (frame2, 0)); + } + else + { + src = UnpackRLE (src, count); + } + } + } + + byte[] UnpackAlpha (int bits_src, int data_src) + { + m_bits_src = bits_src; + m_bits_count = 0; + var alpha = new byte[m_alpha_height * m_alpha_width]; + int dst = 0; + while (dst < alpha.Length) + { + if (GetNextBit() != 0) + { + int count = GetInt(); + byte v = m_input[data_src++]; + while (count --> 0) + alpha[dst++] = v; + } + else + { + alpha[dst++] = m_input[data_src++]; + } + } + return alpha; + } + + void ReadFrame (byte[] frame, int count) // sub_423990 + { + int j = 0; + while (j < count) + { + if (0 != GetNextBit()) + { + frame[j++] = (byte)GetInt(); + } + else + { + int n = GetInt(); + while (n --> 0) + frame[j++] = 0; + } + } + } + + void UnpackFrame1 (byte[] frame, byte[] dst, int count) // sub_423670 + { + byte prev = 1; + int n = 0; + while (count --> 0) + { + byte v8 = frame[n]; + byte v9 = m_table[v8]; + if (1 == v8) + { + if (prev != 0) + { + m_table[1] = m_table[0]; + m_table[0] = v9; + } + } + else if (v8 > 1) + { + Buffer.BlockCopy (m_table, 1, m_table, 2, v8 - 1); + m_table[1] = v9; + } + dst[n++] = v9; + prev = v8; + } + } + + ushort[] table1 = new ushort[0x100]; + ushort[] table2 = new ushort[0x100]; + ushort[] table3 = new ushort[0x10002]; + + void UnpackFrame2 (byte[] frame, int src, int count, ushort first) // sub_4236F0 + { + for (int i = 0; i < 0x100; ++i) + table1[i] = 0; + for (int i = 0; i < count; ++i) + ++table1[frame[src+i]]; + ushort v = 0; + for (int i = 0; i < 0x100; i += 1) + { + table2[i] = v; + v += table1[i]; + table1[i] = 0; + } + for (int i = 0; i < count; ++i) + { + int d = frame[src+i]; + ushort a = table2[d]; + ushort b = table1[d]++; + table3[a + b] = (ushort)i; + } + ushort next = table3[first]; + while (count --> 0) + { + m_output[m_dst++] = frame[src+next]; + next = table3[next]; + } + } + + int UnpackRLE (int src, int count) // sub_423A10 + { + while (count > 0) + { + if (0 == GetNextBit()) + { + m_output[m_dst++] = m_input[src++]; + m_output[m_dst++] = m_input[src++]; + m_output[m_dst++] = m_input[src++]; + count -= 3; + } + else + { + int n = GetInt() * 3; + m_output[m_dst] = m_input[src++]; + m_output[m_dst+1] = m_input[src++]; + m_output[m_dst+2] = m_input[src++]; + Binary.CopyOverlapped (m_output, m_dst, m_dst+3, n-3); + m_dst += n; + count -= n; + } + } + return src; + } + + int GetInt () // sub_423810 + { + int count = 0; + while (0 == GetNextBit()) + { + ++count; + } + int v = 1; + while (count > 0) + { + v = (v << 1) | GetNextBit(); + --count; + } + return v; + } + + int GetNextBit () + { + if (m_bits_count-- <= 0) + { + m_bits = LittleEndian.ToInt32 (m_input, m_bits_src); + m_bits_src += 4; + m_bits_count = 31; + } + else + { + m_bits >>= 1; + } + return m_bits & 1; + } + } +} diff --git a/supported.html b/supported.html index d92c451d..a841c50e 100644 --- a/supported.html +++ b/supported.html @@ -99,10 +99,13 @@ Okaa-san ga Ippai!
*.pakHyPackYesKogadoSymphonic Rain *+*.lst-NoNexton LikeC Chikan Ou ~Inkoku no Souzousha~
+Daisaimin Rankou Gakuen
Injoku Shinryuu Club
Moon.
Ryoujoku Famiresu Choukyou Menu
Ryoujoku Idol Mesu Dorei
+Sei Dorei Gakuen
+Seidorei Onna Kyoushi
Sentoreido Gakuen
Yakuchu!
@@ -735,11 +738,13 @@ Izumo 3
*.pbxPandora.boxNoTerios Ikinari Happy Bell
-*.pk-NoBANANA Shu-Shu +*.pk
*.dat-NoBANANA Shu-Shu
Yellow Pig Tama Tama ~Tonari no Kanojo...
Tama Tama Christmas Box
+Otto no Mae de Okasarete...
*.mag-No +*.gec-No *.arc-NoAI6WIN Gakuen Saimin Reido