mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-30 17:05:37 +08:00
implemented incremental compression of S25 images.
This commit is contained in:
parent
ea6eab502c
commit
2d22dbe946
@ -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))
|
||||
|
@ -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<uint, int> rows_count)
|
||||
{
|
||||
m_input.BaseStream.Position = 4;
|
||||
int count = m_input.ReadInt32();
|
||||
var frames = new List<uint> (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<uint, int> (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<uint, byte[]> (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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user