diff --git a/ArcFormats/CatSystem/ArcHG2.cs b/ArcFormats/CatSystem/ArcHG2.cs index ff7d886a..f62142cc 100644 --- a/ArcFormats/CatSystem/ArcHG2.cs +++ b/ArcFormats/CatSystem/ArcHG2.cs @@ -52,7 +52,7 @@ namespace GameRes.Formats.CatSystem uint section_size = file.View.ReadUInt32 (offset+0x40); var entry = new Entry { - Name = string.Format ("{0}#{1:D4}.tga", base_name, i), + Name = string.Format ("{0}#{1:D4}", base_name, i), Type = "image", Offset = offset, }; @@ -71,9 +71,8 @@ namespace GameRes.Formats.CatSystem return new ArcFile (file, this, dir); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { - // emulate TGA image var offset = entry.Offset; var info = new Hg2MetaData { @@ -90,11 +89,8 @@ namespace GameRes.Formats.CatSystem OffsetX = arc.File.View.ReadInt32 (offset+0x34), OffsetY = arc.File.View.ReadInt32 (offset+0x38), }; - using (var input = arc.File.CreateStream (entry.Offset, entry.Size)) - using (var reader = new Hg2Reader (input, info)) - { - return TgaStream.Create (info, reader.Unpack(), true); - } + var input = arc.File.CreateStream (entry.Offset, entry.Size); + return new Hg2Reader (input, info); } } } diff --git a/ArcFormats/CatSystem/ArcHG3.cs b/ArcFormats/CatSystem/ArcHG3.cs index 79f4d721..a6e8cf43 100644 --- a/ArcFormats/CatSystem/ArcHG3.cs +++ b/ArcFormats/CatSystem/ArcHG3.cs @@ -55,7 +55,7 @@ namespace GameRes.Formats.CatSystem { var entry = new Entry { - Name = string.Format ("{0}#{1:D4}.tga", base_name, i), + Name = string.Format ("{0}#{1:D4}", base_name, i), Type = "image", Offset = offset + 8, Size = section_size - 8, @@ -70,9 +70,8 @@ namespace GameRes.Formats.CatSystem return new ArcFile (file, this, dir); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { - // emulate TGA image var offset = entry.Offset+8; var info = new HgMetaData { @@ -85,12 +84,8 @@ namespace GameRes.Formats.CatSystem CanvasWidth = arc.File.View.ReadUInt32 (offset+0x1C), CanvasHeight = arc.File.View.ReadUInt32 (offset+0x20), }; - using (var input = arc.File.CreateStream (entry.Offset, entry.Size)) - using (var reader = new Hg3Reader (input, info)) - { - var pixels = reader.Unpack(); - return TgaStream.Create (info, pixels, reader.Flipped); - } + var input = arc.File.CreateStream (entry.Offset, entry.Size); + return new Hg3Reader (input, info); } } } diff --git a/ArcFormats/CatSystem/ImageHG2.cs b/ArcFormats/CatSystem/ImageHG2.cs index d0b3ebe2..e107f57c 100644 --- a/ArcFormats/CatSystem/ImageHG2.cs +++ b/ArcFormats/CatSystem/ImageHG2.cs @@ -74,11 +74,7 @@ namespace GameRes.Formats.CatSystem public override ImageData Read (IBinaryStream stream, ImageMetaData info) { using (var reader = new Hg2Reader (stream, (Hg2MetaData)info)) - { - var pixels = reader.Unpack(); - var format = 24 == info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgra32; - return ImageData.CreateFlipped (info, format, null, pixels, reader.Stride); - } + return reader.Image; } public override void Write (Stream file, ImageData image) @@ -91,6 +87,20 @@ namespace GameRes.Formats.CatSystem { Hg2MetaData m_hg2; + public override ImageData Image + { + get + { + if (null == m_image) + { + var pixels = Unpack(); + var format = 24 == m_hg2.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgra32; + m_image = ImageData.CreateFlipped (m_hg2, format, null, pixels, Stride); + } + return m_image; + } + } + public Hg2Reader (IBinaryStream input, Hg2MetaData info) : base (input, info) { m_hg2 = info; diff --git a/ArcFormats/CatSystem/ImageHG3.cs b/ArcFormats/CatSystem/ImageHG3.cs index 9a6baf2d..0d58583f 100644 --- a/ArcFormats/CatSystem/ImageHG3.cs +++ b/ArcFormats/CatSystem/ImageHG3.cs @@ -77,11 +77,7 @@ namespace GameRes.Formats.CatSystem using (var input = new BinaryStream (reg, stream.Name)) using (var reader = new Hg3Reader (input, meta)) { - var pixels = reader.Unpack(); - if (reader.Flipped) - return ImageData.CreateFlipped (info, PixelFormats.Bgra32, null, pixels, reader.Stride); - else - return ImageData.Create (info, PixelFormats.Bgra32, null, pixels, reader.Stride); + return reader.Image; } } @@ -91,15 +87,18 @@ namespace GameRes.Formats.CatSystem } } - internal class HgReader : IDisposable + internal class HgReader : IImageDecoder { - private IBinaryStream m_input; + protected IBinaryStream m_input; protected HgMetaData m_info; protected int m_pixel_size; + protected ImageData m_image; - protected IBinaryStream Input { get { return m_input; } } - protected Stream InputStream { get { return m_input.AsStream; } } + public Stream Input { get { return m_input.AsStream; } } public int Stride { get; protected set; } + public ImageFormat Format { get { return null; } } + public ImageMetaData Info { get { return m_info; } } + public virtual ImageData Image { get { throw new NotImplementedException(); } } protected HgReader (IBinaryStream input, HgMetaData info) { @@ -113,12 +112,12 @@ namespace GameRes.Formats.CatSystem { var ctl_offset = data_offset + data_packed; var data = new byte[data_unpacked]; - using (var z = new StreamRegion (InputStream, data_offset, data_packed, true)) + using (var z = new StreamRegion (Input, data_offset, data_packed, true)) using (var data_in = new ZLibStream (z, CompressionMode.Decompress)) if (data.Length != data_in.Read (data, 0, data.Length)) throw new EndOfStreamException(); - using (var z = new StreamRegion (InputStream, ctl_offset, ctl_packed, true)) + using (var z = new StreamRegion (Input, ctl_offset, ctl_packed, true)) using (var ctl_in = new ZLibStream (z, CompressionMode.Decompress)) using (var bits = new LsbBitStream (ctl_in)) { @@ -237,14 +236,30 @@ namespace GameRes.Formats.CatSystem { public bool Flipped { get; private set; } + public override ImageData Image + { + get + { + if (null == m_image) + { + var pixels = Unpack(); + if (Flipped) + m_image = ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, Stride); + else + m_image = ImageData.Create (Info, PixelFormats.Bgra32, null, pixels, Stride); + } + return m_image; + } + } + public Hg3Reader (IBinaryStream input, HgMetaData info) : base (input, info) { } public byte[] Unpack () { - InputStream.Position = m_info.HeaderSize; - var img_type = Input.ReadBytes (8); + Input.Position = m_info.HeaderSize; + var img_type = m_input.ReadBytes (8); if (Binary.AsciiEqual (img_type, "img0000\0")) return UnpackImg0000(); else if (Binary.AsciiEqual (img_type, "img_jpg\0")) @@ -256,21 +271,21 @@ namespace GameRes.Formats.CatSystem byte[] UnpackImg0000 () { Flipped = true; - InputStream.Position = m_info.HeaderSize+0x18; - int packed_data_size = Input.ReadInt32(); - int data_size = Input.ReadInt32(); - int packed_ctl_size = Input.ReadInt32(); - int ctl_size = Input.ReadInt32(); + Input.Position = m_info.HeaderSize+0x18; + int packed_data_size = m_input.ReadInt32(); + int data_size = m_input.ReadInt32(); + int packed_ctl_size = m_input.ReadInt32(); + int ctl_size = m_input.ReadInt32(); return UnpackStream (m_info.HeaderSize+0x28, packed_data_size, data_size, packed_ctl_size, ctl_size); } byte[] UnpackJpeg () { Flipped = false; - Input.ReadInt32(); - var jpeg_size = Input.ReadInt32(); - long next_section = InputStream.Position + jpeg_size; - var decoder = new JpegBitmapDecoder (InputStream, + m_input.ReadInt32(); + var jpeg_size = m_input.ReadInt32(); + long next_section = Input.Position + jpeg_size; + var decoder = new JpegBitmapDecoder (Input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); var frame = decoder.Frames[0]; if (frame.Format.BitsPerPixel < 24) @@ -292,13 +307,13 @@ namespace GameRes.Formats.CatSystem src += src_pixel_size; } - Input.Position = next_section; - var section_header = Input.ReadBytes (8); + m_input.Position = next_section; + var section_header = m_input.ReadBytes (8); if (!Binary.AsciiEqual (section_header, "img_al\0\0")) return output; - Input.Seek (8, SeekOrigin.Current); - int alpha_size = Input.ReadInt32(); - using (var alpha_in = new StreamRegion (InputStream, InputStream.Position+4, alpha_size, true)) + m_input.Seek (8, SeekOrigin.Current); + int alpha_size = m_input.ReadInt32(); + using (var alpha_in = new StreamRegion (Input, Input.Position+4, alpha_size, true)) using (var alpha = new ZLibStream (alpha_in, CompressionMode.Decompress)) { for (int i = 3; i < output.Length; i += 4) diff --git a/ArcFormats/Kaguya/ArcANM.cs b/ArcFormats/Kaguya/ArcANM.cs index bf2b90f7..512cddca 100644 --- a/ArcFormats/Kaguya/ArcANM.cs +++ b/ArcFormats/Kaguya/ArcANM.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Windows.Media; namespace GameRes.Formats.Kaguya { @@ -79,7 +80,7 @@ namespace GameRes.Formats.Kaguya uint height = file.View.ReadUInt32 (current_offset+12); var entry = new Entry { - Name = string.Format ("{0}#{1:D2}.tga", base_name, i), + Name = string.Format ("{0}#{1:D2}", base_name, i), Type = "image", Offset = current_offset, Size = 0x10 + 4*width*height, @@ -90,22 +91,34 @@ namespace GameRes.Formats.Kaguya return new AnmArchive (file, this, dir, base_info); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { var base_info = ((AnmArchive)arc).ImageInfo; - // emulate TGA image - var offset = entry.Offset; - var info = new ImageMetaData + var input = arc.File.CreateStream (entry.Offset, entry.Size); + return new An00Decoder (input, base_info); + } + } + + internal class An00Decoder : BinaryImageDecoder + { + public An00Decoder (IBinaryStream input, ImageMetaData base_info) : base (input) + { + Info = new ImageMetaData { - OffsetX = base_info.OffsetX + arc.File.View.ReadInt32 (offset), - OffsetY = base_info.OffsetY + arc.File.View.ReadInt32 (offset+4), - Width = arc.File.View.ReadUInt32 (offset+8), - Height = arc.File.View.ReadUInt32 (offset+12), - BPP = 32, + OffsetX = base_info.OffsetX + m_input.ReadInt32(), + OffsetY = base_info.OffsetY + m_input.ReadInt32(), + Width = m_input.ReadUInt32(), + Height = m_input.ReadUInt32(), + BPP = 32, }; - offset += 0x10; - var pixels = arc.File.View.ReadBytes (offset, 4*info.Width*info.Height); - return TgaStream.Create (info, pixels, true); + } + + protected override ImageData GetImageData () + { + m_input.Position = 0x10; + int stride = 4*(int)Info.Width; + var pixels = m_input.ReadBytes (stride*(int)Info.Height); + return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, stride); } } @@ -163,7 +176,7 @@ namespace GameRes.Formats.Kaguya uint depth = file.View.ReadUInt32 (current_offset+0x10); var entry = new Entry { - Name = string.Format ("{0}#{1:D2}.tga", base_name, i), + Name = string.Format ("{0}#{1:D2}", base_name, i), Type = "image", Offset = current_offset, Size = 0x14 + depth*width*height, @@ -174,22 +187,45 @@ namespace GameRes.Formats.Kaguya return new AnmArchive (file, this, dir, base_info); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { var base_info = ((AnmArchive)arc).ImageInfo; - // emulate TGA image - var offset = entry.Offset; - var info = new ImageMetaData + var input = arc.File.CreateStream (entry.Offset, entry.Size); + return new An20Decoder (input, base_info); + } + } + + internal class An20Decoder : BinaryImageDecoder + { + public An20Decoder (IBinaryStream input, ImageMetaData base_info) : base (input) + { + Info = new ImageMetaData { - OffsetX = base_info.OffsetX + arc.File.View.ReadInt32 (offset), - OffsetY = base_info.OffsetY + arc.File.View.ReadInt32 (offset+4), - Width = arc.File.View.ReadUInt32 (offset+8), - Height = arc.File.View.ReadUInt32 (offset+0xC), - BPP = arc.File.View.ReadInt32 (offset+0x10) * 8, + OffsetX = base_info.OffsetX + m_input.ReadInt32(), + OffsetY = base_info.OffsetY + m_input.ReadInt32(), + Width = m_input.ReadUInt32(), + Height = m_input.ReadUInt32(), + BPP = m_input.ReadInt32() * 8, }; - offset += 0x14; - var pixels = arc.File.View.ReadBytes (offset, (uint)info.BPP/8*info.Width*info.Height); - return TgaStream.Create (info, pixels, true); + } + + protected override ImageData GetImageData () + { + m_input.Position = 0x14; + int stride = info.BPP/8*(int)Info.Width; + var pixels = m_input.ReadBytes (stride*(int)info.Height); + return ImageData.CreateFlipped (Info, GetFormat(), null, pixels, stride); + } + + PixelFormat GetFormat () + { + switch (Info.BPP) + { + case 8: return PixelFormats.Gray8; + case 24: return PixelFormats.Bgr24; + case 32: return PixelFormats.Bgra32; + default: throw new InvalidFormatException(); + } } } } diff --git a/ArcFormats/Musica/ArcSQZ.cs b/ArcFormats/Musica/ArcSQZ.cs index 4bdbecbb..a4a0596d 100644 --- a/ArcFormats/Musica/ArcSQZ.cs +++ b/ArcFormats/Musica/ArcSQZ.cs @@ -69,7 +69,7 @@ namespace GameRes.Formats.Musica for (int i = 0; i < count; ++i) { var entry = new Entry { - Name = string.Format ("{0}#{1:D4}.tga", base_name, i), + Name = string.Format ("{0}#{1:D4}", base_name, i), Type = "image", Offset = file.View.ReadUInt32 (index_offset), Size = file.View.ReadUInt32 (index_offset+4), @@ -82,15 +82,51 @@ namespace GameRes.Formats.Musica return new SqzArchive (file, this, dir, info); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { var sqarc = (SqzArchive)arc; - using (var input = arc.File.CreateStream (entry.Offset, entry.Size)) - using (var zs = new ZLibStream (input, CompressionMode.Decompress)) + var input = arc.File.CreateStream (entry.Offset, entry.Size); + var zs = new ZLibStream (input, CompressionMode.Decompress); + return new SqzReader (zs, sqarc.Info); + } + + internal sealed class SqzReader : IImageDecoder + { + Stream m_input; + ImageMetaData m_info; + ImageData m_image; + + public Stream Input { get { return m_input; } } + public ImageFormat Format { get { return null; } } + public ImageMetaData Info { get { return m_info; } } + public ImageData Image { - var pixels = new byte[sqarc.Info.Width * sqarc.Info.Height * 4]; - zs.Read (pixels, 0, pixels.Length); - return TgaStream.Create (sqarc.Info, pixels); + get + { + if (null == m_image) + { + var pixels = new byte[m_info.Width * m_info.Height * 4]; + m_input.Read (pixels, 0, pixels.Length); + m_image = ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels); + } + return m_image; + } + } + + public SqzReader (Stream input, ImageMetaData info) + { + m_input = input; + m_info = info; + } + + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } } } } diff --git a/ArcFormats/ShiinaRio/ArcS25.cs b/ArcFormats/ShiinaRio/ArcS25.cs index 8ec35306..42153ca9 100644 --- a/ArcFormats/ShiinaRio/ArcS25.cs +++ b/ArcFormats/ShiinaRio/ArcS25.cs @@ -59,7 +59,7 @@ namespace GameRes.Formats.ShiinaRio { var entry = new Entry { - Name = string.Format ("{0}@{1:D4}.tga", base_name, i), + Name = string.Format ("{0}@{1:D4}", base_name, i), Type = "image", Offset = offset, }; @@ -79,9 +79,8 @@ namespace GameRes.Formats.ShiinaRio return new ArcFile (file, this, dir); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) { - // emulate TGA image var offset = entry.Offset; var info = new S25MetaData { @@ -93,11 +92,8 @@ namespace GameRes.Formats.ShiinaRio 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)) - { - return TgaStream.Create (info, reader.Unpack()); - } + var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset); + return new S25Format.Reader (input, info); } } } diff --git a/ArcFormats/ShiinaRio/ImageS25.cs b/ArcFormats/ShiinaRio/ImageS25.cs index 04e3b190..7f8fd4c2 100644 --- a/ArcFormats/ShiinaRio/ImageS25.cs +++ b/ArcFormats/ShiinaRio/ImageS25.cs @@ -76,10 +76,7 @@ namespace GameRes.Formats.ShiinaRio public override ImageData Read (IBinaryStream stream, ImageMetaData info) { using (var reader = new Reader (stream, (S25MetaData)info)) - { - var pixels = reader.Unpack(); - return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); - } + return reader.Image; } public override void Write (Stream file, ImageData image) @@ -87,7 +84,7 @@ namespace GameRes.Formats.ShiinaRio throw new NotImplementedException ("S25Format.Write not implemented"); } - internal sealed class Reader : IDisposable + internal sealed class Reader : IImageDecoder { IBinaryStream m_input; int m_width; @@ -95,6 +92,25 @@ namespace GameRes.Formats.ShiinaRio uint m_origin; byte[] m_output; bool m_incremental; + ImageMetaData m_info; + ImageData m_image; + + public Stream Input { get { m_input.Position = 0; return m_input.AsStream; } } + public ImageMetaData Info { get { return m_info; } } + public ImageFormat Format { get { return null; } } + + public ImageData Image + { + get + { + if (null == m_image) + { + var pixels = Unpack(); + m_image = ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels); + } + return m_image; + } + } public byte[] Data { get { return m_output; } } @@ -106,6 +122,7 @@ namespace GameRes.Formats.ShiinaRio m_input = file; m_origin = info.FirstOffset; m_incremental = info.Incremental; + m_info = info; } public byte[] Unpack ()