diff --git a/ArcFormats/Unity/Gx4Lib/ArcDAT.cs b/ArcFormats/Unity/Gx4Lib/ArcDAT.cs index 2311cad2..d3ef91ee 100644 --- a/ArcFormats/Unity/Gx4Lib/ArcDAT.cs +++ b/ArcFormats/Unity/Gx4Lib/ArcDAT.cs @@ -23,6 +23,8 @@ // IN THE SOFTWARE. // +using System; +using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; using System.Linq; @@ -32,6 +34,13 @@ using GameRes.Utility; namespace GameRes.Formats.Unity.Gx4Lib { + internal class VisualDiffData + { + public string BaseFileName; + public int PosX; + public int PosY; + } + [Export(typeof(ArchiveFormat))] public class DatOpener : ArchiveFormat { @@ -41,6 +50,8 @@ namespace GameRes.Formats.Unity.Gx4Lib public override bool IsHierarchic { get { return false; } } public override bool CanWrite { get { return false; } } + Lazy> DefaultVisualMap = new Lazy> (ReadVisualData); + public override ArcFile TryOpen (ArcView file) { uint index_size = file.View.ReadUInt32 (0); @@ -103,6 +114,25 @@ namespace GameRes.Formats.Unity.Gx4Lib int buf_width = input.ReadInt32(); int buf_height = input.ReadInt32(); var info = new ImageMetaData { Width = width, Height = height, BPP = 32 }; + if (DefaultVisualMap.Value.ContainsKey (entry.Name)) + { + var diff_info = DefaultVisualMap.Value[entry.Name]; + if (VFS.FileExists (diff_info.BaseFileName)) + { + var base_entry = VFS.FindFile (diff_info.BaseFileName); + using (var visbase = VFS.OpenImage (base_entry)) + { + var base_decoder = visbase as Gx4ImageDecoder; + if (base_decoder != null) + { + info.OffsetX = diff_info.PosX; + info.OffsetY = diff_info.PosY; + var pixels = base_decoder.ReadPixels(); + return new Gx4OverlayDecoder (pixels, base_decoder.Info, input, info); + } + } + } + } return new Gx4ImageDecoder (input, info); } @@ -204,26 +234,97 @@ namespace GameRes.Formats.Unity.Gx4Lib bits >>= 1; } } + + internal static Dictionary ReadVisualData () + { + var map = new Dictionary(); + try + { + FormatCatalog.Instance.ReadFileList ("gx4_bsz_visual.lst", (line) => { + var parts = line.Split (':', ','); + if (parts.Length > 3) + { + var diff = new VisualDiffData { BaseFileName = parts[1] }; + if (int.TryParse (parts[2], out diff.PosX) && int.TryParse (parts[3], out diff.PosY)) + map[parts[0]] = diff; + } + }); + } + catch { /* ignore errors */ } + return map; + } } internal class Gx4ImageDecoder : BinaryImageDecoder { + public int Stride { get; private set; } + public Gx4ImageDecoder (IBinaryStream input, ImageMetaData info) : base (input, info) { + Stride = (int)info.Width * 4; } - protected override ImageData GetImageData () + public byte[] ReadPixels () { m_input.Position = 0x10; - int stride = (int)Info.Width * 4; - var pixels = m_input.ReadBytes (stride * (int)Info.Height); + var pixels = m_input.ReadBytes (Stride * (int)Info.Height); for (int i = 0; i < pixels.Length; i += 4) { byte t = pixels[i]; pixels[i] = pixels[i+2]; pixels[i+2] = t; } - return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, stride); + return pixels; + } + + protected override ImageData GetImageData () + { + var pixels = ReadPixels(); + return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, Stride); + } + } + + internal class Gx4OverlayDecoder : BinaryImageDecoder + { + byte[] m_pixels; + + ImageMetaData OverlayInfo { get; set; } + + public Gx4OverlayDecoder (byte[] pixels, ImageMetaData info, IBinaryStream input, ImageMetaData overlay_info) + : base (input, info) + { + m_pixels = pixels; + OverlayInfo = overlay_info; + } + + protected override ImageData GetImageData () + { + m_input.Position = 0x10; + int base_stride = (int)Info.Width * 4; + int diff_stride = (int)OverlayInfo.Width * 4; + var diff = m_input.ReadBytes (diff_stride * (int)OverlayInfo.Height); + int dst_line = m_pixels.Length - base_stride * (OverlayInfo.OffsetY + 1) + OverlayInfo.OffsetX * 4; + int src_line = diff.Length - diff_stride; + for (int y = 0; y < (int)OverlayInfo.Height; ++y) + { + int dst = dst_line; + int src = src_line; + for (int x = 0; x < (int)OverlayInfo.Width; ++x) + { + if (!(0 == diff[src] && 0 == diff[src+1] && 0 == diff[src+2] && 0xFF == diff[src+3])) + { + m_pixels[dst ] = diff[src+2]; + m_pixels[dst+1] = diff[src+1]; + m_pixels[dst+2] = diff[src ]; + m_pixels[dst+3] = diff[src+3]; + } + dst += 4; + src += 4; + } + dst_line -= base_stride; + src_line -= diff_stride; + } + return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, m_pixels, base_stride); } } }