From 2d22dbe9465e5a00f4b8cf0a0d39ddd26e160bc2 Mon Sep 17 00:00:00 2001 From: morkt Date: Sat, 1 Aug 2015 00:24:04 +0400 Subject: [PATCH] implemented incremental compression of S25 images. --- ArcFormats/ArcS25.cs | 1 + ArcFormats/ImageS25.cs | 328 ++++++++++++++++++++++++++++++----------- 2 files changed, 240 insertions(+), 89 deletions(-) diff --git a/ArcFormats/ArcS25.cs b/ArcFormats/ArcS25.cs index 1bb52278..637444f3 100644 --- a/ArcFormats/ArcS25.cs +++ b/ArcFormats/ArcS25.cs @@ -96,6 +96,7 @@ namespace GameRes.Formats.ShiinaRio OffsetY = arc.File.View.ReadInt32 (offset+12), BPP = 32, FirstOffset = (uint)(offset + 0x14), + Incremental = 0 != (arc.File.View.ReadUInt32 (offset+0x10) & 0x80000000u), }; using (var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset)) using (var reader = new S25Format.Reader (input, info)) diff --git a/ArcFormats/ImageS25.cs b/ArcFormats/ImageS25.cs index e0db0f8d..91e12b9a 100644 --- a/ArcFormats/ImageS25.cs +++ b/ArcFormats/ImageS25.cs @@ -37,6 +37,7 @@ namespace GameRes.Formats.ShiinaRio internal class S25MetaData : ImageMetaData { public uint FirstOffset; + public bool Incremental; } [Export(typeof(ImageFormat))] @@ -67,6 +68,7 @@ namespace GameRes.Formats.ShiinaRio info.OffsetX = input.ReadInt32(); info.OffsetY = input.ReadInt32(); info.FirstOffset = first_offset+0x14; + info.Incremental = 0 != (input.ReadUInt32() & 0x80000000u); info.BPP = 32; return info; } @@ -96,8 +98,8 @@ namespace GameRes.Formats.ShiinaRio int m_width; int m_height; uint m_origin; - uint[] m_rows; byte[] m_output; + bool m_incremental; public byte[] Data { get { return m_output; } } @@ -105,111 +107,259 @@ namespace GameRes.Formats.ShiinaRio { m_width = (int)info.Width; m_height = (int)info.Height; - m_rows = new uint[m_height]; m_output = new byte[m_width * m_height * 4]; m_input = new ArcView.Reader (file); m_origin = info.FirstOffset; + m_incremental = info.Incremental; } public byte[] Unpack () { m_input.BaseStream.Position = m_origin; - for (int i = 0; i < m_rows.Length; ++i) - m_rows[i] = m_input.ReadUInt32(); + if (m_incremental) + return UnpackIncremental(); + var rows = new uint[m_height]; + for (int i = 0; i < rows.Length; ++i) + rows[i] = m_input.ReadUInt32(); + var row_buffer = new byte[m_width]; int dst = 0; - int stride = m_width * 4; - for (int y = 0; y < m_height && dst != m_output.Length; ++y) + for (int y = 0; y < m_height && dst < m_output.Length; ++y) { - byte b, g, r, a; - uint row_pos = m_rows[y]; + uint row_pos = rows[y]; m_input.BaseStream.Position = row_pos; - m_input.ReadUInt16(); - for (int x = m_width; x > 0 && dst != m_output.Length; ) + int row_length = m_input.ReadUInt16(); + row_pos += 2; + if (0 != (row_pos & 1)) { - if (0 != (row_pos & 1)) - { - m_input.ReadByte(); - ++row_pos; - } - int count = m_input.ReadUInt16(); - row_pos += 2; - int method = count >> 13; - int skip = (count & 0x1800) >> 11; - if (0 != skip) - { - m_input.BaseStream.Seek (skip, SeekOrigin.Current); - row_pos += (uint)skip; - } - count &= 0x7ff; - if (count > x) count = x; - x -= count; - if (0 == method || 1 == method) - { - dst += count * 4; - } - else if (2 == method) - { - for (int i = 0; i < count; ++i) - { - m_output[dst++] = m_input.ReadByte(); - m_output[dst++] = m_input.ReadByte(); - m_output[dst++] = m_input.ReadByte(); - m_output[dst++] = 0xff; - row_pos += 3; - } - } - else if (3 == method) - { - b = m_input.ReadByte(); - g = m_input.ReadByte(); - r = m_input.ReadByte(); - row_pos += 3; - for (int i = 0; i < count; ++i) - { - m_output[dst++] = b; - m_output[dst++] = g; - m_output[dst++] = r; - m_output[dst++] = 0xff; - } - } - else if (4 == method) - { - for (int i = 0; i < count; ++i) - { - a = m_input.ReadByte(); - m_output[dst] += (byte)((m_input.ReadByte() - m_output[dst]) * a / 256); - ++dst; - m_output[dst] += (byte)((m_input.ReadByte() - m_output[dst]) * a / 256); - ++dst; - m_output[dst] += (byte)((m_input.ReadByte() - m_output[dst]) * a / 256); - ++dst; - m_output[dst++] = a; - row_pos += 4; - } - } - else - { - row_pos += 4; - a = m_input.ReadByte(); - b = m_input.ReadByte(); - g = m_input.ReadByte(); - r = m_input.ReadByte(); - for (int i = 0; i < count; ++i) - { - m_output[dst] += (byte)((b - m_output[dst]) * a / 256); - ++dst; - m_output[dst] += (byte)((g - m_output[dst]) * a / 256); - ++dst; - m_output[dst] += (byte)((r - m_output[dst]) * a / 256); - ++dst; - m_output[dst++] = a; - } - } + m_input.ReadByte(); + --row_length; } + if (row_buffer.Length < row_length) + row_buffer = new byte[row_length]; + m_input.Read (row_buffer, 0, row_length); + dst = UnpackLine (row_buffer, dst); } return m_output; } + void UpdateRepeatCount (Dictionary rows_count) + { + m_input.BaseStream.Position = 4; + int count = m_input.ReadInt32(); + var frames = new List (count); + for (int i = 0; i < count; ++i) + { + var offset = m_input.ReadUInt32(); + if (0 != offset) + frames.Add (offset); + } + foreach (var offset in frames) + { + if (offset+0x14 == m_origin) + continue; + m_input.BaseStream.Position = offset+4; + int height = m_input.ReadInt32(); + m_input.BaseStream.Position = offset+0x14; + for (int i = 0; i < height; ++i) + { + var row_offset = m_input.ReadUInt32(); + if (rows_count.ContainsKey (row_offset)) + ++rows_count[row_offset]; + } + } + } + + byte[] UnpackIncremental () + { + var rows = new uint[m_height]; + var rows_count = new Dictionary (m_height); + for (int i = 0; i < rows.Length; ++i) + { + uint offset = m_input.ReadUInt32(); + rows[i] = offset; + if (rows_count.ContainsKey (offset)) + ++rows_count[offset]; + else + rows_count[offset] = 1; + } + UpdateRepeatCount (rows_count); + var input_rows = new Dictionary (m_height); + var input_lines = new byte[m_height][]; + for (int y = 0; y < m_height; ++y) + { + uint row_pos = rows[y]; +// if (183 == y) +// System.Diagnostics.Debugger.Break(); +// if (0x82 == y) +// System.Diagnostics.Debugger.Break(); + if (input_rows.ContainsKey (row_pos)) + { + input_lines[y] = input_rows[row_pos]; + continue; + } + var row = ReadLine (row_pos, rows_count[row_pos]); + input_rows[row_pos] = row; + input_lines[y] = row; + } + int dst = 0; + foreach (var line in input_lines) + { + dst = UnpackLine (line, dst); + } + return m_output; + } + + int UnpackLine (byte[] line, int dst) + { + int row_pos = 0; + for (int x = m_width; x > 0 && dst < m_output.Length && row_pos < line.Length; ) + { + if (0 != (row_pos & 1)) + { + ++row_pos; + } + int count = LittleEndian.ToUInt16 (line, row_pos); + row_pos += 2; + int method = count >> 13; + int skip = (count >> 11) & 3; + if (0 != skip) + { + row_pos += skip; + } + count &= 0x7ff; + if (0 == count) + { + count = LittleEndian.ToInt32 (line, row_pos); + row_pos += 4; + } + if (count > x) count = x; + x -= count; + byte b, g, r, a; + + switch (method) + { + case 2: + for (int i = 0; i < count && row_pos < line.Length; ++i) + { + m_output[dst++] = line[row_pos++]; + m_output[dst++] = line[row_pos++]; + m_output[dst++] = line[row_pos++]; + m_output[dst++] = 0xff; + } + break; + case 3: + b = line[row_pos++]; + g = line[row_pos++]; + r = line[row_pos++]; + for (int i = 0; i < count; ++i) + { + m_output[dst++] = b; + m_output[dst++] = g; + m_output[dst++] = r; + m_output[dst++] = 0xff; + } + break; + case 4: + for (int i = 0; i < count && row_pos < line.Length; ++i) + { + a = line[row_pos++]; + m_output[dst++] = line[row_pos++]; + m_output[dst++] = line[row_pos++]; + m_output[dst++] = line[row_pos++]; + m_output[dst++] = a; + } + break; + case 5: + a = line[row_pos++]; + b = line[row_pos++]; + g = line[row_pos++]; + r = line[row_pos++]; + for (int i = 0; i < count; ++i) + { + m_output[dst++] = b; + m_output[dst++] = g; + m_output[dst++] = r; + m_output[dst++] = a; + } + break; + default: + dst += count * 4; + break; + } + } + return dst; + } + + byte[] ReadLine (uint offset, int repeat) + { + m_input.BaseStream.Position = offset; + int row_length = m_input.ReadUInt16(); + if (0 != (offset & 1)) + { + m_input.ReadByte(); + --row_length; + } + var row = new byte[row_length]; + m_input.Read (row, 0, row.Length); + int row_pos = 0; + for (int x = m_width; x > 0; ) + { + if (0 != (row_pos & 1)) + { + ++row_pos; + } + int count = LittleEndian.ToUInt16 (row, row_pos); + row_pos += 2; + int method = count >> 13; + int skip = (count >> 11) & 3; + if (0 != skip) + { + row_pos += skip; + } + count &= 0x7ff; + if (0 == count) + { + count = LittleEndian.ToInt32 (row, row_pos); + row_pos += 4; + } + if (count < 0 || count > x) count = x; + x -= count; + + switch (method) + { + case 2: + for (int j = 0; j < repeat; ++j) + { + for (int i = 3; i < count*3 && row_pos+i < row.Length; ++i) + { + row[row_pos+i] += row[row_pos+i-3]; + } + } + row_pos += count*3; + break; + case 3: + row_pos += 3; + break; + case 4: + for (int j = 0; j < repeat; ++j) + { + for (int i = 4; i < count*4 && row_pos+i < row.Length; ++i) + { + row[row_pos+i] += row[row_pos+i-4]; + } + } + row_pos += count*4; + break; + case 5: + row_pos += 4; + break; + default: + break; + } + } + return row; + } + #region IDisposable Members bool disposed = false;