mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
implemented PBZ archives, PSB images and MV audio.
This commit is contained in:
297
ArcFormats/Cmvs/ImagePSB.cs
Normal file
297
ArcFormats/Cmvs/ImagePSB.cs
Normal file
@@ -0,0 +1,297 @@
|
||||
//! \file ImagePSB.cs
|
||||
//! \date Thu Jun 23 20:16:31 2016
|
||||
//! \brief PVNS engine 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 GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Pvns
|
||||
{
|
||||
internal class PsbMetaData : ImageMetaData
|
||||
{
|
||||
public int Method;
|
||||
public int TableOffset;
|
||||
public int DataOffset;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PsbFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "PSB"; } }
|
||||
public override string Description { get { return "PVNS engine image format"; } }
|
||||
public override uint Signature { get { return 0x50425350; } } // 'PSBP'
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var header = new byte[0x14];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
stream.Seek (-0x13, SeekOrigin.End);
|
||||
var tail = new byte[0x13];
|
||||
stream.Read (tail, 0, tail.Length);
|
||||
for (int i = 4; i < 0x14; ++i)
|
||||
{
|
||||
header[i] ^= tail[tail.Length - 3 + (i & 1)];
|
||||
header[i] -= tail[i-4];
|
||||
}
|
||||
return new PsbMetaData
|
||||
{
|
||||
Width = LittleEndian.ToUInt16 (header, 0x0E),
|
||||
Height = LittleEndian.ToUInt16 (header, 0x10),
|
||||
BPP = LittleEndian.ToUInt16 (header, 0x12),
|
||||
Method = LittleEndian.ToUInt16 (header, 0x0C),
|
||||
TableOffset = LittleEndian.ToInt32 (header, 4),
|
||||
DataOffset = LittleEndian.ToInt32 (header, 8),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var reader = new PsbReader (stream, (PsbMetaData)info);
|
||||
reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, null, reader.Data);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PsbFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PsbReader
|
||||
{
|
||||
byte[] m_input;
|
||||
byte[] m_output;
|
||||
PsbMetaData m_info;
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_channels;
|
||||
byte[] m_lzss_frame;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public PsbReader (Stream input, PsbMetaData info)
|
||||
{
|
||||
m_info = info;
|
||||
m_width = (int)m_info.Width;
|
||||
m_height = (int)m_info.Height;
|
||||
m_channels = m_info.BPP / 8;
|
||||
if (4 == m_channels)
|
||||
Format = PixelFormats.Bgra32;
|
||||
else if (3 == m_channels)
|
||||
Format = PixelFormats.Bgr24;
|
||||
else
|
||||
throw new InvalidFormatException();
|
||||
m_input = new byte[input.Length];
|
||||
input.Read (m_input, 0, m_input.Length);
|
||||
m_output = new byte[info.Width * info.Height * m_channels];
|
||||
m_lzss_frame = new byte[0x1000];
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
switch (m_info.Method)
|
||||
{
|
||||
case 2: UnpackV2(); break;
|
||||
case 3: UnpackV3(); break;
|
||||
default:
|
||||
throw new NotImplementedException (string.Format ("PSB images type {0} not implemented", m_info.Method));
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV2 ()
|
||||
{
|
||||
int plane_size = m_width * m_height;
|
||||
var plane = new byte[plane_size];
|
||||
|
||||
int src = m_info.TableOffset;
|
||||
var bits_table = new int[m_channels];
|
||||
int offset = m_info.TableOffset + 4 * m_channels;
|
||||
for (int i = 0; i < m_channels; ++i)
|
||||
{
|
||||
bits_table[i] = offset;
|
||||
offset += LittleEndian.ToInt32 (m_input, src);
|
||||
src += 4;
|
||||
}
|
||||
src = offset;
|
||||
offset += 4 * m_channels;
|
||||
var data_table = new int[m_channels];
|
||||
for (int i = 0; i < m_channels; ++i)
|
||||
{
|
||||
data_table[i] = offset;
|
||||
offset += LittleEndian.ToInt32 (m_input, src);
|
||||
src += 4;
|
||||
}
|
||||
for (int channel = 0; channel < m_channels; ++channel)
|
||||
{
|
||||
LzssUnpack (bits_table[channel], data_table[channel], plane, plane.Length);
|
||||
int dst = channel;
|
||||
byte pixel = 0;
|
||||
for (int i = 0; i < plane_size; ++i)
|
||||
{
|
||||
pixel += plane[i];
|
||||
m_output[dst] = pixel;
|
||||
dst += m_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV3 ()
|
||||
{
|
||||
int stride = m_width * m_channels;
|
||||
var plane = new byte[m_width * m_height];
|
||||
int y_blocks = m_height >> 3;
|
||||
int x_blocks = m_width >> 3;
|
||||
if (0 != (m_width & 7))
|
||||
++x_blocks;
|
||||
if (0 != (m_height & 7))
|
||||
++y_blocks;
|
||||
|
||||
int src = m_info.TableOffset;
|
||||
var bits_table = new int[m_channels];
|
||||
int offset = m_info.TableOffset + 4 * m_channels;
|
||||
for (int i = 0; i < m_channels; ++i)
|
||||
{
|
||||
bits_table[i] = offset;
|
||||
offset += LittleEndian.ToInt32 (m_input, src);
|
||||
src += 4;
|
||||
}
|
||||
src = m_info.DataOffset;
|
||||
var data_offsets = new int[m_channels];
|
||||
offset = m_info.DataOffset + 4 * m_channels;
|
||||
for (int i = 0; i < m_channels; ++i)
|
||||
{
|
||||
data_offsets[i] = offset;
|
||||
offset += LittleEndian.ToInt32 (m_input, src);
|
||||
src += 4;
|
||||
}
|
||||
|
||||
for (int channel = 0; channel < m_channels; ++channel)
|
||||
{
|
||||
int dst = channel;
|
||||
|
||||
src = bits_table[channel];
|
||||
int bit_length = LittleEndian.ToInt32 (m_input, src);
|
||||
int bit_src = src + 12 + bit_length + LittleEndian.ToInt32 (m_input, src+4);
|
||||
int plane_size = LittleEndian.ToInt32 (m_input, src+8);
|
||||
|
||||
LzssUnpack (bit_src, data_offsets[channel], plane, plane_size);
|
||||
|
||||
int plane_src = 0;
|
||||
bit_src = src + 12;
|
||||
src = bit_src + bit_length;
|
||||
int bit_mask = 128;
|
||||
int y_pos = 0;
|
||||
for (int y = 0; y < y_blocks; ++y)
|
||||
{
|
||||
int block_height = Math.Min (8, m_height - y_pos);
|
||||
y_pos += 8;
|
||||
int x_pos = 0;
|
||||
int dst_origin = dst;
|
||||
for (int x = 0; x < x_blocks; ++x)
|
||||
{
|
||||
int block_width = Math.Min (8, m_width - x_pos);
|
||||
x_pos += 8;
|
||||
if (0 == bit_mask)
|
||||
{
|
||||
++bit_src;
|
||||
bit_mask = 128;
|
||||
}
|
||||
if (0 != (bit_mask & m_input[bit_src]))
|
||||
{
|
||||
byte b = m_input[src++];
|
||||
for (int j = 0; j < block_height; ++j)
|
||||
{
|
||||
int d = dst + stride * j;
|
||||
for (int i = 0; i < block_width; ++i)
|
||||
{
|
||||
m_output[d] = b;
|
||||
d += m_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < block_height; ++j)
|
||||
{
|
||||
int d = dst + stride * j;
|
||||
for (int i = 0; i < block_width; ++i)
|
||||
{
|
||||
m_output[d] = plane[plane_src++];
|
||||
d += m_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
dst += 8 * m_channels;
|
||||
}
|
||||
dst = dst_origin + 8 * stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LzssUnpack (int bit_src, int data_src, byte[] output, int output_size)
|
||||
{
|
||||
for (int i = 0; i < m_lzss_frame.Length; ++i)
|
||||
m_lzss_frame[i] = 0;
|
||||
int dst = 0;
|
||||
int bit_mask = 0x80;
|
||||
int frame_offset = 0xFEE;
|
||||
while (dst < output_size)
|
||||
{
|
||||
if (0 == bit_mask)
|
||||
{
|
||||
bit_mask = 0x80;
|
||||
++bit_src;
|
||||
}
|
||||
if (0 != (bit_mask & m_input[bit_src]))
|
||||
{
|
||||
int v = LittleEndian.ToUInt16 (m_input, data_src);
|
||||
data_src += 2;
|
||||
int count = (v & 0xF) + 3;
|
||||
int offset = v >> 4;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
byte b = m_lzss_frame[(i + offset) & 0xFFF];
|
||||
output[dst++] = b;
|
||||
m_lzss_frame[frame_offset++] = b;
|
||||
frame_offset &= 0xFFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte b = m_input[data_src++];
|
||||
output[dst++] = b;
|
||||
m_lzss_frame[frame_offset++] = b;
|
||||
frame_offset &= 0xFFF;
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user