From 982d6fb2b1f207822edf8775b7e644aea4a78241 Mon Sep 17 00:00:00 2001 From: morkt Date: Sun, 10 Dec 2017 14:14:58 +0400 Subject: [PATCH] implemented DNS archives and 'YP' images. --- ArcFormats/ArcFormats.csproj | 2 + ArcFormats/Marble/ArcDNS.cs | 82 ++++++++++++++++++++++ ArcFormats/Marble/ArcMBL.cs | 12 +++- ArcFormats/Marble/ImageYP.cs | 128 +++++++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 ArcFormats/Marble/ArcDNS.cs create mode 100644 ArcFormats/Marble/ImageYP.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index e622db77..8b95cd9a 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -135,6 +135,8 @@ + + diff --git a/ArcFormats/Marble/ArcDNS.cs b/ArcFormats/Marble/ArcDNS.cs new file mode 100644 index 00000000..59866a51 --- /dev/null +++ b/ArcFormats/Marble/ArcDNS.cs @@ -0,0 +1,82 @@ +//! \file ArcDNS.cs +//! \date 2017 Dec 10 +//! \brief DarkNiteSystem resource archive. +// +// Copyright (C) 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 +// 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.DarkNiteSystem +{ + [Export(typeof(ArchiveFormat))] + public class DnsOpener : ArchiveFormat + { + public override string Tag { get { return "DNS"; } } + public override string Description { get { return "DarkNiteSystem resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.Name.EndsWith ("data.dns", StringComparison.InvariantCultureIgnoreCase)) + return null; + if (file.View.ReadUInt32 (8) != 0x8000) // first file offset + return null; + + var dir = new List(); + uint index_offset = 0; + while (index_offset < 0x8000) + { + var name = file.View.ReadString (index_offset, 8); + if (0 == name.Length) + break; + var entry = new Entry { + Name = Path.ChangeExtension (name, "S"), + Type = "script", + Offset = file.View.ReadUInt32 (index_offset+8), + Size = file.View.ReadUInt32 (index_offset+12), + }; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x10; + } + if (0 == dir.Count) + return null; + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var data = arc.File.View.ReadBytes (entry.Offset, entry.Size); + for (int i = 0; i < data.Length; ++i) + { + data[i] = (byte)-data[i]; + } + return new BinMemoryStream (data, entry.Name); + } + } +} diff --git a/ArcFormats/Marble/ArcMBL.cs b/ArcFormats/Marble/ArcMBL.cs index 08c60646..cd21f09b 100644 --- a/ArcFormats/Marble/ArcMBL.cs +++ b/ArcFormats/Marble/ArcMBL.cs @@ -65,6 +65,11 @@ namespace GameRes.Formats.Marble public override bool IsHierarchic { get { return false; } } public override bool CanWrite { get { return false; } } + public MblOpener () + { + Extensions = new string[] { "mbl", "dns" }; + } + public override ArcFile TryOpen (ArcView file) { int count = file.View.ReadInt32 (0); @@ -81,7 +86,8 @@ namespace GameRes.Formats.Marble return arc; } - static readonly Lazy PrsFormat = new Lazy (() => ImageFormat.FindByTag ("PRS")); + static readonly ResourceInstance PrsFormat = new ResourceInstance ("PRS"); + static readonly ResourceInstance YpFormat = new ResourceInstance ("PRS/YP"); private ArcFile ReadIndex (ArcView file, int count, uint filename_len, uint index_offset) { @@ -121,8 +127,10 @@ namespace GameRes.Formats.Marble { entry = new AutoEntry (name, () => { uint signature = file.View.ReadUInt32 (offset); - if (0x4259 == (0xffff & signature)) + if (0x4259 == (0xFFFF & signature)) return PrsFormat.Value; + else if (0x5059 == (0xFFFF & signature)) + return YpFormat.Value; else if (0 != signature) return FormatCatalog.Instance.LookupSignature (signature).FirstOrDefault(); else diff --git a/ArcFormats/Marble/ImageYP.cs b/ArcFormats/Marble/ImageYP.cs new file mode 100644 index 00000000..7130ff1e --- /dev/null +++ b/ArcFormats/Marble/ImageYP.cs @@ -0,0 +1,128 @@ +//! \file ImageYP.cs +//! \date 2017 Dec 10 +//! \brief DarkNiteSystem image format. +// +// Copyright (C) 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 +// 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.ComponentModel.Composition; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.DarkNiteSystem +{ + internal class YpMetaData : ImageMetaData + { + public int UnpackedSize; + } + + /// + /// Marble YB image format predecessor. + /// + [Export(typeof(ImageFormat))] + public class YpFormat : ImageFormat + { + public override string Tag { get { return "PRS/YP"; } } + public override string Description { get { return "DarkNiteSystem image format"; } } + public override uint Signature { get { return 0; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (8); + if (!header.AsciiEqual ("YP")) + return null; + int unpacked_size = header.ToInt24 (2); + int packed_size = header.ToInt24 (5); + var data = LzUnpack (file, 0x36); + using (var bmp = new BinMemoryStream (data, file.Name)) + { + var info = Bmp.ReadMetaData (bmp); + if (null == info) + return info; + return new YpMetaData { + Width = info.Width, + Height = info.Height, + BPP = info.BPP, + UnpackedSize = unpacked_size, + }; + } + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (YpMetaData)info; + file.Position = 8; + var data = LzUnpack (file, meta.UnpackedSize); + using (var bmp = new BinMemoryStream (data, file.Name)) + return Bmp.Read (bmp, info); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("YpFormat.Write not implemented"); + } + + byte[] LzUnpack (IBinaryStream input, int unpacked_size) + { + var output = new byte[unpacked_size]; + var frame = new byte[0x4000]; + int dst = 0; + int ctl_bits = 0; + byte ctl_mask = 0; + int frame_pos = 0; + while (dst < unpacked_size) + { + if (0 == ctl_mask) + { + ctl_bits = input.ReadByte(); + if (-1 == ctl_bits) + break; + ctl_mask = 0x80; + } + if (0 != (ctl_bits & ctl_mask)) + { + byte lo = input.ReadUInt8(); + byte hi = input.ReadUInt8(); + int count = Math.Min (CountTable[lo & 0xF], unpacked_size - dst); + int offset = hi << 4 | lo >> 4; + int src = frame_pos - offset; + for (int i = 0; i < count; ++i) + { + byte v = frame[src++ & 0x3FFF]; + output[dst++] = v; + frame[frame_pos++ & 0x3FFF] = v; + } + } + else + { + byte v = input.ReadUInt8(); + output[dst++] = v; + frame[frame_pos++ & 0x3FFF] = v; + } + ctl_mask >>= 1; + } + return output; + } + + static readonly byte[] CountTable = { 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xE, 0x10, 0x18, 0x20, 0x40, 0x80 }; + } +}