diff --git a/ArcFormats/Cyberworks/ArcDAT.cs b/ArcFormats/Cyberworks/ArcDAT.cs index 2afd1739..57a58f86 100644 --- a/ArcFormats/Cyberworks/ArcDAT.cs +++ b/ArcFormats/Cyberworks/ArcDAT.cs @@ -2,7 +2,7 @@ //! \date Thu Jun 16 13:48:04 2016 //! \brief Tinker Bell resource archive. // -// Copyright (C) 2016 by morkt +// Copyright (C) 2016-2017 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 @@ -272,8 +272,12 @@ namespace GameRes.Formats.Cyberworks try { var reader = DecryptImage (input, barc.Scheme); - if (BlendOverlayImages && reader is AImageReader) - reader = BlendAImage (barc, entry, reader as AImageReader); + if (BlendOverlayImages) + { + var overlay = reader as AImageReader; + if (overlay != null) + overlay.ReadBaseline (barc, entry); + } return reader; } catch @@ -294,55 +298,18 @@ namespace GameRes.Formats.Cyberworks input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name); } } - else if (scheme != null && 'a' == type && input.Length > 21) + else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21) { int id = input.ReadByte(); if (id == scheme.Value2) { - return new AImageReader (input, scheme); + return new AImageReader (input, scheme, type); } } input.Position = 0; return new ImageFormatDecoder (input); } - IImageDecoder BlendAImage (BellArchive arc, Entry entry, AImageReader overlay) - { - var header = overlay.ReadHeader(); - if (header[0] != 1) - return overlay; - var scheme = arc.Scheme; - var dir = (List)arc.Dir; - int i = dir.IndexOf (entry); - while (--i >= 0 && "image" == dir[i].Type) - { - using (var input = OpenEntry (arc, dir[i])) - { - int type = input.ReadByte(); - if (type != 'a') - break; - int id = input.ReadByte(); - if (id != scheme.Value2) - break; - using (var bin = new BinaryStream (input, dir[i].Name)) - using (var base_image = new AImageReader (bin, scheme)) - { - var base_header = base_image.ReadHeader(); - if (1 == base_header[0]) - continue; - // check if image width/height are the same - if (base_header[3] == header[3] && base_header[4] == header[4]) - { - base_image.Unpack(); - overlay.Baseline = base_image.Data; - } - break; - } - } - } - return overlay; - } - internal AImageScheme QueryScheme (string arc_name) { var title = FormatCatalog.Instance.LookupGame (arc_name); diff --git a/ArcFormats/Cyberworks/ImageTINK.cs b/ArcFormats/Cyberworks/ImageTINK.cs index a876d691..b5715212 100644 --- a/ArcFormats/Cyberworks/ImageTINK.cs +++ b/ArcFormats/Cyberworks/ImageTINK.cs @@ -2,7 +2,7 @@ //! \date Fri Jun 17 18:49:04 2016 //! \brief Tinker Bell encrypted image file. // -// Copyright (C) 2016 by morkt +// Copyright (C) 2016-2017 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 @@ -24,9 +24,10 @@ // using System; -using System.ComponentModel.Composition; +using System.Collections.Generic; using System.IO; using System.Windows.Media; +using System.Windows.Media.Imaging; namespace GameRes.Formats.Cyberworks { @@ -52,11 +53,13 @@ namespace GameRes.Formats.Cyberworks AImageScheme m_scheme; ImageData m_image; int[] m_header; + int m_type; public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } } public ImageFormat SourceFormat { get { return null; } } public ImageMetaData Info { get { return m_info; } } public byte[] Baseline { get; set; } + public int Type { get { return m_type; } } public ImageData Image { @@ -77,10 +80,11 @@ namespace GameRes.Formats.Cyberworks public byte[] Data { get { return m_output; } } - public AImageReader (IBinaryStream input, AImageScheme scheme) + public AImageReader (IBinaryStream input, AImageScheme scheme, int type = 'a') { m_input = input; m_scheme = scheme; + m_type = type; } internal int[] ReadHeader () @@ -99,6 +103,65 @@ namespace GameRes.Formats.Cyberworks return m_header; } + /// + /// Search archive for baseline image. + /// + internal void ReadBaseline (BellArchive arc, Entry entry) + { + var header = ReadHeader(); + if (!((header[0] & 1) == 1 && 'd' == this.Type + || header[0] == 1 && 'a' == this.Type)) + return; + var scheme = arc.Scheme; + var dir = (List)arc.Dir; + int i = dir.IndexOf (entry); + while (--i >= 0 && "image" == dir[i].Type) + { + using (var input = arc.OpenEntry (dir[i])) + { + int type = input.ReadByte(); + if ('d' == type) + continue; + if ('a' == type) + { + int id = input.ReadByte(); + if (id != scheme.Value2) + break; + using (var bin = new BinaryStream (input, dir[i].Name)) + using (var base_image = new AImageReader (bin, scheme)) + { + var base_header = base_image.ReadHeader(); + if (1 == base_header[0]) + continue; + // check if image width/height are the same + if (base_header[3] == header[3] && base_header[4] == header[4]) + { + base_image.Unpack(); + Baseline = base_image.Data; + } + } + } + else if ('b' == type || 'c' == type) + { + var size_buf = new byte[4]; + input.Read (size_buf, 0 , 4); + var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, + BitmapCacheOption.OnLoad); + BitmapSource frame = decoder.Frames[0]; + Info.Width = (uint)frame.PixelWidth; + Info.Height = (uint)frame.PixelHeight; + if (frame.Format.BitsPerPixel != 32) + frame = new FormatConvertedBitmap (frame, PixelFormats.Bgra32, null, 0); + int stride = frame.PixelWidth * 4; + var pixels = new byte[stride * frame.PixelHeight]; + frame.CopyPixels (pixels, stride, 0); + Baseline = pixels; + } + break; + } + } + } + public void Unpack () { var header = ReadHeader(); @@ -118,6 +181,8 @@ namespace GameRes.Formats.Cyberworks { if (0 == bits_size) CopyV6 (unpacked_size, header[6]); + else if (1 == (flags & 1) && 'd' == m_type && Baseline != null) + UnpackV6d (bits_size, bits_size + header[6]); else UnpackV6 (bits_size, data_offset, data_offset + header[6]); } @@ -306,6 +371,34 @@ namespace GameRes.Formats.Cyberworks } } + void UnpackV6d (int bits_size, int rgb_offset) + { + Info.BPP = 32; + var rgb_map = m_input.ReadBytes (bits_size); + var alpha = m_input.ReadBytes (rgb_offset - bits_size); + int plane_size = Math.Min (Baseline.Length, bits_size*8); + m_output = Baseline; + int bit = 1; + int bit_src = 0; + int alpha_src = 0; + int dst = 0; + for (int i = 0; i < plane_size; ++i) + { + if ((bit & rgb_map[bit_src]) != 0) + { + m_input.Read (m_output, dst, 3); + m_output[dst+3] = alpha[alpha_src++]; + } + dst += 4; + bit <<= 1; + if (0x100 == bit) + { + ++bit_src; + bit = 1; + } + } + } + int GetInt () { byte a = m_input.ReadUInt8();