From f4e5fc60294d308cead81095e3cc48dac67aef18 Mon Sep 17 00:00:00 2001 From: morkt Date: Thu, 16 Nov 2017 11:34:52 +0400 Subject: [PATCH] implemented Tamamo PCK archives. --- ArcFormats/ArcFormats.csproj | 8 + ArcFormats/Properties/Settings.Designer.cs | 12 + ArcFormats/Properties/Settings.settings | 3 + ArcFormats/Tamamo/ArcPCK.cs | 260 +++++++++++++++++++++ ArcFormats/Tamamo/WidgetPCK.xaml | 8 + ArcFormats/Tamamo/WidgetPCK.xaml.cs | 17 ++ ArcFormats/app.config | 67 +++--- 7 files changed, 343 insertions(+), 32 deletions(-) create mode 100644 ArcFormats/Tamamo/ArcPCK.cs create mode 100644 ArcFormats/Tamamo/WidgetPCK.xaml create mode 100644 ArcFormats/Tamamo/WidgetPCK.xaml.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 695a8e60..77cbb52b 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -388,6 +388,10 @@ WidgetTactics.xaml + + + WidgetPCK.xaml + @@ -870,6 +874,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs index 8b229ab8..3f70c6e1 100644 --- a/ArcFormats/Properties/Settings.Designer.cs +++ b/ArcFormats/Properties/Settings.Designer.cs @@ -669,5 +669,17 @@ namespace GameRes.Formats.Properties { this["FJSYSPassword"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string PCKTitle { + get { + return ((string)(this["PCKTitle"])); + } + set { + this["PCKTitle"] = value; + } + } } } diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings index 5c698f28..9d00bf70 100644 --- a/ArcFormats/Properties/Settings.settings +++ b/ArcFormats/Properties/Settings.settings @@ -164,5 +164,8 @@ + + + \ No newline at end of file diff --git a/ArcFormats/Tamamo/ArcPCK.cs b/ArcFormats/Tamamo/ArcPCK.cs new file mode 100644 index 00000000..4be5b90e --- /dev/null +++ b/ArcFormats/Tamamo/ArcPCK.cs @@ -0,0 +1,260 @@ +//! \file ArcPCK.cs +//! \date 2017 Nov 16 +//! \brief TamamoSystem 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; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Cryptography; +using GameRes.Formats.Properties; +using GameRes.Formats.Strings; + +namespace GameRes.Formats.Tamamo +{ + [Serializable] + public class PckScheme : ResourceScheme + { + public Dictionary KnownKeys; + } + + public class PckOptions : ResourceOptions + { + public byte[] Key { get; set; } + } + + internal class PckArchive : ArcFile + { + public readonly Blowfish Encryption; + + public PckArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte[] key) + : base (arc, impl, dir) + { + Encryption = new Blowfish (key); + } + } + + [Export(typeof(ArchiveFormat))] + public class PckOpener : ArchiveFormat + { + public override string Tag { get { return "PCK/TAMAMO"; } } + public override string Description { get { return "TamamoSystem resource archive"; } } + public override uint Signature { get { return 0x4B434150; } } // 'PACK' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (4, "_FILE001")) + return null; + int count = file.View.ReadInt32 (0xC); + if (!IsSaneCount (count)) + return null; + var key = QueryKey (file.Name); + if (null == key) + return null; + uint index_length = file.View.ReadUInt32 (0x10); + var index = file.View.ReadBytes (0x14, index_length); + if (index.Length != index_length) + return null; + var bf = new Blowfish (key); + bf.Decipher (index, index.Length); + + long data_offset = 0x14 + index_length; + int pos = 0; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + uint size = index.ToUInt32 (pos); + pos += 4; + int name_end = Array.IndexOf (index, 0, pos); + if (-1 == name_end) + return null; + var name = Encodings.cp932.GetString (index, pos, name_end-pos); + pos = name_end+1; + uint enc_size = index.ToUInt32 (pos); + pos += 4; + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = data_offset; + entry.Size = enc_size; + entry.UnpackedSize = size; + entry.IsPacked = enc_size != size; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + data_offset += enc_size + 1; + } + return new PckArchive (file, this, dir, key); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var parc = (PckArchive)arc; + var pent = (PackedEntry)entry; + byte data_type = arc.File.View.ReadByte (pent.Offset); + Stream input = arc.File.CreateStream (pent.Offset+1, pent.Size); + if (data_type != 0) + { + input = new InputCryptoStream (input, parc.Encryption.CreateDecryptor()); + if (data_type != 3) + { + input = new LimitStream (input, pent.UnpackedSize); + } + else + { +// input = new Bzip2InputStream (input); // requires #ZipLib/DonNetZip + } + } + return input; + } + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var decoder = base.OpenImage (arc, entry); +// if (!entry.Name.StartsWith ("ev")) // XXX +// return decoder; + if (1024 != decoder.Info.Width || 512 != decoder.Info.Height) + return decoder; + var source = decoder.Image.Bitmap; + if (PixelFormats.Bgra32 == source.Format) + return decoder; + using (decoder) + { + var ev_bitmap = new WriteableBitmap (800, 600, ImageData.DefaultDpiX, ImageData.DefaultDpiY, + source.Format, source.Palette); + var rect = new Int32Rect (0, 0, 800, 512); + int buffer_size = ev_bitmap.BackBufferStride * ev_bitmap.PixelHeight; + ev_bitmap.Lock(); + source.CopyPixels (rect, ev_bitmap.BackBuffer, buffer_size, ev_bitmap.BackBufferStride); + ev_bitmap.AddDirtyRect (rect); + ev_bitmap.Unlock(); + CopyRegion (source, new Int32Rect (800, 0, 88, 400), ev_bitmap, 0, 512); + CopyRegion (source, new Int32Rect (888, 0, 88, 400), ev_bitmap, 400, 512); + return new BitmapSourceDecoder (ev_bitmap); + } + } + + void CopyRegion (BitmapSource source, Int32Rect rect, WriteableBitmap target, int dst_x, int dst_y) + { + int bpp = source.Format.BitsPerPixel / 8; + int width = rect.Width; + int height = rect.Height; + int src_stride = width * bpp; + var src_pixels = new byte[src_stride * height]; + source.CopyPixels (rect, src_pixels, src_stride, 0); + + var transformed = new byte[src_pixels.Length]; + int dst_stride = height * bpp; + for (int y = 0; y < height; ++y) + { + int src = y * src_stride; + int dst = (width - 1) * dst_stride + y * bpp; + for (int x = 0; x < width; ++x) + { + for (int p = 0; p < bpp; ++p) + { + transformed[dst+p] = src_pixels[src+p]; + } + src += bpp; + dst -= dst_stride; + } + } + var target_rect = new Int32Rect (0, 0, height, width); + target.WritePixels (target_rect, transformed, dst_stride, dst_x, dst_y); + } + + PckScheme DefaultScheme = new PckScheme { + KnownKeys = new Dictionary() + }; + + public Dictionary KnownKeys { get { return DefaultScheme.KnownKeys; } } + + public override ResourceScheme Scheme + { + get { return DefaultScheme; } + set { DefaultScheme = (PckScheme)value; } + } + + public override object GetAccessWidget () + { + return new GUI.WidgetPCK (KnownKeys.Keys.OrderBy (x => x)); + } + + public override ResourceOptions GetDefaultOptions () + { + return new PckOptions { + Key = GetKeyForTitle (Settings.Default.PCKTitle) + }; + } + + byte[] QueryKey (string arc_name) + { + if (0 == KnownKeys.Count) + return null; + if (1 == KnownKeys.Count) + return KnownKeys.Values.First(); + var title = FormatCatalog.Instance.LookupGame (arc_name); + var key = GetKeyForTitle (title); + if (key != null) + return key; + var options = Query (arcStrings.ArcEncryptedNotice); + return options.Key; + } + + byte[] GetKeyForTitle (string title) + { + byte[] key = null; + if (!string.IsNullOrEmpty (title)) + KnownKeys.TryGetValue (title, out key); + return key; + } + } + + internal class BitmapSourceDecoder : IImageDecoder + { + public Stream Source { get { return null; } } + public ImageFormat SourceFormat { get { return null; } } + public ImageMetaData Info { get; private set; } + public ImageData Image { get; private set; } + + public BitmapSourceDecoder (BitmapSource bitmap) + { + Info = new ImageMetaData { + Width = (uint)bitmap.PixelWidth, + Height = (uint)bitmap.PixelHeight, + BPP = bitmap.Format.BitsPerPixel, + }; + Image = new ImageData (bitmap); + } + + public void Dispose () + { + } + } +} diff --git a/ArcFormats/Tamamo/WidgetPCK.xaml b/ArcFormats/Tamamo/WidgetPCK.xaml new file mode 100644 index 00000000..7514f291 --- /dev/null +++ b/ArcFormats/Tamamo/WidgetPCK.xaml @@ -0,0 +1,8 @@ + + + diff --git a/ArcFormats/Tamamo/WidgetPCK.xaml.cs b/ArcFormats/Tamamo/WidgetPCK.xaml.cs new file mode 100644 index 00000000..3aba2827 --- /dev/null +++ b/ArcFormats/Tamamo/WidgetPCK.xaml.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Windows.Controls; + +namespace GameRes.Formats.GUI +{ + /// + /// Interaction logic for WidgetPCK.xaml + /// + public partial class WidgetPCK : StackPanel + { + public WidgetPCK (IEnumerable keys) + { + InitializeComponent (); + Title.ItemsSource = keys; + } + } +} diff --git a/ArcFormats/app.config b/ArcFormats/app.config index 7527f3ea..d89e63c0 100644 --- a/ArcFormats/app.config +++ b/ArcFormats/app.config @@ -8,10 +8,10 @@ - + - + 4294967295 @@ -35,7 +35,7 @@ None - + False @@ -62,22 +62,22 @@ 8 - + - + - + Default - + - + 65432 @@ -86,37 +86,37 @@ 1139247708 - + - + - + 0 - + - + - + - + - + - + - + Optimal @@ -125,46 +125,49 @@ 932 - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + +