From c75cd84b4239aa5219ee5e14112a1735608c84c8 Mon Sep 17 00:00:00 2001 From: morkt Date: Wed, 20 Apr 2016 17:54:52 +0400 Subject: [PATCH] implemented variation of CPB image format. --- ArcFormats/AZSys/ImageCPB.cs | 144 +++++++++++++++++++++++++++++------ supported.html | 2 + 2 files changed, 124 insertions(+), 22 deletions(-) diff --git a/ArcFormats/AZSys/ImageCPB.cs b/ArcFormats/AZSys/ImageCPB.cs index 35c7d327..03b3a33c 100644 --- a/ArcFormats/AZSys/ImageCPB.cs +++ b/ArcFormats/AZSys/ImageCPB.cs @@ -2,7 +2,7 @@ //! \date Wed Apr 22 11:08:13 2015 //! \brief AZ system image format implementation. // -// Copyright (C) 2015 by morkt +// Copyright (C) 2015-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 @@ -30,12 +30,16 @@ using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using GameRes.Compression; +using GameRes.Utility; namespace GameRes.Formats.AZSys { internal class CpbMetaData : ImageMetaData { - public uint[] Channel = new uint[4]; + public int Type; + public int Version; + public uint[] Channel = new uint[4]; + public uint DataOffset; } [Export(typeof(ImageFormat))] @@ -52,38 +56,51 @@ namespace GameRes.Formats.AZSys public override ImageMetaData ReadMetaData (Stream stream) { - stream.Seek (5, SeekOrigin.Current); + stream.Seek (4, SeekOrigin.Current); + int type = stream.ReadByte(); int bpp = stream.ReadByte(); if (24 != bpp && 32 != bpp) throw new NotSupportedException ("Not supported CPB image format"); using (var input = new ArcView.Reader (stream)) { int version = input.ReadInt16 (); - if (1 != version) + if (1 != version && 0 != version) throw new NotSupportedException ("Not supported CPB image version"); - var info = new CpbMetaData (); - info.BPP = bpp; - input.ReadUInt32(); - info.Width = input.ReadUInt16(); - info.Height = input.ReadUInt16(); - info.Channel[0] = input.ReadUInt32(); // Alpha - info.Channel[1] = input.ReadUInt32(); - info.Channel[2] = input.ReadUInt32(); - info.Channel[3] = input.ReadUInt32(); + var info = new CpbMetaData { + Type = type, + Version = version, + BPP = bpp, + }; + if (1 == version) + { + input.ReadUInt32(); + info.Width = input.ReadUInt16(); + info.Height = input.ReadUInt16(); + info.Channel[0] = input.ReadUInt32(); + info.Channel[1] = input.ReadUInt32(); + info.Channel[2] = input.ReadUInt32(); + info.Channel[3] = input.ReadUInt32(); + } + else + { + info.Width = input.ReadUInt16(); + info.Height = input.ReadUInt16(); + input.ReadUInt32(); + info.Channel[0] = input.ReadUInt32(); + info.Channel[1] = input.ReadUInt32(); + info.Channel[2] = input.ReadUInt32(); + info.Channel[3] = input.ReadUInt32(); + } + info.DataOffset = (uint)stream.Position; return info; } } public override ImageData Read (Stream stream, ImageMetaData info) { - var meta = info as CpbMetaData; - if (null == meta) - throw new ArgumentException ("CpbFormat.Read should be supplied with CpbMetaData", "info"); - - stream.Position = 0x20; - var reader = new Reader (stream, meta); + var reader = new Reader (stream, (CpbMetaData)info); reader.Unpack(); - return ImageData.Create (meta, reader.Format, reader.Palette, reader.Data); + return ImageData.Create (info, reader.Format, reader.Palette, reader.Data); } internal class Reader @@ -94,6 +111,7 @@ namespace GameRes.Formats.AZSys Stream m_input; byte[] m_output; uint[] m_channel; + CpbMetaData m_info; public PixelFormat Format { get; private set; } public BitmapPalette Palette { get; private set; } @@ -103,6 +121,7 @@ namespace GameRes.Formats.AZSys { m_width = (int)info.Width; m_height = (int)info.Height; + m_info = info; m_bpp = info.BPP; m_channel = info.Channel; m_output = new byte[m_width * m_height * 4]; @@ -113,12 +132,32 @@ namespace GameRes.Formats.AZSys else Format = PixelFormats.Bgra32; m_input = input; + m_input.Position = info.DataOffset; + + if (1 == m_info.Version) + { + StreamMap = new byte[] { 0, 3, 1, 2 }; + ChannelMap = new byte[] { 3, 0, 1, 2 }; + } + else + { + StreamMap = new byte[] { 0, 1, 2, 3 }; + ChannelMap = new byte[] { 2, 1, 0, 3 }; + } } - static byte[] StreamMap = new byte[] { 0, 3, 1, 2 }; - static byte[] ChannelMap = new byte[] { 3, 0, 1, 2 }; + byte[] StreamMap; + byte[] ChannelMap; public void Unpack () + { + if (0 == m_info.Version && 3 == m_info.Type) + UnpackV3(); + else + UnpackV0(); + } + + void UnpackV0 () { byte[] channel = new byte[m_width*m_height]; long start_pos = m_input.Position; @@ -140,6 +179,67 @@ namespace GameRes.Formats.AZSys start_pos += m_channel[StreamMap[i]]; } } + + void UnpackV3 () + { + byte[] channel = new byte[m_width*m_height]; + long start_pos = m_input.Position; + for (int i = 0; i < 4; ++i) + { + int packed_size = (int)m_channel[StreamMap[i]]; + if (0 == packed_size) + continue; + m_input.Position = start_pos; + int channel_size = Decompress (packed_size, channel); + int dst = ChannelMap[i]; + for (int j = 0; j < channel_size; ++j) + { + m_output[dst] = channel[j]; + dst += 4; + } + start_pos += packed_size; + } + } + + int Decompress (int input_size, byte[] output) + { + var input = new byte[input_size]; + if (input_size != m_input.Read (input, 0, input_size)) + throw new EndOfStreamException(); + int src1 = 0x14; + int src2 = src1 + LittleEndian.ToInt32 (input, 4); + int src3 = src2 + LittleEndian.ToInt32 (input, 8); + int remaining = LittleEndian.ToInt32 (input, 0x10); + int dst = 0; + int mask = 0x80; + while (remaining > 0) + { + int count; + if (0 != (mask & input[src1])) + { + int offset = LittleEndian.ToUInt16 (input, src2); + src2 += 2; + count = (offset >> 13) + 3; + offset = (offset & 0x1FFF) + 1; + Binary.CopyOverlapped (output, dst-offset, dst, count); + } + else + { + count = input[src3++] + 1; + Buffer.BlockCopy (input, src3, output, dst, count); + src3 += count; + } + dst += count; + remaining -= count; + mask >>= 1; + if (0 == mask) + { + ++src1; + mask = 0x80; + } + } + return dst; + } } } } diff --git a/supported.html b/supported.html index 8039896a..a2b8e67d 100644 --- a/supported.html +++ b/supported.html @@ -358,6 +358,7 @@ Shuffle! Essence+
*.elgELGNo *.arcARC\x1a
ARCNoAZ System +Clover Heart's
Triptych
Tsurugi Otome Noah
@@ -784,6 +785,7 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-
Sweet ~Hanjuku na Tenshi-tachi~
*.ovk-NoRealLive +Joi-san no Iitsuke!!
Shiawase na Ohime-sama
*.nwa-No