From 0425aa269ecef3cf07dbb854a0e1acb6a4576345 Mon Sep 17 00:00:00 2001 From: morkt Date: Tue, 23 Jan 2018 15:38:52 +0400 Subject: [PATCH] implemented TCD1 archives and SPD7 images. --- ArcFormats/ArcFormats.csproj | 1 + ArcFormats/TopCat/ArcTCD1.cs | 91 +++++++++++++++++++++++++++++++++++ ArcFormats/TopCat/ArcTCD3.cs | 2 +- ArcFormats/TopCat/ImageSPD.cs | 33 +++++++------ 4 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 ArcFormats/TopCat/ArcTCD1.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 5f3d3853..0f65c022 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -598,6 +598,7 @@ + diff --git a/ArcFormats/TopCat/ArcTCD1.cs b/ArcFormats/TopCat/ArcTCD1.cs new file mode 100644 index 00000000..ae850b52 --- /dev/null +++ b/ArcFormats/TopCat/ArcTCD1.cs @@ -0,0 +1,91 @@ +//! \file ArcTCD1.cs +//! \date 2018 Jan 23 +//! \brief TopCat TCD1 data archive. +// +// Copyright (C) 2018 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.TopCat +{ + [Export(typeof(ArchiveFormat))] + public class Tcd1Opener : TcdOpener + { + public override string Tag { get { return "TCD1"; } } + public override string Description { get { return "TopCat data archive"; } } + public override uint Signature { get { return 0x31444354; } } // 'TCD1' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public Tcd1Opener () + { + Extensions = new string[] { "tcd" }; + Signatures = new uint[] { 0x31444354 }; + } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + + uint index_offset = file.View.ReadUInt32 (8); + uint names_offset = file.View.ReadUInt32 (12); + + uint pos = index_offset; + var offsets = new uint[count+1]; + for (int i = 0; i < count; ++i) + { + offsets[i] = file.View.ReadUInt32 (pos) - (index_offset << ((i & 7) + 8)); + pos += 4; + } + offsets[count] = file.View.ReadUInt32 (pos); + var names = file.View.ReadBytes (names_offset, (uint)(file.MaxOffset - names_offset)); + var dir = new List (count); + int name_start = 0; + int entry_num = 0; + for (int i = 0; i < names.Length; ++i) + { + if (names[i] != 0) + { + names[i] -= 0x57; + } + else + { + var name = Encodings.cp932.GetString (names, name_start, i - name_start); + name_start = i+1; + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = offsets[entry_num]; + entry.Size = offsets[entry_num+1] - offsets[entry_num]; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + ++entry_num; + } + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/TopCat/ArcTCD3.cs b/ArcFormats/TopCat/ArcTCD3.cs index 2bc7c7e6..f027ffda 100644 --- a/ArcFormats/TopCat/ArcTCD3.cs +++ b/ArcFormats/TopCat/ArcTCD3.cs @@ -136,7 +136,7 @@ namespace GameRes.Formats.TopCat header[1] -= header_key; header[2] -= header_key; header[3] -= header_key; - bool spdc_entry = Binary.AsciiEqual (header, "SPD") && (header[3] == 'C' || header[3] == '8'); + bool spdc_entry = Binary.AsciiEqual (header, "SPD") && (header[3] == 'C' || header[3] == '8' || header[3] == '7'); if (!spdc_entry) { LittleEndian.Pack (signature, header, 0); diff --git a/ArcFormats/TopCat/ImageSPD.cs b/ArcFormats/TopCat/ImageSPD.cs index fb6214ab..1a1a0d38 100644 --- a/ArcFormats/TopCat/ImageSPD.cs +++ b/ArcFormats/TopCat/ImageSPD.cs @@ -2,7 +2,7 @@ //! \date Thu Oct 08 16:25:55 2015 //! \brief TopCat compressed image. // -// Copyright (C) 2015-2016 by morkt +// Copyright (C) 2015-2018 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 @@ -36,7 +36,7 @@ namespace GameRes.Formats.TopCat { public Compression Method; public uint UnpackedSize; - public bool IsSpd8; + public byte SpdType; } internal enum Compression @@ -59,7 +59,7 @@ namespace GameRes.Formats.TopCat public SpdFormat () { - Signatures = new uint[] { 0x43445053, 0x38445053 }; // 'SPD8' + Signatures = new uint[] { 0x43445053, 0x38445053, 0x37445053 }; // 'SPD8', 'SPD7' } public override ImageMetaData ReadMetaData (IBinaryStream stream) @@ -80,7 +80,7 @@ namespace GameRes.Formats.TopCat BPP = (int)(dw[1] >> 16), Method = (Compression)(dw[1] & 0xFFFF), UnpackedSize = dw[4], - IsSpd8 = header[3] == '8', + SpdType = header[3], }; } } @@ -172,9 +172,16 @@ namespace GameRes.Formats.TopCat UnpackLz(); var rgb = new byte[m_info.Height * m_info.Width * 4]; if (Compression.LzRle == m_info.Method || Compression.LzRle2 == m_info.Method) - UnpackRle (rgb); - else if (m_info.IsSpd8) - UnpackSpd8Alpha (rgb); + { + if ('7' == m_info.SpdType) + UnpackRle (rgb, 4, 0, 8); + else + UnpackRle (rgb, 0, 8, 12); + } + else if ('8' == m_info.SpdType) + UnpackSpdAlpha (rgb, 8); + else if ('7' == m_info.SpdType) + UnpackSpdAlpha (rgb, 4); else UnpackRleAlpha (rgb); m_output = rgb; @@ -220,11 +227,10 @@ namespace GameRes.Formats.TopCat } } - void UnpackRle (byte[] rgb) + void UnpackRle (byte[] rgb, int rgb_pos, int skip_pos, int ctl_src) { - int rgb_src = LittleEndian.ToInt32 (m_output, 0); - bool skip = 0 == LittleEndian.ToInt32 (m_output, 8); - int ctl_src = 12; + int rgb_src = LittleEndian.ToInt32 (m_output, rgb_pos); + bool skip = 0 == LittleEndian.ToInt32 (m_output, skip_pos); int dst = 0; while (dst < rgb.Length) { @@ -280,10 +286,9 @@ namespace GameRes.Formats.TopCat } } - void UnpackSpd8Alpha (byte[] rgb) + void UnpackSpdAlpha (byte[] rgb, int ctl_src) { int rgb_src = LittleEndian.ToInt32 (m_output, 0); - int ctl_src = 8; int dst = 0; while (dst < rgb.Length) { @@ -309,7 +314,7 @@ namespace GameRes.Formats.TopCat rgb[dst++] = m_output[rgb_src++]; rgb[dst++] = m_output[rgb_src++]; rgb[dst++] = m_output[rgb_src++]; - rgb[dst++] = (byte)~(control - 1); + rgb[dst++] = (byte)-control; } } }