diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index c5a51c084..e154c3596 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -281,6 +281,7 @@
+
diff --git a/ArcFormats/Silky/ImageAKB.cs b/ArcFormats/Silky/ImageAKB.cs
index a6f28da15..b1186ff3f 100644
--- a/ArcFormats/Silky/ImageAKB.cs
+++ b/ArcFormats/Silky/ImageAKB.cs
@@ -40,6 +40,7 @@ internal class AkbMetaData : ImageMetaData
public uint Flags;
public string BaseFileName;
public uint DataOffset;
+ public bool TopDownOrder;
}
[Export(typeof(ImageFormat))]
@@ -150,13 +151,34 @@ public byte[] Unpack (byte[] background = null)
var pixels = new byte[m_info.InnerHeight * inner_stride];
using (var lz = new LzssStream (m_input, LzssMode.Decompress, true))
{
- for (int pos = pixels.Length - inner_stride; pos >= 0; pos -= inner_stride)
+ if (m_info.TopDownOrder)
{
- if (inner_stride != lz.Read (pixels, pos, inner_stride))
+ if (lz.Read(pixels, 0, pixels.Length) != pixels.Length)
+ {
throw new InvalidFormatException();
+ }
+ }
+ else
+ {
+ for (int pos = pixels.Length - inner_stride; pos >= 0; pos -= inner_stride)
+ {
+ if (inner_stride != lz.Read (pixels, pos, inner_stride))
+ throw new InvalidFormatException();
+ }
}
+
}
RestoreDelta (pixels, inner_stride);
+ if (m_info.TopDownOrder)
+ {
+ var tmp_stride = new byte[inner_stride];
+ for (int i = 0, j = m_info.InnerHeight - 1; i < j; ++i, --j)
+ {
+ Buffer.BlockCopy(pixels, j * inner_stride, tmp_stride, 0, inner_stride);
+ Buffer.BlockCopy(pixels, i * inner_stride, pixels, j * inner_stride, inner_stride);
+ Buffer.BlockCopy(tmp_stride, 0, pixels, i * inner_stride, inner_stride);
+ }
+ }
if (null == background && m_info.InnerWidth == m_info.Width && m_info.InnerHeight == m_info.Height)
return pixels;
diff --git a/ArcFormats/Silky/ImageMMO.cs b/ArcFormats/Silky/ImageMMO.cs
new file mode 100644
index 000000000..0846f0687
--- /dev/null
+++ b/ArcFormats/Silky/ImageMMO.cs
@@ -0,0 +1,46 @@
+using System.ComponentModel.Composition;
+
+// [070223][Silky's] Himekishi Angelica ~Anata tte, Hontou ni Saitei no Kuzu Da wa!~
+
+namespace GameRes.Formats.Silky
+{
+ [Export(typeof(ImageFormat))]
+ public class MmoFormat : AkbFormat
+ {
+ public override string Tag => "MMO";
+ public override string Description => "Silky's MMO image format";
+ public override uint Signature => 0x204F4D4D; // 'MMO '
+
+ public MmoFormat()
+ {
+ Signatures = new[] { Signature };
+ }
+
+ public override ImageMetaData ReadMetaData(IBinaryStream file)
+ {
+ file.Position += 4;
+ var metaData = new AkbMetaData();
+ metaData.TopDownOrder = true;
+ metaData.Flags = 0;
+ metaData.OffsetX = file.ReadInt32();
+ metaData.OffsetY = file.ReadInt32();
+ metaData.InnerWidth = file.ReadInt32() - metaData.OffsetX;
+ metaData.InnerHeight = file.ReadInt32() - metaData.OffsetY;
+ // Not sure if there are any other pixel encoding
+ metaData.BPP = 24;
+ // Skip 8 byte unknown field
+ file.Position += 8;
+ metaData.Width = file.ReadUInt32();
+ metaData.Height = file.ReadUInt32();
+ // Skip another 4 byte unknown field
+ file.Position += 4;
+ metaData.DataOffset = (uint)file.Position;
+ // FIXME: Implement reliable way to detect difference image and find the corresponding base image name Find base image
+ // Unlike AKB format, MMO format has neither mark for difference image nor the name of its base image file embedded.
+ // Also, there doesn't seem to have a constant naming pattern even in the same game.
+ metaData.BaseFileName = null;
+ metaData.Background = new byte[4];
+ return metaData;
+ }
+ }
+}
\ No newline at end of file