diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index d351cbda..2bcd592d 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -77,6 +77,8 @@ + + WidgetMCG.xaml diff --git a/ArcFormats/FC01/ArcMCA.cs b/ArcFormats/FC01/ArcMCA.cs new file mode 100644 index 00000000..c03da904 --- /dev/null +++ b/ArcFormats/FC01/ArcMCA.cs @@ -0,0 +1,132 @@ +//! \file ArcMCA.cs +//! \date Sun Dec 06 19:12:34 2015 +//! \brief F&C Co. multi-frame image format. +// +// Copyright (C) 2015 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 GameRes.Formats.Properties; +using GameRes.Formats.Strings; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.FC01 +{ + internal class McaArchive : ArcFile + { + public readonly byte Key; + + public McaArchive (ArcView arc, ArchiveFormat impl, ICollection dir, byte key) + : base (arc, impl, dir) + { + Key = key; + } + } + + [Export(typeof(ArchiveFormat))] + public class McaOpener : ArchiveFormat + { + public override string Tag { get { return "MCA"; } } + public override string Description { get { return "F&C Co. multi-frame image format"; } } + public override uint Signature { get { return 0x2041434D; } } // 'MCA' + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + uint index_offset = file.View.ReadUInt32 (0x10); + int count = file.View.ReadInt32 (0x20); + if (index_offset >= file.MaxOffset || !IsSaneCount (count)) + return null; + + string base_name = Path.GetFileNameWithoutExtension (file.Name); + long next_offset = file.View.ReadUInt32 (index_offset); + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + if (next_offset > file.MaxOffset || next_offset <= index_offset) + return null; + index_offset += 4; + var entry = new Entry + { + Name = string.Format ("{0}#{1:D4}.tga", base_name, i), + Offset = next_offset, + Type = "image", + }; + next_offset = i+1 == count ? file.MaxOffset : file.View.ReadUInt32 (index_offset); + entry.Size = (uint)(next_offset - entry.Offset); + if (entry.Size > 0x20) + dir.Add (entry); + } + if (0 == dir.Count) + return null; + var options = Query (arcStrings.MCAEncryptedNotice); + return new McaArchive (file, this, dir, options.Key); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + var mca = arc as McaArchive; + int method = arc.File.View.ReadInt32 (entry.Offset); + if (null == mca || method < 0 || method > 1) + return base.OpenEntry (arc, entry); + uint width = arc.File.View.ReadUInt32 (entry.Offset+0xC); + uint height = arc.File.View.ReadUInt32 (entry.Offset+0x10); + uint packed_size = arc.File.View.ReadUInt32 (entry.Offset+0x14); + int unpacked_size = arc.File.View.ReadInt32 (entry.Offset+0x18); + + var data = arc.File.View.ReadBytes (entry.Offset+0x20, packed_size); + MrgOpener.Decrypt (data, 0, data.Length, mca.Key); + if (method > 0) + { + using (var input = new MemoryStream (data)) + using (var lzss = new MrgLzssReader (input, data.Length, unpacked_size)) + { + lzss.Unpack(); + data = lzss.Data; + } + } + int stride = ((int)width * 3 + 3) & ~3; + var info = new ImageMetaData { Width = width, Height = height, BPP = 24 }; + return TgaStream.Create (info, stride, data); + } + + public override ResourceOptions GetDefaultOptions () + { + return new McgOptions { Key = Settings.Default.MCGLastKey }; + } + + public override ResourceOptions GetOptions (object widget) + { + var w = widget as GUI.WidgetMCG; + if (null != w) + Settings.Default.MCGLastKey = w.GetKey(); + return GetDefaultOptions(); + } + + public override object GetAccessWidget () + { + return new GUI.WidgetMCG(); + } + } +} diff --git a/ArcFormats/FC01/ImageCLM.cs b/ArcFormats/FC01/ImageCLM.cs new file mode 100644 index 00000000..7196ed78 --- /dev/null +++ b/ArcFormats/FC01/ImageCLM.cs @@ -0,0 +1,116 @@ +//! \file ImageCLM.cs +//! \date Sun Dec 06 20:56:50 2015 +//! \brief F&C Co. image format. +// +// Copyright (C) 2015 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 GameRes.Utility; +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace GameRes.Formats.FC01 +{ + internal class ClmMetaData : ImageMetaData + { + public int UnpackedSize; + public uint DataOffset; + } + + [Export(typeof(ImageFormat))] + public class ClmFormat : ImageFormat + { + public override string Tag { get { return "CLM"; } } + public override string Description { get { return "F&C Co. image format"; } } + public override uint Signature { get { return 0x204D4C43; } } // 'CLM' + + public override ImageMetaData ReadMetaData (Stream stream) + { + var header = new byte[0x40]; + if (header.Length != stream.Read (header, 0, header.Length)) + return null; + if (!Binary.AsciiEqual (header, 4, "1.00")) + return null; + uint data_offset = LittleEndian.ToUInt32 (header, 0x10); + if (data_offset < 0x40) + return null; + uint width = LittleEndian.ToUInt32 (header, 0x1C); + uint height = LittleEndian.ToUInt32 (header, 0x20); + int bpp = LittleEndian.ToInt32 (header, 0x24); + int unpacked_size = LittleEndian.ToInt32 (header, 0x28); + return new ClmMetaData + { + Width = width, + Height = height, + BPP = bpp, + UnpackedSize = unpacked_size, + DataOffset = data_offset, + }; + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var meta = (ClmMetaData)info; + stream.Position = meta.DataOffset; + PixelFormat format; + BitmapPalette palette = null; + if (8 == meta.BPP) + { + format = PixelFormats.Indexed8; + palette = ReadPalette (stream); + } + else if (24 == meta.BPP) + format = PixelFormats.Bgr24; + else if (32 == meta.BPP) + format = PixelFormats.Bgr32; + else + throw new NotSupportedException ("Not supported CLM color depth"); + int packed_size = (int)(stream.Length - stream.Position); + using (var reader = new MrgLzssReader (stream, packed_size, meta.UnpackedSize)) + { + reader.Unpack(); + return ImageData.Create (info, format, palette, reader.Data); + } + } + + BitmapPalette ReadPalette (Stream input) + { + var palette_data = new byte[0x400]; + if (palette_data.Length != input.Read (palette_data, 0, palette_data.Length)) + throw new InvalidFormatException(); + var palette = new Color[0x100]; + for (int i = 0; i < palette.Length; ++i) + { + int c = i * 4; + palette[i] = Color.FromRgb (palette_data[c+2], palette_data[c+1], palette_data[c]); + } + return new BitmapPalette (palette); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("ClmFormat.Write not implemented"); + } + } +} diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs index 14bd1274..915e6f43 100644 --- a/ArcFormats/Strings/arcStrings.Designer.cs +++ b/ArcFormats/Strings/arcStrings.Designer.cs @@ -359,6 +359,15 @@ namespace GameRes.Formats.Strings { } } + /// + /// Looks up a localized string similar to Archive content is encrypted.. + /// + public static string MCAEncryptedNotice { + get { + return ResourceManager.GetString("MCAEncryptedNotice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Choose title or enter a key. /// diff --git a/ArcFormats/Strings/arcStrings.ko-KR.resx b/ArcFormats/Strings/arcStrings.ko-KR.resx index b97e3406..3bb88488 100644 --- a/ArcFormats/Strings/arcStrings.ko-KR.resx +++ b/ArcFormats/Strings/arcStrings.ko-KR.resx @@ -365,4 +365,7 @@ 8-bit 암호값 + + 아카이브 내용이 암호화됨. + \ No newline at end of file diff --git a/ArcFormats/Strings/arcStrings.resx b/ArcFormats/Strings/arcStrings.resx index 0083bafd..80cd656c 100644 --- a/ArcFormats/Strings/arcStrings.resx +++ b/ArcFormats/Strings/arcStrings.resx @@ -361,4 +361,7 @@ Choose appropriate encryption scheme. 8-bit encryption key + + Archive content is encrypted. + \ No newline at end of file diff --git a/ArcFormats/Strings/arcStrings.ru-RU.resx b/ArcFormats/Strings/arcStrings.ru-RU.resx index 9a4a36bc..dac6abf3 100644 --- a/ArcFormats/Strings/arcStrings.ru-RU.resx +++ b/ArcFormats/Strings/arcStrings.ru-RU.resx @@ -200,6 +200,9 @@ Архив содержит зашифрованные скрипты. Выберите способ шифрования или введите текстовый пароль. + + Содержимое архива зашифровано. + Выберите наименование или введите ключ