diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 2eee4cbb..eb8ea731 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -113,6 +113,8 @@ + + diff --git a/ArcFormats/Ivory/ImageMMD.cs b/ArcFormats/Ivory/ImageMMD.cs new file mode 100644 index 00000000..97de8fe7 --- /dev/null +++ b/ArcFormats/Ivory/ImageMMD.cs @@ -0,0 +1,155 @@ +//! \file ImageMMD.cs +//! \date Thu Sep 08 18:23:14 2016 +//! \brief Ivory compressed 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 System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Ivory +{ + internal class MmdMetaData : ImageMetaData + { + public int Colors; + public int Size1; + public int Size2; + public int Size3; + } + + [Export(typeof(ImageFormat))] + public class MmdFormat : ImageFormat + { + public override string Tag { get { return "MOE/MMD"; } } + public override string Description { get { return "Ivory image format"; } } + public override uint Signature { get { return 0x1A444D4D; } } // 'MMD' + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x18]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + var info = new MmdMetaData + { + Width = LittleEndian.ToUInt16 (header, 4), + Height = LittleEndian.ToUInt16 (header, 6), + BPP = 8, + Size1 = LittleEndian.ToInt32 (header, 8), + Size2 = LittleEndian.ToInt32 (header, 0x0C), + Size3 = LittleEndian.ToInt32 (header, 0x10), + Colors = LittleEndian.ToInt32 (header, 0x14), + }; + if (info.Size1 <= 0 || info.Size2 <= info.Size1 || info.Size3 <= 0) + return null; + return info; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = (MmdMetaData)info; + var pixels = new byte[info.Width * info.Height]; + stream.Position = 0x18; + using (var input = new ArcView.Reader (stream)) + { + var buf1 = input.ReadBytes (meta.Size1); + var buf2 = input.ReadBytes (meta.Size2 - meta.Size1); + int w = (int)info.Width / 4; + var line = new byte[w]; + int mask = 0x80; + int b1 = 0; + int b2 = 0; + int dst = 0; + for (int y = (int)info.Height; y > 0; --y) + { + for (int x = 0; x < w; ++x) + { + if (0 != (mask & buf1[b1])) + { + line[x] ^= buf2[b2++]; + } + mask >>= 1; + if (0 == mask) + { + mask = 0x80; + ++b1; + } + byte p = line[x]; + int q = p >> 4; + for (int j = 0; j < 2; ++j) + { + if (0 != q) + { + int offset = ShiftTable[q + 16] + (int)info.Width * ShiftTable[q]; + int src = dst - offset; + pixels[dst++] = pixels[src]; + pixels[dst++] = pixels[src+1]; + } + else + { + input.Read (pixels, dst, 2); + dst += 2; + } + q = p & 0xF; + } + } + } + } + stream.Position = 0x18 + meta.Size2 + meta.Size3; + var palette = ReadPalette (stream, meta.Colors); + return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels); + } + + static readonly byte[] ShiftTable = { + 0, 0, 0, 0, 1, 1, 2, 2, 2, 4, 4, 4, 8, 8, 8, 16, + 0, 2, 4, 8, 0, 2, 0, 2, 4, 0, 2, 4, 0, 2, 4, 0, + }; + + BitmapPalette ReadPalette (Stream input, int colors) + { + int palette_size = colors * 3; + var palette_data = new byte[Math.Max (palette_size, 0x300)]; + if (palette_size != input.Read (palette_data, 0, palette_size)) + throw new EndOfStreamException(); + var palette = new Color[0x100]; + if (colors > 0x100) + colors = 0x100; + int src = 0; + for (int i = 0; i < palette.Length; ++i) + { + byte r = palette_data[src++]; + byte g = palette_data[src++]; + byte b = palette_data[src++]; + palette[i] = Color.FromRgb (r, g, b); + } + return new BitmapPalette (palette); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("MmdFormat.Write not implemented"); + } + } +} diff --git a/ArcFormats/Ivory/ImageMOE.cs b/ArcFormats/Ivory/ImageMOE.cs new file mode 100644 index 00000000..e51142de --- /dev/null +++ b/ArcFormats/Ivory/ImageMOE.cs @@ -0,0 +1,112 @@ +//! \file ImageMOE.cs +//! \date Thu Sep 08 17:56:51 2016 +//! \brief Ivory 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.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using GameRes.Utility; + +namespace GameRes.Formats.Ivory +{ + [Export(typeof(ImageFormat))] + public class MoeFormat : ImageFormat + { + public override string Tag { get { return "MOE"; } } + public override string Description { get { return "Ivory image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (Stream stream) + { + var wh = FormatCatalog.ReadSignature (stream); + uint width = wh & 0xFFFF; + uint height = wh >> 16; + if (0 == width || width > 800 || 0 == height || height > 600) + return null; + if (!IsValidInput (stream, width, height)) + return null; + return new ImageMetaData { Width = width, Height = height, BPP = 24 }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + stream.Position = 4; + var pixels = new byte[3 * info.Width * info.Height]; + int dst = 0; + while (dst < pixels.Length) + { + int count = stream.ReadByte(); + if (-1 == count) + throw new EndOfStreamException(); + if (0 != (count & 0x80)) + { + count = 3 * (count & 0x7F); + stream.Read (pixels, dst, count); + dst += count; + } + else + { + count *= 3; + stream.Read (pixels, dst, 3); + Binary.CopyOverlapped (pixels, dst, dst+3, count-3); + dst += count; + } + } + return ImageData.Create (info, PixelFormats.Bgr24, null, pixels); + } + + /// + /// Try to interpret input stream as a compressed image. + /// + bool IsValidInput (Stream input, uint width, uint height) + { + int total = (int)width * (int)height; + int dst = 0; + while (dst < total) + { + int count = input.ReadByte(); + if (-1 == count) + return false; + if (0 != (count & 0x80)) + { + count &= 0x7F; + input.Seek (count * 3, SeekOrigin.Current); + } + else + { + input.Seek (3, SeekOrigin.Current); + } + dst += count; + if (dst > total) + return false; + } + return true; + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("MoeFormat.Write not implemented"); + } + } +} diff --git a/supported.html b/supported.html index 8ce44fed..d2ec0cd1 100644 --- a/supported.html +++ b/supported.html @@ -105,6 +105,7 @@ Daisaimin Rankou Gakuen
Haitoku no Gakuen ~Yami ni Sasagerareta Otome-tachi~
Haitoku no Gakuen 2 ~Yami o Tsugu Mono~
Injoku Shinryuu Club
+Mashou no Nie 2
Moon.
Ryoujoku Famiresu Choukyou Menu
Ryoujoku Idol Mesu Dorei
@@ -175,6 +176,7 @@ Tsukihime
Umineko
*.pacPACNoNeXAS +Aqua Blue
Baldr Sky DiveX
Fossette ~Cafe au Le Ciel Bleu~
Jinki Extend Re:Vision
@@ -237,6 +239,7 @@ Damegane
Distance
ExE
Fair Child
+Fantasical
Fate/stay night
Fate/hollow ataraxia
G-senjou no Maou
@@ -324,6 +327,7 @@ Shoujo Senki Soul Eater
*.dat-NoM no Violet
Snack Factory
Nanase Ren
Imouto ~Mitsutsubo Complete Edition~
+Ren no Koi
*gra
mas
difNo *.ald-NoAlice Soft @@ -388,6 +392,7 @@ Enkaku Sousa 2.36 or 2.37
Gohoushi Nurse ~Mayonaka no Kyousei Call~ ShiinaRio v2.50
Helter Skelter ShiinaRio v2.40
Hitozuma Onna Kyoushi Reika ShiinaRio v2.39
+Intruder ShiinaRio v2.49
Itsuka, Dokoka de ~Ano Ameoto no Kioku~2.36 or 2.37
Kichiku Nakadashi SuieibuShiinaRio v2.41
Mahou Shoujo no Taisetsu na Koto ShiinaRio v2.47
@@ -402,6 +407,7 @@ RinĂ—Sen ShiinaRio v2.47
Sabae no Ou ShiinaRio v2.36
Shinigami no Testament ShiinaRio v2.49
Shojo MamaShiinaRio v2.49
+Sorcery JokersShiinaRio v2.50
Tantei Shounen Asome old version, like 2.twenty-something
Tekoire Princess!ShiinaRio v2.37
Wana ~Hakudaku Mamire no Houkago~ShiinaRio v2.36
@@ -613,6 +619,7 @@ Tutorial Summer
*.wbpARCFORM3 WBUG
ARCFORM4 WBUGNoWild Bug Happy Planning
Ryoujoku Hitozuma Club
+Sister Angel
Yuukyou Gangu 2
*.wbmWPX\x1ABMPNo @@ -1092,6 +1099,9 @@ Inumimi Berserk
*.kgpGRPHNo *.kslKSLMNo +*.moe-
MMD\x1ANoIvory +Triangle Heart
+

1 Non-encrypted only