From 9bd7f743dd468f44556d02f3cbdd36d45ac0004e Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 18 Oct 2018 23:05:35 +0400 Subject: [PATCH] implemented MPK archives and LAY images. --- ArcFormats/ArcFormats.csproj | 5 + ArcFormats/NitroPlus/ArcLAY.cs | 175 +++++++++++++++++++++++++++++++++ ArcFormats/NitroPlus/ArcMPK.cs | 62 ++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 ArcFormats/NitroPlus/ArcLAY.cs create mode 100644 ArcFormats/NitroPlus/ArcMPK.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 2f1c6614..59156cd5 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -209,6 +209,8 @@ + + @@ -265,7 +267,10 @@ + + + diff --git a/ArcFormats/NitroPlus/ArcLAY.cs b/ArcFormats/NitroPlus/ArcLAY.cs new file mode 100644 index 00000000..7869257f --- /dev/null +++ b/ArcFormats/NitroPlus/ArcLAY.cs @@ -0,0 +1,175 @@ +//! \file ArcLAY.cs +//! \date 2018 Oct 18 +//! \brief MAGES engine composite layers. +// +// 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; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace GameRes.Formats.NitroPlus +{ + internal class LayEntry : Entry + { + public uint Id; + public int First; + public int Count; + } + + internal class LayCoord + { + public float TargetX, TargetY; + public float SourceX, SourceY; + } + + internal class LayArchive : ArcFile + { + public readonly BitmapSource Source; + public readonly IList Tiles; + public readonly IDictionary LayerMap; + + public LayArchive (ArcView arc, ArchiveFormat impl, ICollection dir, BitmapSource image, IList tiles) + : base (arc, impl, dir) + { + Source = image; + Tiles = tiles; + LayerMap = new Dictionary (dir.Count); + foreach (LayEntry entry in dir) + { + LayerMap[entry.Id] = entry; + } + } + + public IEnumerable GetTiles (LayEntry layer) + { + return Tiles.Skip (layer.First).Take (layer.Count); + } + } + + [Export(typeof(ArchiveFormat))] + public class LayOpener : ArchiveFormat + { + public override string Tag { get { return "LAY/MAGES"; } } + public override string Description { get { return "MAGES engine composite image archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + const int DefaultWidth = 1920; + const int DefaultHeight = 1080; + const int BlockSize = 32; + + public override ArcFile TryOpen (ArcView file) + { + if (!file.Name.HasExtension (".lay")) + return null; + int tile_count = file.View.ReadInt32 (0); + int coord_count = file.View.ReadInt32 (4); + if (!IsSaneCount (tile_count) || !IsSaneCount (coord_count)) + return null; + var base_name = Path.GetFileNameWithoutExtension (file.Name).TrimEnd ('_'); + var png_name = VFS.ChangeFileName (file.Name, base_name + ".png"); + if (!VFS.FileExists (png_name)) + return null; + ImageData image; + var png_entry = VFS.FindFile (png_name); + using (var decoder = VFS.OpenImage (png_entry)) + image = decoder.Image; + using (var input = file.CreateStream()) + using (var index = new BinaryReader (input)) + { + input.Position = 8; + var dir = new List (tile_count); + for (int i = 0; i < tile_count; ++i) + { + uint id = index.ReadUInt32(); + int first = index.ReadInt32(); + int count = index.ReadInt32(); + var name = string.Format ("{0}#{1:X8}", base_name, id); + var entry = new LayEntry { + Name = name, Type = "image", Offset = 0, + Id = id, First = first, Count = count + }; + dir.Add (entry); + } + var tiles = new List (coord_count); + for (int i = 0; i < coord_count; ++i) + { + var tile = new LayCoord(); + tile.TargetX = index.ReadSingle() + 1; + tile.TargetY = index.ReadSingle() + 1; + tile.SourceX = index.ReadSingle() - 1; + tile.SourceY = index.ReadSingle() - 1; + tiles.Add (tile); + } + return new LayArchive (file, this, dir, image.Bitmap, tiles); + } + } + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var lent = (LayEntry)entry; + var larc = (LayArchive)arc; + var visual = new DrawingVisual(); + using (var context = visual.RenderOpen()) + { + if (lent.Id != 1) + { + LayEntry base_layer; + if (larc.LayerMap.TryGetValue (1, out base_layer)) + DrawLayer (context, larc.Source, larc.GetTiles (base_layer)); + } + if ((lent.Id >> 28) == 4) + { + uint face_id = (lent.Id >> 8) & 0xF | 0x20000000; + LayEntry face_layer; + if (larc.LayerMap.TryGetValue (face_id, out face_layer) || + larc.LayerMap.TryGetValue (face_id-1, out face_layer)) + DrawLayer (context, larc.Source, larc.GetTiles (face_layer)); + } + DrawLayer (context, larc.Source, larc.GetTiles (lent)); + } + var bmp = new RenderTargetBitmap (DefaultWidth, DefaultHeight, ImageData.DefaultDpiX, + ImageData.DefaultDpiY, PixelFormats.Pbgra32); + bmp.Render (visual); + return new BitmapSourceDecoder (bmp); + } + + void DrawLayer (DrawingContext context, BitmapSource source, IEnumerable tiles) + { + foreach (var coord in tiles) + { + var src_rect = new Int32Rect ((int)coord.SourceX, (int)coord.SourceY, BlockSize, BlockSize); + var tile = new CroppedBitmap (source, src_rect); + var dst_rect = new Rect (coord.TargetX + DefaultWidth / 2, coord.TargetY + DefaultHeight / 2, + BlockSize, BlockSize); + context.DrawImage (tile, dst_rect); + } + } + } +} diff --git a/ArcFormats/NitroPlus/ArcMPK.cs b/ArcFormats/NitroPlus/ArcMPK.cs new file mode 100644 index 00000000..e7989f8f --- /dev/null +++ b/ArcFormats/NitroPlus/ArcMPK.cs @@ -0,0 +1,62 @@ +//! \file ArcMPK.cs +//! \date 2018 Oct 18 +//! \brief MAGES engine archive implementation. +// +// 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.Collections.Generic; +using System.ComponentModel.Composition; + +namespace GameRes.Formats.NitroPlus +{ + [Export(typeof(ArchiveFormat))] + public class MpkOpener : ArchiveFormat + { + public override string Tag { get { return "MPK/MAGES"; } } + public override string Description { get { return "MAGES engine resource archive"; } } + public override uint Signature { get { return 0x4B504D; } } // 'MPK' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (8); + if (!IsSaneCount (count)) + return null; + uint index_offset = 0x48; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset+0x18, 0xE0); + var entry = Create (name); + entry.Offset = file.View.ReadInt64 (index_offset); + entry.Size = file.View.ReadUInt32 (index_offset+8); + entry.UnpackedSize = file.View.ReadUInt32 (index_offset+0x10); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x100; + } + return new ArcFile (file, this, dir); + } + } +}