mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-25 04:14:13 +08:00
(PsbOpener): rewritten with IImageDecoder.
support TLG-based layers.
This commit is contained in:
parent
559f47c673
commit
75f37f9bfd
@ -41,6 +41,8 @@ namespace GameRes.Formats.Emote
|
|||||||
public int Height;
|
public int Height;
|
||||||
public int TruncatedWidth;
|
public int TruncatedWidth;
|
||||||
public int TruncatedHeight;
|
public int TruncatedHeight;
|
||||||
|
public int OffsetX;
|
||||||
|
public int OffsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@ -62,7 +64,7 @@ namespace GameRes.Formats.Emote
|
|||||||
|
|
||||||
public PsbOpener ()
|
public PsbOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "psb" };
|
Extensions = new string[] { "psb", "pimg" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
@ -77,39 +79,57 @@ namespace GameRes.Formats.Emote
|
|||||||
var dir = reader.GetTextures();
|
var dir = reader.GetTextures();
|
||||||
if (null == dir || 0 == dir.Count)
|
if (null == dir || 0 == dir.Count)
|
||||||
return null;
|
return null;
|
||||||
else
|
return new ArcFile (file, this, dir);
|
||||||
return new ArcFile (file, this, dir);
|
|
||||||
}
|
}
|
||||||
if (!reader.IsEncrypted)
|
if (!reader.IsEncrypted)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (reader.ParseNonEncrypted())
|
||||||
|
{
|
||||||
|
var dir = reader.GetLayers();
|
||||||
|
if (null == dir)
|
||||||
|
return null;
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||||
{
|
{
|
||||||
var tex = entry as TexEntry;
|
var tex = (TexEntry)entry;
|
||||||
if (null == tex)
|
if ("TLG" == tex.TexType)
|
||||||
return base.OpenEntry (arc, entry);
|
return OpenTlg (arc, tex);
|
||||||
|
var info = new PsbTexMetaData
|
||||||
byte[] header;
|
|
||||||
using (var mem = new MemoryStream())
|
|
||||||
using (var writer = new BinaryWriter (mem))
|
|
||||||
{
|
{
|
||||||
writer.Write ((uint)0x81C3D2D1); // 'PSB' ^ 0x81818181
|
FullWidth = tex.Width,
|
||||||
writer.Write ((int)0);
|
FullHeight = tex.Height,
|
||||||
writer.Write (tex.Width);
|
Width = (uint)tex.TruncatedWidth,
|
||||||
writer.Write (tex.Height);
|
Height = (uint)tex.TruncatedHeight,
|
||||||
writer.Write (tex.TruncatedWidth);
|
TexType = tex.TexType,
|
||||||
writer.Write (tex.TruncatedHeight);
|
BPP = 32
|
||||||
writer.Write (tex.TexType);
|
};
|
||||||
writer.BaseStream.Position = 4;
|
|
||||||
writer.Write ((int)writer.BaseStream.Length);
|
|
||||||
header = mem.ToArray();
|
|
||||||
}
|
|
||||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
return new PrefixStream (header, input);
|
return new PsbTextureDecoder (input, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
IImageDecoder OpenTlg (ArcFile arc, TexEntry entry)
|
||||||
|
{
|
||||||
|
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var info = TlgFormat.ReadMetaData (input);
|
||||||
|
if (null == info)
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
info.OffsetX = entry.OffsetX;
|
||||||
|
info.OffsetY = entry.OffsetY;
|
||||||
|
return new ImageFormatDecoder (input, TlgFormat, info);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
input.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ResourceScheme Scheme
|
public override ResourceScheme Scheme
|
||||||
@ -117,6 +137,10 @@ namespace GameRes.Formats.Emote
|
|||||||
get { return new PsbScheme { KnownKeys = KnownKeys }; }
|
get { return new PsbScheme { KnownKeys = KnownKeys }; }
|
||||||
set { KnownKeys = ((PsbScheme)value).KnownKeys; }
|
set { KnownKeys = ((PsbScheme)value).KnownKeys; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageFormat TlgFormat { get { return s_TlgFormat.Value; } }
|
||||||
|
|
||||||
|
static Lazy<ImageFormat> s_TlgFormat = new Lazy<ImageFormat> (() => ImageFormat.FindByTag ("TLG"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -149,6 +173,11 @@ namespace GameRes.Formats.Emote
|
|||||||
uint[] m_key = new uint[6];
|
uint[] m_key = new uint[6];
|
||||||
Dictionary<int, string> m_name_map;
|
Dictionary<int, string> m_name_map;
|
||||||
|
|
||||||
|
public bool ParseNonEncrypted ()
|
||||||
|
{
|
||||||
|
return Parse (false);
|
||||||
|
}
|
||||||
|
|
||||||
public bool Parse (uint key)
|
public bool Parse (uint key)
|
||||||
{
|
{
|
||||||
m_key[0] = 0x075BCD15;
|
m_key[0] = 0x075BCD15;
|
||||||
@ -158,7 +187,12 @@ namespace GameRes.Formats.Emote
|
|||||||
m_key[4] = 0;
|
m_key[4] = 0;
|
||||||
m_key[5] = 0;
|
m_key[5] = 0;
|
||||||
|
|
||||||
if (!ReadHeader())
|
return Parse (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parse (bool encrypted)
|
||||||
|
{
|
||||||
|
if (!ReadHeader (encrypted))
|
||||||
return false;
|
return false;
|
||||||
if (Version < 2)
|
if (Version < 2)
|
||||||
throw new NotSupportedException ("Not supported PSB version");
|
throw new NotSupportedException ("Not supported PSB version");
|
||||||
@ -169,6 +203,36 @@ namespace GameRes.Formats.Emote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Entry> GetLayers ()
|
||||||
|
{
|
||||||
|
var layers = GetRootKey<IList> ("layers");
|
||||||
|
if (null == layers || 0 == layers.Count)
|
||||||
|
return null;
|
||||||
|
var dir = new List<Entry> (layers.Count);
|
||||||
|
foreach (IDictionary layer in layers)
|
||||||
|
{
|
||||||
|
var name = layer["layer_id"].ToString() + ".tlg";
|
||||||
|
var layer_data = GetRootKey<EmChunk> (name);
|
||||||
|
if (null == layer_data)
|
||||||
|
continue;
|
||||||
|
var entry = new TexEntry {
|
||||||
|
Name = name,
|
||||||
|
Type = "image",
|
||||||
|
Offset = DataOffset + layer_data.Offset,
|
||||||
|
Size = (uint)layer_data.Length,
|
||||||
|
TexType = "TLG",
|
||||||
|
OffsetX = Convert.ToInt32 (layer["left"]),
|
||||||
|
OffsetY = Convert.ToInt32 (layer["top"]),
|
||||||
|
Width = Convert.ToInt32 (layer["width"]),
|
||||||
|
Height = Convert.ToInt32 (layer["height"]),
|
||||||
|
};
|
||||||
|
dir.Add (entry);
|
||||||
|
}
|
||||||
|
if (0 == dir.Count)
|
||||||
|
return null;
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Entry> GetTextures ()
|
public List<Entry> GetTextures ()
|
||||||
{
|
{
|
||||||
var source = GetRootKey<IDictionary> ("source");
|
var source = GetRootKey<IDictionary> ("source");
|
||||||
@ -211,17 +275,16 @@ namespace GameRes.Formats.Emote
|
|||||||
int m_root;
|
int m_root;
|
||||||
byte[] m_data;
|
byte[] m_data;
|
||||||
|
|
||||||
bool ReadHeader ()
|
bool ReadHeader (bool encrypted)
|
||||||
{
|
{
|
||||||
m_input.Position = 4;
|
m_input.Position = 4;
|
||||||
m_version = m_input.ReadUInt16();
|
m_version = m_input.ReadUInt16();
|
||||||
m_flags = m_input.ReadUInt16();
|
m_flags = m_input.ReadUInt16();
|
||||||
if (m_version < 3)
|
if (encrypted && m_version < 3)
|
||||||
m_flags = 2;
|
m_flags = 2;
|
||||||
|
|
||||||
var header = new byte[0x20];
|
var header = m_input.ReadBytes (0x20);
|
||||||
m_input.Read (header, 0, header.Length);
|
if (encrypted && 0 != (m_flags & 1))
|
||||||
if (0 != (m_flags & 1))
|
|
||||||
Decrypt (header, 0, 0x20);
|
Decrypt (header, 0, 0x20);
|
||||||
|
|
||||||
m_names = LittleEndian.ToInt32 (header, 0x04);
|
m_names = LittleEndian.ToInt32 (header, 0x04);
|
||||||
@ -246,7 +309,7 @@ namespace GameRes.Formats.Emote
|
|||||||
m_data = new byte[m_chunk_data];
|
m_data = new byte[m_chunk_data];
|
||||||
int data_pos = (int)m_input.Position;
|
int data_pos = (int)m_input.Position;
|
||||||
m_input.Read (m_data, data_pos, m_chunk_data-data_pos);
|
m_input.Read (m_data, data_pos, m_chunk_data-data_pos);
|
||||||
if (0 != (m_flags & 2))
|
if (encrypted && 0 != (m_flags & 2))
|
||||||
Decrypt (m_data, m_names, m_chunk_offsets-m_names);
|
Decrypt (m_data, m_names, m_chunk_offsets-m_names);
|
||||||
// root object is a dictionary
|
// root object is a dictionary
|
||||||
return 0x21 == m_data[m_root];
|
return 0x21 == m_data[m_root];
|
||||||
@ -559,79 +622,58 @@ namespace GameRes.Formats.Emote
|
|||||||
public string TexType;
|
public string TexType;
|
||||||
public int FullWidth;
|
public int FullWidth;
|
||||||
public int FullHeight;
|
public int FullHeight;
|
||||||
public int DataOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Artificial format representing PSB texture.
|
/// Artificial format representing PSB texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Export(typeof(ImageFormat))]
|
internal sealed class PsbTextureDecoder : BinaryImageDecoder
|
||||||
internal class PsbTextureFormat : ImageFormat
|
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "PSB/TEXTURE"; } }
|
PsbTexMetaData m_info;
|
||||||
public override string Description { get { return "PSB texture format"; } }
|
|
||||||
public override uint Signature { get { return 0x81C3D2D1; } } // 'PSB' ^ 0x81818181
|
|
||||||
|
|
||||||
public PsbTextureFormat ()
|
public PsbTextureDecoder (IBinaryStream input, PsbTexMetaData info) : base (input, info)
|
||||||
{
|
{
|
||||||
Extensions = new string[0];
|
m_input = input;
|
||||||
|
m_info = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
protected override ImageData GetImageData ()
|
||||||
{
|
{
|
||||||
stream.Position = 4;
|
var pixels = new byte[m_info.Width * m_info.Height * 4];
|
||||||
// need BinaryReader because of ReadString
|
if ("RGBA8" == m_info.TexType)
|
||||||
using (var reader = new BinaryReader (stream.AsStream, Encoding.UTF8, true))
|
ReadRgba8 (pixels);
|
||||||
{
|
else if ("RGBA4444" == m_info.TexType)
|
||||||
var info = new PsbTexMetaData { BPP = 32 };
|
ReadRgba4444 (pixels);
|
||||||
info.DataOffset = reader.ReadInt32();
|
|
||||||
info.FullWidth = reader.ReadInt32();
|
|
||||||
info.FullHeight = reader.ReadInt32();
|
|
||||||
info.Width = reader.ReadUInt32();
|
|
||||||
info.Height = reader.ReadUInt32();
|
|
||||||
info.TexType = reader.ReadString();
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
|
||||||
{
|
|
||||||
var meta = (PsbTexMetaData)info;
|
|
||||||
var pixels = new byte[meta.Width * meta.Height * 4];
|
|
||||||
if ("RGBA8" == meta.TexType)
|
|
||||||
ReadRgba8 (stream.AsStream, meta, pixels);
|
|
||||||
else if ("RGBA4444" == meta.TexType)
|
|
||||||
ReadRgba4444 (stream.AsStream, meta, pixels);
|
|
||||||
else
|
else
|
||||||
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", meta.TexType));
|
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", m_info.TexType));
|
||||||
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels);
|
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadRgba8 (Stream input, PsbTexMetaData meta, byte[] output)
|
void ReadRgba8 (byte[] output)
|
||||||
{
|
{
|
||||||
int dst_stride = (int)meta.Width * 4;
|
int dst_stride = (int)m_info.Width * 4;
|
||||||
long next_row = meta.DataOffset;
|
long next_row = 0;
|
||||||
int src_stride = meta.FullWidth * 4;
|
int src_stride = m_info.FullWidth * 4;
|
||||||
int dst = 0;
|
int dst = 0;
|
||||||
for (uint i = 0; i < meta.Height; ++i)
|
for (uint i = 0; i < m_info.Height; ++i)
|
||||||
{
|
{
|
||||||
input.Position = next_row;
|
m_input.Position = next_row;
|
||||||
input.Read (output, dst, dst_stride);
|
m_input.Read (output, dst, dst_stride);
|
||||||
dst += dst_stride;
|
dst += dst_stride;
|
||||||
next_row += src_stride;
|
next_row += src_stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadRgba4444 (Stream input, PsbTexMetaData meta, byte[] output)
|
void ReadRgba4444 (byte[] output)
|
||||||
{
|
{
|
||||||
int dst_stride = (int)meta.Width * 4;
|
int dst_stride = (int)m_info.Width * 4;
|
||||||
int src_stride = meta.FullWidth * 2;
|
int src_stride = m_info.FullWidth * 2;
|
||||||
int dst = 0;
|
int dst = 0;
|
||||||
var row = new byte[src_stride];
|
var row = new byte[src_stride];
|
||||||
input.Position = meta.DataOffset;
|
m_input.Position = 0;
|
||||||
for (uint i = 0; i < meta.Height; ++i)
|
for (uint i = 0; i < m_info.Height; ++i)
|
||||||
{
|
{
|
||||||
input.Read (row, 0, src_stride);
|
m_input.Read (row, 0, src_stride);
|
||||||
int src = 0;
|
int src = 0;
|
||||||
for (int x = 0; x < dst_stride; x += 4)
|
for (int x = 0; x < dst_stride; x += 4)
|
||||||
{
|
{
|
||||||
@ -644,10 +686,5 @@ namespace GameRes.Formats.Emote
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write (Stream file, ImageData image)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException ("PsbTextureFormat.Write not supported");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user