diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c8792a27..4c12366b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: run: nuget restore # Build the solution - name: Build - run: msbuild /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFrameworkVersion=v4.6.2 GARbro.sln + run: msbuild /p:Configuration=Release /p:Platform="Any CPU" GARbro.sln # Publish the artifacts - name: Publish Artifact if: success() diff --git a/ArcFormats/AIRNovel/ArcAIR.cs b/ArcFormats/AIRNovel/ArcAIR.cs index addbe36c..980783fd 100644 --- a/ArcFormats/AIRNovel/ArcAIR.cs +++ b/ArcFormats/AIRNovel/ArcAIR.cs @@ -89,8 +89,8 @@ namespace GameRes.Formats.AirNovel SharpZip.ZipFile zip = null; try { - SharpZip.ZipStrings.CodePage = Encoding.UTF8.CodePage; - zip = new SharpZip.ZipFile (input); + var sc = SharpZip.StringCodec.FromCodePage (Encoding.UTF8.CodePage); + zip = new SharpZip.ZipFile (input, false, sc); var files = zip.Cast().Where (z => !z.IsDirectory); bool has_encrypted = false; var dir = new List(); diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 859bbce1..2aa2aa8c 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -9,7 +9,7 @@ Properties GameRes.Formats ArcFormats - v4.6.1 + v4.7.2 512 ..\ @@ -51,61 +51,95 @@ MinimumRecommendedRules.ruleset - - ..\packages\Crc32C.NET.1.0.5.0\lib\net20\Crc32C.NET.dll + + ..\packages\BCnEncoder.Net.2.3.0\lib\netstandard2.0\BCnEncoder.dll - - ..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll + + ..\packages\CommunityToolkit.HighPerformance.8.4.0\lib\netstandard2.0\CommunityToolkit.HighPerformance.dll - - ..\packages\NAudio.1.7.3\lib\net35\NAudio.dll + + ..\packages\SharpZipLib.1.4.2\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll - - ..\packages\NVorbis.0.10.4\lib\net45\NVorbis.dll + + ..\packages\K4os.Compression.LZ4.1.3.8\lib\net462\K4os.Compression.LZ4.dll + + + ..\packages\Microsoft.Bcl.HashCode.6.0.0\lib\net462\Microsoft.Bcl.HashCode.dll + + + ..\packages\Microsoft.Bcl.Numerics.10.0.5\lib\net462\Microsoft.Bcl.Numerics.dll + + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll + + + ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll + + + ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll + + + ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll + + + ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll + + + ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll + + + ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + + + ..\packages\NVorbis.0.10.5\lib\net45\NVorbis.dll - - ..\packages\Snappy.NET.1.1.1.8\lib\net45\Snappy.NET.dll + + ..\packages\SixLabors.ImageSharp.2.1.13\lib\net472\SixLabors.ImageSharp.dll + + + ..\packages\Snappier.1.3.0\lib\net472\Snappier.dll - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll - - - ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - - ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll + + ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll - - ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll - - ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + ..\packages\System.Text.Encoding.CodePages.10.0.5\lib\net462\System.Text.Encoding.CodePages.dll + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll + + @@ -113,8 +147,8 @@ - - ..\packages\ZstdNet.1.4.5\lib\net45\ZstdNet.dll + + ..\packages\ZstdSharp.Port.0.8.7\lib\net462\ZstdSharp.dll @@ -142,6 +176,7 @@ + @@ -157,12 +192,15 @@ WidgetDXA.xaml + + + @@ -172,6 +210,7 @@ + @@ -234,6 +273,7 @@ + @@ -244,8 +284,10 @@ + + @@ -939,6 +981,7 @@ + @@ -1381,18 +1424,11 @@ exit 0 if not exist "$(TargetDir)\GameData" mkdir "$(TargetDir)\GameData" xcopy "$(ProjectDir)\Resources\*" "$(TargetDir)\GameData\" /D /Y >NUL - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - + \ No newline at end of file diff --git a/ArcFormats/Artemis/ImageNekoPNG.cs b/ArcFormats/Artemis/ImageNekoPNG.cs index e70af69d..cf95a60a 100644 --- a/ArcFormats/Artemis/ImageNekoPNG.cs +++ b/ArcFormats/Artemis/ImageNekoPNG.cs @@ -65,10 +65,10 @@ namespace GameRes.Formats.Artemis data[j] -= (byte)(0x77 * j); var slice_width = 0; var slice_height = 0; - if (1 != WebPGetInfo (data, (UIntPtr)data.Length, ref slice_width, ref slice_height)) + if (1 != WebPCodec.WebPGetInfo(data, (UIntPtr)data.Length, ref slice_width, ref slice_height)) throw new InvalidFormatException("WebP image decoder failed."); var slice_size = stride * slice_height; - if (IntPtr.Zero == WebPDecodeBGRAInto (data, (UIntPtr)data.Length, output, (UIntPtr)slice_size, stride)) + if (IntPtr.Zero == WebPCodec.WebPDecodeBGRAInto (data, (UIntPtr)data.Length, output, (UIntPtr)slice_size, stride)) throw new InvalidFormatException("WebP image decoder failed."); output += slice_size; } @@ -94,7 +94,7 @@ namespace GameRes.Formats.Artemis offset[i] = file.ReadInt32(); for (var i = 0; i < 8; i++) length[i] = file.ReadInt32(); - LibWebPLoader.Load(); + WebPCodec.Load(); var image_width = 0; var image_height = 0; for (var i = 0; i < 8; i++) @@ -111,7 +111,7 @@ namespace GameRes.Formats.Artemis webp_header[j] -= (byte)(0x77 * j); var slice_width = 0; var slice_height = 0; - if (1 != WebPGetInfo (webp_header, (UIntPtr)webp_header.Length, ref slice_width, ref slice_height)) + if (1 != WebPCodec.WebPGetInfo (webp_header, (UIntPtr)webp_header.Length, ref slice_width, ref slice_height)) throw new InvalidFormatException ("WebP image decoder failed."); image_width = Math.Max (image_width, slice_width); image_height += slice_height; @@ -130,36 +130,5 @@ namespace GameRes.Formats.Artemis { throw new NotImplementedException("ImageNekoPNG.Write not implemented"); } - - [DllImport("libwebp.dll", EntryPoint = "WebPGetInfo", CallingConvention = CallingConvention.Cdecl)] - public static extern int WebPGetInfo ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, ref int width, ref int height); - - [DllImport("libwebp.dll", EntryPoint = "WebPDecodeBGRAInto", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr WebPDecodeBGRAInto ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride); - } - - internal static class LibWebPLoader - { - [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] - static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hReservedNull, uint dwFlags); - - static bool loaded = false; - - const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100; - const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; - - public static void Load () - { - if (loaded) - return; - var folder = Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location); - folder = Path.Combine (folder, (IntPtr.Size == 4) ? "x86" : "x64"); - var fullPath = Path.Combine (folder, "libwebp.dll"); - fullPath = Path.GetFullPath (fullPath); - var handle = LoadLibraryEx (fullPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); - if (IntPtr.Zero == handle) - throw new Win32Exception (Marshal.GetLastWin32Error()); - loaded = true; - } } } diff --git a/ArcFormats/AudioWMA.cs b/ArcFormats/AudioWMA.cs index dca846a3..f80ffe3d 100644 --- a/ArcFormats/AudioWMA.cs +++ b/ArcFormats/AudioWMA.cs @@ -136,7 +136,7 @@ namespace GameRes.Formats private static long GetDuration (IMFSourceReader reader) { - var variantPtr = Marshal.AllocHGlobal (MarshalHelpers.SizeOf()); + var variantPtr = Marshal.AllocHGlobal (Marshal.SizeOf()); try { int hResult = reader.GetPresentationAttribute (MediaFoundationInterop.MF_SOURCE_READER_MEDIASOURCE, @@ -146,7 +146,7 @@ namespace GameRes.Formats if (hResult != 0) Marshal.ThrowExceptionForHR (hResult); - var variant = MarshalHelpers.PtrToStructure (variantPtr); + var variant = Marshal.PtrToStructure (variantPtr); return (long)variant.Value; } finally diff --git a/ArcFormats/Circus/ArcCircus.cs b/ArcFormats/Circus/ArcCircus.cs index 5a2d2958..065dde31 100644 --- a/ArcFormats/Circus/ArcCircus.cs +++ b/ArcFormats/Circus/ArcCircus.cs @@ -47,20 +47,46 @@ namespace GameRes.Formats.Circus public override ArcFile TryOpen (ArcView file) { - int count = file.View.ReadInt32 (0); - if (count <= 1 || count > 0xfffff) - return null; - var dir = ReadIndex (file, count, 0x24); - if (null == dir) - dir = ReadIndex (file, count, 0x30); - if (null == dir) - dir = ReadIndex (file, count, 0x3C); - if (null == dir) - return null; - return new ArcFile (file, this, dir); + var arcFile = TryOpenFromHeader(file); + if (null != arcFile) + { + return arcFile; + } + + arcFile = TryOpenFromFooter(file); + + return arcFile; } - private List ReadIndex (ArcView file, int count, int name_length) + public ArcFile TryOpenFromHeader(ArcView file) + { + int count = file.View.ReadInt32(0); + if (count <= 1 || count > 0xfffff) + return null; + var dir = ReadIndexV1(file, count, 0x24); + if (null == dir) + dir = ReadIndexV1(file, count, 0x30); + if (null == dir) + dir = ReadIndexV1(file, count, 0x3C); + if (null == dir) + return null; + return new ArcFile(file, this, dir); + } + + public ArcFile TryOpenFromFooter(ArcView file) + { + int count = file.View.ReadInt32(file.MaxOffset - 4); + if(count <= 1 || count > 0xfffff) + { + return null; + } + + var dir = ReadIndexV2(file, count, file.View.ReadInt32(file.MaxOffset - 0x8)); + + return new ArcFile(file, this, dir); + } + + private List ReadIndexV1 (ArcView file, int count, int name_length) { long index_offset = 4; uint index_size = (uint)((name_length + 4) * count); @@ -98,5 +124,36 @@ namespace GameRes.Formats.Circus } return dir; } + + private List ReadIndexV2(ArcView file, int count, long start_offset) + { + uint max_index_size = (uint)(count * 0x4C); + long index_offset = start_offset; + var dir = new List (count); + + file.View.Reserve(start_offset, max_index_size); + + for (int i = 0; i < count; i++) + { + int name_length = file.View.ReadByte(index_offset); + index_offset++; + + string name = file.View.ReadString(index_offset, (uint)name_length); + index_offset += name_length; + + uint entry_size = file.View.ReadUInt32(index_offset); + index_offset += 8; + + long entry_offset = file.View.ReadInt32(index_offset); + index_offset += 4; + + var entry = FormatCatalog.Instance.Create(name); + entry.Size = entry_size; + entry.Offset = entry_offset; + dir.Add(entry); + } + + return dir; + } } } diff --git a/ArcFormats/Edoire/ArcARC.cs b/ArcFormats/Edoire/ArcARC.cs new file mode 100644 index 00000000..2468e724 --- /dev/null +++ b/ArcFormats/Edoire/ArcARC.cs @@ -0,0 +1,87 @@ +//! \file ArcARC.cs +//! \date 2026 Feb 02 +//! \brief Edoire's resource 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.Text; + +namespace GameRes.Formats.Edoire +{ + [Export(typeof(ArchiveFormat))] + public class ArcOpener : ArchiveFormat + { + public override string Tag { get { return "ARC"; } } + public override string Description { get { return "Edoire's resource archive"; } } + public override uint Signature { get { return 0x43524140; } } // "@ARCH000" + public override bool IsHierarchic { get { return true; } } + public override bool CanWrite { get { return false; } } + + public ArcOpener () + { + Extensions = new string[] { "arc" }; + } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (0, "@ARCH000")) + return null; + var index_offset = file.View.ReadInt64 (file.MaxOffset-8); + if (index_offset <= 0 || index_offset >= file.MaxOffset-12) + return null; + var count = file.View.ReadInt32 (index_offset); + if (!IsSaneCount (count)) + return null; + index_offset += 4; + var dir = new List (count); + for (var i = 0; i < count; i++) + { + var len = file.View.ReadByte (index_offset); + index_offset += 1; + var name = file.View.ReadString (index_offset, len, Encoding.UTF8); + index_offset += len; + var offset = file.View.ReadInt64 (index_offset); + index_offset += 8; + var size = file.View.ReadInt64 (index_offset); + index_offset += 9; + len = file.View.ReadByte (index_offset); + index_offset += 1; + var path = file.View.ReadString (index_offset, len, Encoding.UTF8); + index_offset += len; + if (path.StartsWith ("/")) + path = path.Substring (1); + if (!string.IsNullOrEmpty (path) && !path.EndsWith ("/")) + path += "/"; + var entry = Create (path+name); + entry.Offset = offset; + entry.Size = Convert.ToUInt32 (size); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/ArcFormats/Entis/ArcNOA.cs b/ArcFormats/Entis/ArcNOA.cs index 296f5a09..262e8056 100644 --- a/ArcFormats/Entis/ArcNOA.cs +++ b/ArcFormats/Entis/ArcNOA.cs @@ -29,6 +29,7 @@ using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using GameRes.Formats.Strings; @@ -131,11 +132,27 @@ namespace GameRes.Formats.Entis if (EncType.Raw == nent.Encryption || null == narc || null == narc.Password) return input; + if (EncType.SimpleCrypt32 == nent.Encryption) + { + if (0 != size % 4) + throw new InvalidFormatException (); + + using (input) + return DecodeSimpleCrypt32 (input, narc.Password, BitConverter.ToUInt32 (nent.Extra, 4)); + } + if (EncType.BSHFCrypt == nent.Encryption) { using (input) return DecodeBSHF (input, narc.Password); } + if (EncType.ERISACrypt == nent.Encryption) + { + Stream enc; + using (input) + enc = DecodeBSHF (input, narc.Password); + return new ErisaNemesisStream (enc, (int)entry.Size); + } Trace.WriteLine (string.Format ("{0}: encryption scheme 0x{1:x8} not implemented", nent.Name, nent.Encryption)); return input; @@ -232,6 +249,37 @@ namespace GameRes.Formats.Entis return new MemoryStream (buf); } + Stream DecodeSimpleCrypt32(Stream input, string password, uint imul_key) + { + var xor_key_map = Encoding.UTF8.GetBytes (password); + imul_key ^= Crc32.Compute (xor_key_map, 0, xor_key_map.Length); + + for (int i = 0; i < xor_key_map.Length; i++) + { + var key_byte = (byte)~xor_key_map[i]; + xor_key_map[i] = (byte)(key_byte ^ (key_byte * 7)); + } + + var buffer = new byte[input.Length]; + input.Read(buffer, 0, (int)input.Length); + + var buffer_span = MemoryMarshal.Cast (buffer); + var xor_key_span = MemoryMarshal.Cast (xor_key_map); + + for (int i = 0; i < buffer_span.Length; i += xor_key_span.Length) + { + var window_size = Math.Min (buffer_span.Length - i, xor_key_span.Length); + var window_span = buffer_span.Slice (i, window_size); + + for (int j = 0; j < window_size; j++) + { + window_span[j] = (window_span[j] * imul_key) ^ xor_key_span[j]; + } + } + + return new MemoryStream (buffer); + } + public override ResourceOptions GetDefaultOptions () { return new NoaOptions { @@ -306,7 +354,7 @@ namespace GameRes.Formats.Entis entry.Encryption = m_file.View.ReadUInt32 (dir_offset); m_found_encrypted = m_found_encrypted || (EncType.Raw != entry.Encryption && EncType.ERISACode != entry.Encryption); - bool is_packed = EncType.ERISACode == entry.Encryption; + bool is_packed = EncType.ERISACode == entry.Encryption || EncType.ERISACrypt == entry.Encryption; dir_offset += 4; entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset); diff --git a/ArcFormats/FrontierWorks/ArcPCARC.cs b/ArcFormats/FrontierWorks/ArcPCARC.cs new file mode 100644 index 00000000..56f0d855 --- /dev/null +++ b/ArcFormats/FrontierWorks/ArcPCARC.cs @@ -0,0 +1,117 @@ +//! \file ArcPCARC.cs +//! \date Tue Feb 10 2026 07:56:13 +//! \brief Frontier Works engine resource archive. +// +// 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 System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameRes.Formats.FrontierWorks +{ + [Export(typeof(ArchiveFormat))] + public class PcArcOpener : ArchiveFormat + { + public override string Tag { get { return "PCARC"; } } + public override string Description { get { return "Frontier Works engine resource archive"; } } + public override uint Signature { get { return 0x30303130; } } // "0100" + public override bool IsHierarchic { get { return true; } } + public override bool CanWrite { get { return false; } } + + public PcArcOpener() + { + Extensions = new string[] { "pcarc" }; + } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (0, "0100")) + return null; + var dir_count = file.View.ReadInt32 (4); + if (dir_count <= 0 || dir_count > 32) + return null; + var data_offset = file.View.ReadInt64 (8); + if (data_offset < 0x190) + return null; + var offset = 0x10; + var dir = new List (); + for (var i = 0; i < dir_count; i++) + { + var dir_offset = file.View.ReadInt64 (offset); + offset += 8; + if (dir_offset < 0x190) + break; + var path = file.View.ReadString (dir_offset, 0x40); + var entry_count = file.View.ReadInt32 (dir_offset+0x40); + dir_offset += 0x50; + if (!string.IsNullOrEmpty (path) && !path.EndsWith ("/")) + path += "/"; + for (var j = 0; j < entry_count; j++) + { + var entry = new Entry (); + entry.Name = path+file.View.ReadString (dir_offset, 0x40); + entry.Offset = data_offset+file.View.ReadInt64 (dir_offset+0x40); + entry.Size = file.View.ReadUInt32 (dir_offset+0x48); + dir.Add (entry); + dir_offset += 0x50; + } + } + DetectFileTypes (dir); + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + if (entry.Name.HasExtension (".gz")) + { + using (var input = arc.File.CreateStream (entry.Offset, entry.Size)) + using (var gzs = new GZipStream (input, CompressionMode.Decompress)) + { + var output = new MemoryStream (); + gzs.CopyTo (output); + return new BinMemoryStream (output, entry.Name.Substring (0, entry.Name.Length-3)); + } + } + return base.OpenEntry (arc, entry); + } + + static void DetectFileTypes (List dir) + { + foreach (var entry in dir) + { + var name = entry.Name; + if (name.HasExtension (".gz")) + name = name.Substring (0, name.Length-3); + if (name.HasExtension (".oggl")) + entry.Type = "audio"; + if (string.IsNullOrEmpty (entry.Type)) + entry.Type = FormatCatalog.Instance.GetTypeFromName (name); + } + } + } +} diff --git a/ArcFormats/FrontierWorks/ImageEXT.cs b/ArcFormats/FrontierWorks/ImageEXT.cs new file mode 100644 index 00000000..7232b83e --- /dev/null +++ b/ArcFormats/FrontierWorks/ImageEXT.cs @@ -0,0 +1,82 @@ +//! \file ImageEXT.cs +//! \date Tue Feb 10 2026 08:21:40 +//! \brief Frontier Works engine 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 System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; + +namespace GameRes.Formats.FrontierWorks +{ + [Export(typeof(ImageFormat))] + public class ExtFormat : ImageFormat + { + public override string Tag { get { return "EXT"; } } + public override string Description { get { return "Frontier Works engine image format"; } } + public override uint Signature { get { return 0x30545845; } } // 'EXT0' + + public ExtFormat () + { + Extensions = new string[] { "ext" }; + } + + public override ImageMetaData ReadMetaData (IBinaryStream stream) + { + stream.Position = 0; + var signature = stream.ReadInt32 (); + if (0x30545845 != signature) + return null; + stream.Position = 0xC; + var width = stream.ReadUInt32 (); + var height = stream.ReadUInt32 (); + if (width > 0x1000 || height > 0x1000) + return null; + stream.Position = 0x24; + var bpp = stream.ReadByte (); + if (32 != bpp) + return null; + return new ImageMetaData + { + Width = width, + Height = height, + BPP = bpp, + }; + } + + public override ImageData Read (IBinaryStream stream, ImageMetaData info) + { + stream.Position = 0x100; + var pixels = new byte[info.Width*info.Height*4]; + if (pixels.Length != stream.Read (pixels, 0, pixels.Length)) + throw new EndOfStreamException (); + return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("ExtFormat.Write not implemented"); + } + } +} diff --git a/ArcFormats/ImageKTX.cs b/ArcFormats/ImageKTX.cs new file mode 100644 index 00000000..ae314f49 --- /dev/null +++ b/ArcFormats/ImageKTX.cs @@ -0,0 +1,110 @@ +//! \file ImageKTX.cs +//! \date 2026 March 01 +//! \brief Khronos texture 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 BCnEncoder.Decoder; +using BCnEncoder.Shared; +using BCnEncoder.Shared.ImageFiles; +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Reflection; +using System.Windows.Media; + +namespace GameRes.Formats +{ + internal class KtxMetaData : ImageMetaData + { + public KtxHeader Header; + } + + [Export(typeof(ImageFormat))] + public class KtxFormat : ImageFormat + { + public override string Tag { get { return "KTX"; } } + public override string Description { get { return "Khronos texture format"; } } + public override uint Signature { get { return 0x58544BAB; } } + public override bool CanWrite { get { return false; } } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var signature = file.ReadInt32 (); + if (0x58544BAB != signature) + return null; + file.Position = 0xC; + var header = new KtxHeader + { + Endianness = file.ReadUInt32 (), + GlType = (GlType) file.ReadUInt32 (), + GlTypeSize = file.ReadUInt32 (), + GlFormat = (GlFormat) file.ReadUInt32 (), + GlInternalFormat = (GlInternalFormat) file.ReadUInt32 (), + GlBaseInternalFormat = (GlFormat) file.ReadUInt32 (), + PixelWidth = file.ReadUInt32 (), + PixelHeight = file.ReadUInt32 (), + PixelDepth = file.ReadUInt32 (), + NumberOfArrayElements = file.ReadUInt32 (), + NumberOfFaces = file.ReadUInt32 (), + NumberOfMipmapLevels = file.ReadUInt32 (), + BytesOfKeyValueData = file.ReadUInt32 (), + }; + var ktx = new KtxFile (header); + var decoder = new BcDecoder (); + if (!decoder.IsSupportedFormat (ktx)) + return null; + return new KtxMetaData + { + Width = header.PixelWidth, + Height = header.PixelHeight, + BPP = 32, + Header = header, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var ktx = KtxFile.Load (file.AsStream); + var decoder = new BcDecoder (); + var buffer = decoder.Decode (ktx); + var pixels = new byte[buffer.Length*4]; + var src = 0; + var dst = 0; + while (src < buffer.Length) + { + pixels[dst ] = buffer[src].b; + pixels[dst+1] = buffer[src].g; + pixels[dst+2] = buffer[src].r; + pixels[dst+3] = buffer[src].a; + src += 1; + dst += 4; + } + return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("KtxFormat.Write not implemented"); + } + } +} diff --git a/ArcFormats/KiriKiri/ArcTLG.cs b/ArcFormats/KiriKiri/ArcTLG.cs new file mode 100644 index 00000000..2832d485 --- /dev/null +++ b/ArcFormats/KiriKiri/ArcTLG.cs @@ -0,0 +1,110 @@ +//! \file ArcTLG.cs +//! \date Tue Mar 17 2026 10:35:55 +//! \brief KiriKiri TLG image implementation. +//--------------------------------------------------------------------------- +// TLGqoi multi-layer image decoder +// +// C# port by crsky +// + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.KiriKiri +{ + internal class TlgLayerEntry : Entry + { + public int Index; + } + + [Export(typeof(ArchiveFormat))] + public class TlgOpener : ArchiveFormat + { + public override string Tag { get { return "TLG"; } } + public override string Description { get { return "KiriKiri game engine image format"; } } + public override uint Signature { get { return 0x71474C54; } } // 'TLGq' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public TlgOpener () + { + Extensions = new string[] { "tlg" }; + } + + public override ArcFile TryOpen (ArcView file) + { + if (!file.View.AsciiEqual (0, "TLGqoi") || !file.View.AsciiEqual (7, "raw")) + return null; + var qhdr = Array.Empty (); + var offset = 0x14; + while (true) + { + var entry_signature = file.View.ReadInt32 (offset); + var entry_size = file.View.ReadInt32 (offset+4); + offset += 8; + if (0x52444851 == entry_signature) // 'QHDR' + { + if (0x30 != entry_size) + return null; + qhdr = file.View.ReadBytes (offset, (uint)entry_size); + if (entry_size != qhdr.Length) + return null; + offset += entry_size; + } + else if (0 == entry_signature && 0 == entry_size) + break; + else + return null; + } + if (0 == qhdr.Length) + return null; + var layer_count = qhdr.ToInt32 (4); + if (layer_count < 1) + return null; + var block_count = qhdr.ToInt32 (12); + if (0 == block_count) + return null; + var dir = new List (layer_count); + for (var i = 0; i < layer_count; i++) + { + dir.Add (new TlgLayerEntry + { + Name = string.Format ("{0}#{1:D3}.tlg", Path.GetFileNameWithoutExtension (file.Name), i), + Offset = 0, + Size = (uint)file.MaxOffset, + Type = "image", + Index = i, + }); + } + return new ArcFile (file, this, dir); + } + + static readonly ResourceInstance s_TlgFormat = new ResourceInstance ("TLG"); + + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var layer_entry = entry as TlgLayerEntry; + if (null == layer_entry) + return base.OpenImage (arc, entry); + var input = arc.File.CreateStream (); + try + { + var info = s_TlgFormat.Value.ReadMetaData (input); + if (null == info) + throw new InvalidFormatException (); + if (info is TlgMetaData tlg) + { + tlg.LayerIndex = layer_entry.Index; + } + return new ImageFormatDecoder (input, s_TlgFormat.Value, info); + } + catch + { + input.Dispose (); + throw; + } + } + } +} diff --git a/ArcFormats/KiriKiri/CryptAlgorithms.cs b/ArcFormats/KiriKiri/CryptAlgorithms.cs index a3b06383..ad473cc7 100644 --- a/ArcFormats/KiriKiri/CryptAlgorithms.cs +++ b/ArcFormats/KiriKiri/CryptAlgorithms.cs @@ -104,6 +104,22 @@ namespace GameRes.Formats.KiriKiri input.Read (header, 0, 5); uint signature = header.ToUInt32 (0); GuessEntryTypeBySignature (entry, signature); + if (0x46464952 == signature) // 'RIFF' + { + if (entry.UnpackedSize >= 12) + { + var header_ext = new byte[12]; + Array.Copy (header, header_ext, header.Length); + input.Read (header_ext, header.Length, header_ext.Length-header.Length); + header = header_ext; + if (header.AsciiEqual (8, "WAVE")) + entry.Type = "audio"; + else if (header.AsciiEqual (8, "WEBP")) + entry.Type = "image"; + else if (header.AsciiEqual (8, "AVI ")) + entry.Type = "video"; + } + } if (0x184D2204 == signature) // LZ4 magic { // assume no scripts are compressed using LZ4, return decompressed stream right away diff --git a/ArcFormats/KiriKiri/HxCrypt.cs b/ArcFormats/KiriKiri/HxCrypt.cs index f8b543d3..aa94ec89 100644 --- a/ArcFormats/KiriKiri/HxCrypt.cs +++ b/ArcFormats/KiriKiri/HxCrypt.cs @@ -53,14 +53,14 @@ namespace GameRes.Formats.KiriKiri public string NamesFile; public Dictionary IndexKeyDict; - public HxCrypt(CxScheme scheme) : base(scheme) + public HxCrypt (CxScheme scheme) : base (scheme) { } [NonSerialized] uint[] _lookup32 = null; - void CreateLookup32() + void CreateLookup32 () { if (null != _lookup32) return; @@ -73,7 +73,7 @@ namespace GameRes.Formats.KiriKiri _lookup32 = result; } - string BinaryToString(byte[] data) + string BinaryToString (byte[] data) { if (data.Length == 0) return string.Empty; @@ -88,7 +88,7 @@ namespace GameRes.Formats.KiriKiri return new string(result); } - internal virtual Dictionary ReadIndex(string arc_name, byte[] data) + internal virtual Dictionary ReadIndex (string arc_name, byte[] data) { if (data.Length <= 20) // 16 + 4 return null; @@ -123,8 +123,8 @@ namespace GameRes.Formats.KiriKiri if (null == root_obj) return null; CreateLookup32 (); - var path_map = new Dictionary(); - var name_map = new Dictionary(); + var path_map = new Dictionary (); + var name_map = new Dictionary (); try { FormatCatalog.Instance.ReadFileList (NamesFile, line => { @@ -138,7 +138,7 @@ namespace GameRes.Formats.KiriKiri }); } catch (Exception) { } - var entry_info_map = new Dictionary(); + var entry_info_map = new Dictionary (); for (var i = 0; i < root_obj.Length; i += 2) { var path_hash = root_obj[i] as byte[]; @@ -180,7 +180,7 @@ namespace GameRes.Formats.KiriKiri return entry_info_map; } - internal virtual string GetUnicodeName(uint hash) + internal virtual string GetUnicodeName (uint hash) { var buf = new char[4]; var i = 0; @@ -195,7 +195,7 @@ namespace GameRes.Formats.KiriKiri return str; } - internal virtual HxFilterKey CreateFilterKey(ulong entry_key, ulong header_key_seed) + internal virtual HxFilterKey CreateFilterKey (ulong entry_key, ulong header_key_seed) { var result = new HxFilterKey { @@ -208,16 +208,16 @@ namespace GameRes.Formats.KiriKiri uint key0 = (uint)(entry_key & 0xffffffff); uint key1 = (uint)((entry_key >> 32) & 0xffffffff); - var k0 = ExecuteXCode(key0); + var k0 = ExecuteXCode (key0); result.Key[0] = (ulong)k0.Item1 | ((ulong)k0.Item2 << 32); - var k1 = ExecuteXCode(key1); + var k1 = ExecuteXCode (key1); result.Key[1] = (ulong)k1.Item1 | ((ulong)k1.Item2 << 32); result.SplitPosition = (long)((this.m_offset + ((entry_key >> 16) & this.m_mask)) & 0xffffffff); /* create header key */ - var k3 = ExecuteXCode((uint)header_key_seed); + var k3 = ExecuteXCode ((uint)header_key_seed); var v5 = (ulong)k3.Item1 | ((ulong)k3.Item2 << 32); v5 = ~v5; @@ -226,7 +226,7 @@ namespace GameRes.Formats.KiriKiri result.HeaderKey[i] = (byte)((v5 >> j) & 0xff); } - k3 = ExecuteXCode((uint)v5); + k3 = ExecuteXCode ((uint)v5); v5 = (ulong)k3.Item1 | ((ulong)k3.Item2 << 32); v5 = ~v5; @@ -241,7 +241,7 @@ namespace GameRes.Formats.KiriKiri return result; } - internal virtual void CreateFilter(Xp3Entry entry) + internal virtual void CreateFilter (Xp3Entry entry) { var info = entry.Extra as HxEntry; if (null == info) @@ -256,20 +256,25 @@ namespace GameRes.Formats.KiriKiri info.Filter = new HxFilter (key); } - public override void Init(ArcFile arc) + public override void Init (ArcFile arc) { return; } - public override byte Decrypt(Xp3Entry entry, long offset, byte value) + public override Stream EntryReadFilter (Xp3Entry entry, Stream input) + { + var info = entry.Extra as HxEntry; + if (null == info) + return base.EntryReadFilter (entry, input); + CreateFilter (entry); + return base.EntryReadFilter (entry, input); + } + + public override byte Decrypt (Xp3Entry entry, long offset, byte value) { - if (entry.Extra == null) - return value; var info = entry.Extra as HxEntry; if (null == info) return value; - if (null == info.Filter) - CreateFilter (entry); if (null == info.Filter) return value; @@ -280,15 +285,11 @@ namespace GameRes.Formats.KiriKiri return buf[0]; } - public override void Decrypt(Xp3Entry entry, long offset, byte[] buffer, int pos, int count) + public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count) { - if (entry.Extra == null) - return; var info = entry.Extra as HxEntry; if (null == info) return; - if (null == info.Filter) - CreateFilter (entry); if (null == info.Filter) return; @@ -297,14 +298,14 @@ namespace GameRes.Formats.KiriKiri return; } - public override void Encrypt(Xp3Entry entry, long offset, byte[] values, int pos, int count) + public override void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count) { throw new NotImplementedException(); } - internal override CxProgram NewProgram(uint seed) + internal override CxProgram NewProgram (uint seed) { - return new HxProgram(seed, ControlBlock, RandomType); + return new HxProgram (seed, ControlBlock, RandomType); } } @@ -346,7 +347,7 @@ namespace GameRes.Formats.KiriKiri public long Position; public HxBufferSpan Data; - bool AdjustHeaderKey(HxHeaderKey key, HxHeaderKey new_key) + bool AdjustHeaderKey (HxHeaderKey key, HxHeaderKey new_key) { if (this.Data.Buffer == null) return false; @@ -380,11 +381,11 @@ namespace GameRes.Formats.KiriKiri return true; } - public void DecryptHeader(HxHeaderKey key) + public void DecryptHeader (HxHeaderKey key) { - var key2 = new HxHeaderKey(); + var key2 = new HxHeaderKey (); - if (AdjustHeaderKey(key, key2)) + if (AdjustHeaderKey (key, key2)) { for (uint i = 0; i < key2.Length; i++) { @@ -394,7 +395,7 @@ namespace GameRes.Formats.KiriKiri } } - public int Split(long split_position, HxFilterSpan[] sub_span) + public int Split (long split_position, HxFilterSpan[] sub_span) { if (this.Data.Buffer == null) return 0; @@ -443,7 +444,7 @@ namespace GameRes.Formats.KiriKiri } } - public void FirstDecrypt(uint key) + public void FirstDecrypt (uint key) { if (this.Data.Buffer == null) return; @@ -451,7 +452,7 @@ namespace GameRes.Formats.KiriKiri if (this.Data.Length == 0) return; - var buf = BitConverter.GetBytes(key); + var buf = BitConverter.GetBytes (key); for (int i = 0; i < this.Data.Length; i++) { @@ -467,7 +468,7 @@ namespace GameRes.Formats.KiriKiri private uint FirstDecryptKey; private uint DecryptKey; - public HxFilterSpanDecryptor(ulong key, bool flag) + public HxFilterSpanDecryptor (ulong key, bool flag) { this.DecryptKey = (uint)((key >> 8) & 0xFF); this.DecryptKey |= (uint)((key >> 8) & 0xFF00); @@ -492,9 +493,9 @@ namespace GameRes.Formats.KiriKiri this.FirstDecryptKey *= 0x1010101; } - public void Decrypt(HxFilterSpan span) + public void Decrypt (HxFilterSpan span) { - span.FirstDecrypt(FirstDecryptKey); + span.FirstDecrypt (FirstDecryptKey); byte key1 = (byte)(this.DecryptKey & 0xFF); byte key2 = (byte)((this.DecryptKey >> 8) & 0xFF); @@ -525,17 +526,17 @@ namespace GameRes.Formats.KiriKiri private long SplitPosition; private HxHeaderKey HeaderKey; - public HxFilter(HxFilterKey key) + public HxFilter (HxFilterKey key) { this.Span = new HxFilterSpanDecryptor[2] { - new HxFilterSpanDecryptor(key.Key[0], key.Flag), - new HxFilterSpanDecryptor(key.Key[1], key.Flag), + new HxFilterSpanDecryptor (key.Key[0], key.Flag), + new HxFilterSpanDecryptor (key.Key[1], key.Flag), }; this.SplitPosition = key.SplitPosition; - this.HeaderKey = new HxHeaderKey(); + this.HeaderKey = new HxHeaderKey (); if (key.HasHeaderKey) { @@ -544,7 +545,7 @@ namespace GameRes.Formats.KiriKiri } } - public void Decrypt(long position, byte[] buffer, int buffer_ptr, int length) + public void Decrypt (long position, byte[] buffer, int buffer_ptr, int length) { var span = new HxFilterSpan { @@ -560,21 +561,21 @@ namespace GameRes.Formats.KiriKiri if (span.Position < this.HeaderKey.Position + this.HeaderKey.Length) { - span.DecryptHeader(this.HeaderKey); + span.DecryptHeader (this.HeaderKey); } var sub_span = new HxFilterSpan[2]; - var flags = span.Split(this.SplitPosition, sub_span); + var flags = span.Split (this.SplitPosition, sub_span); if ((flags & 1) != 0) { - this.Span[0].Decrypt(sub_span[0]); + this.Span[0].Decrypt (sub_span[0]); } if ((flags & 2) != 0) { - this.Span[1].Decrypt(sub_span[1]); + this.Span[1].Decrypt (sub_span[1]); } } } @@ -583,12 +584,12 @@ namespace GameRes.Formats.KiriKiri { private ulong m_seed; - public HxSplittableRandom(ulong seed) + public HxSplittableRandom (ulong seed) { m_seed = seed; } - public ulong Next() + public ulong Next () { ulong z; @@ -618,7 +619,7 @@ namespace GameRes.Formats.KiriKiri readonly int m_random_method; new readonly M64[] m_seed; - public HxProgram(uint seed, uint[] control_block, int random_method) : base(seed, control_block) + public HxProgram (uint seed, uint[] control_block, int random_method) : base(seed, control_block) { m_random_method = random_method; m_seed = new M64[2]; @@ -628,19 +629,19 @@ namespace GameRes.Formats.KiriKiri var r = new HxSplittableRandom(s); - m_seed[0].u64 = r.Next(); - m_seed[1].u64 = r.Next(); + m_seed[0].u64 = r.Next (); + m_seed[1].u64 = r.Next (); } - ulong GetOldRandom() + ulong GetOldRandom () { /* These codes only work correctly in little endian mode! */ - var a = new M64(); - var b = new M64(); - var c = new M64(); - var d = new M64(); - var e = new M64(); + var a = new M64 (); + var b = new M64 (); + var c = new M64 (); + var d = new M64 (); + var e = new M64 (); ulong t; @@ -676,14 +677,14 @@ namespace GameRes.Formats.KiriKiri return t; } - ulong GetNewRandom() + ulong GetNewRandom () { /* These codes only work correctly in little endian mode! */ - var a = new M64(); - var b = new M64(); - var c = new M64(); - var d = new M64(); + var a = new M64 (); + var b = new M64 (); + var c = new M64 (); + var d = new M64 (); ulong t; @@ -719,12 +720,12 @@ namespace GameRes.Formats.KiriKiri return t; } - public override uint GetRandom() + public override uint GetRandom () { if (0 == m_random_method) - return (uint)GetOldRandom(); + return (uint) GetOldRandom (); else - return (uint)GetNewRandom(); + return (uint) GetNewRandom (); } } @@ -741,9 +742,9 @@ namespace GameRes.Formats.KiriKiri readonly State m_state; - public HxChachaDecryptor(byte[] key, byte[] nonce, uint[] seed) + public HxChachaDecryptor (byte[] key, byte[] nonce, uint[] seed) { - m_state = new State(); + m_state = new State (); var constant = Encoding.ASCII.GetBytes ("expand 32-byte k"); @@ -755,91 +756,91 @@ namespace GameRes.Formats.KiriKiri Array.Copy (nonce, 0, m_state.Data, 56, 8); } - void TransformState(State src, State dst) + void TransformState (State src, State dst) { uint z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, za, zb, zc, zd, ze, zf; - z0 = LittleEndian.ToUInt32(src.Data, 0); - z1 = LittleEndian.ToUInt32(src.Data, 4); - z2 = LittleEndian.ToUInt32(src.Data, 8); - z3 = LittleEndian.ToUInt32(src.Data, 12); - z4 = LittleEndian.ToUInt32(src.Data, 16); - z5 = LittleEndian.ToUInt32(src.Data, 20); - z6 = LittleEndian.ToUInt32(src.Data, 24); - z7 = LittleEndian.ToUInt32(src.Data, 28); - z8 = LittleEndian.ToUInt32(src.Data, 32); - z9 = LittleEndian.ToUInt32(src.Data, 36); - za = LittleEndian.ToUInt32(src.Data, 40); - zb = LittleEndian.ToUInt32(src.Data, 44); - zc = LittleEndian.ToUInt32(src.Data, 48); - zd = LittleEndian.ToUInt32(src.Data, 52); - ze = LittleEndian.ToUInt32(src.Data, 56); - zf = LittleEndian.ToUInt32(src.Data, 60); + z0 = LittleEndian.ToUInt32 (src.Data, 0); + z1 = LittleEndian.ToUInt32 (src.Data, 4); + z2 = LittleEndian.ToUInt32 (src.Data, 8); + z3 = LittleEndian.ToUInt32 (src.Data, 12); + z4 = LittleEndian.ToUInt32 (src.Data, 16); + z5 = LittleEndian.ToUInt32 (src.Data, 20); + z6 = LittleEndian.ToUInt32 (src.Data, 24); + z7 = LittleEndian.ToUInt32 (src.Data, 28); + z8 = LittleEndian.ToUInt32 (src.Data, 32); + z9 = LittleEndian.ToUInt32 (src.Data, 36); + za = LittleEndian.ToUInt32 (src.Data, 40); + zb = LittleEndian.ToUInt32 (src.Data, 44); + zc = LittleEndian.ToUInt32 (src.Data, 48); + zd = LittleEndian.ToUInt32 (src.Data, 52); + ze = LittleEndian.ToUInt32 (src.Data, 56); + zf = LittleEndian.ToUInt32 (src.Data, 60); for (int i = 0; i < 10; i++) { // QUARTER(z0, z4, z8, zc); - z0 += z4; zc = Binary.RotL(zc ^ z0, 16); - z8 += zc; z4 = Binary.RotL(z4 ^ z8, 12); - z0 += z4; zc = Binary.RotL(zc ^ z0, 8); - z8 += zc; z4 = Binary.RotL(z4 ^ z8, 7); + z0 += z4; zc = Binary.RotL (zc ^ z0, 16); + z8 += zc; z4 = Binary.RotL (z4 ^ z8, 12); + z0 += z4; zc = Binary.RotL (zc ^ z0, 8); + z8 += zc; z4 = Binary.RotL (z4 ^ z8, 7); // QUARTER(z1, z5, z9, zd); - z1 += z5; zd = Binary.RotL(zd ^ z1, 16); - z9 += zd; z5 = Binary.RotL(z5 ^ z9, 12); - z1 += z5; zd = Binary.RotL(zd ^ z1, 8); - z9 += zd; z5 = Binary.RotL(z5 ^ z9, 7); + z1 += z5; zd = Binary.RotL (zd ^ z1, 16); + z9 += zd; z5 = Binary.RotL (z5 ^ z9, 12); + z1 += z5; zd = Binary.RotL (zd ^ z1, 8); + z9 += zd; z5 = Binary.RotL (z5 ^ z9, 7); // QUARTER(z2, z6, za, ze); - z2 += z6; ze = Binary.RotL(ze ^ z2, 16); - za += ze; z6 = Binary.RotL(z6 ^ za, 12); - z2 += z6; ze = Binary.RotL(ze ^ z2, 8); - za += ze; z6 = Binary.RotL(z6 ^ za, 7); + z2 += z6; ze = Binary.RotL (ze ^ z2, 16); + za += ze; z6 = Binary.RotL (z6 ^ za, 12); + z2 += z6; ze = Binary.RotL (ze ^ z2, 8); + za += ze; z6 = Binary.RotL (z6 ^ za, 7); // QUARTER(z3, z7, zb, zf); - z3 += z7; zf = Binary.RotL(zf ^ z3, 16); - zb += zf; z7 = Binary.RotL(z7 ^ zb, 12); - z3 += z7; zf = Binary.RotL(zf ^ z3, 8); - zb += zf; z7 = Binary.RotL(z7 ^ zb, 7); + z3 += z7; zf = Binary.RotL (zf ^ z3, 16); + zb += zf; z7 = Binary.RotL (z7 ^ zb, 12); + z3 += z7; zf = Binary.RotL (zf ^ z3, 8); + zb += zf; z7 = Binary.RotL (z7 ^ zb, 7); // QUARTER(z0, z5, za, zf); - z0 += z5; zf = Binary.RotL(zf ^ z0, 16); - za += zf; z5 = Binary.RotL(z5 ^ za, 12); - z0 += z5; zf = Binary.RotL(zf ^ z0, 8); - za += zf; z5 = Binary.RotL(z5 ^ za, 7); + z0 += z5; zf = Binary.RotL (zf ^ z0, 16); + za += zf; z5 = Binary.RotL (z5 ^ za, 12); + z0 += z5; zf = Binary.RotL (zf ^ z0, 8); + za += zf; z5 = Binary.RotL (z5 ^ za, 7); // QUARTER(z1, z6, zb, zc); - z1 += z6; zc = Binary.RotL(zc ^ z1, 16); - zb += zc; z6 = Binary.RotL(z6 ^ zb, 12); - z1 += z6; zc = Binary.RotL(zc ^ z1, 8); - zb += zc; z6 = Binary.RotL(z6 ^ zb, 7); + z1 += z6; zc = Binary.RotL (zc ^ z1, 16); + zb += zc; z6 = Binary.RotL (z6 ^ zb, 12); + z1 += z6; zc = Binary.RotL (zc ^ z1, 8); + zb += zc; z6 = Binary.RotL (z6 ^ zb, 7); // QUARTER(z2, z7, z8, zd); - z2 += z7; zd = Binary.RotL(zd ^ z2, 16); - z8 += zd; z7 = Binary.RotL(z7 ^ z8, 12); - z2 += z7; zd = Binary.RotL(zd ^ z2, 8); - z8 += zd; z7 = Binary.RotL(z7 ^ z8, 7); + z2 += z7; zd = Binary.RotL (zd ^ z2, 16); + z8 += zd; z7 = Binary.RotL (z7 ^ z8, 12); + z2 += z7; zd = Binary.RotL (zd ^ z2, 8); + z8 += zd; z7 = Binary.RotL (z7 ^ z8, 7); // QUARTER(z3, z4, z9, ze); - z3 += z4; ze = Binary.RotL(ze ^ z3, 16); - z9 += ze; z4 = Binary.RotL(z4 ^ z9, 12); - z3 += z4; ze = Binary.RotL(ze ^ z3, 8); - z9 += ze; z4 = Binary.RotL(z4 ^ z9, 7); + z3 += z4; ze = Binary.RotL (ze ^ z3, 16); + z9 += ze; z4 = Binary.RotL (z4 ^ z9, 12); + z3 += z4; ze = Binary.RotL (ze ^ z3, 8); + z9 += ze; z4 = Binary.RotL (z4 ^ z9, 7); } - LittleEndian.Pack(z0, dst.Data, 0); - LittleEndian.Pack(z1, dst.Data, 4); - LittleEndian.Pack(z2, dst.Data, 8); - LittleEndian.Pack(z3, dst.Data, 12); - LittleEndian.Pack(z4, dst.Data, 16); - LittleEndian.Pack(z5, dst.Data, 20); - LittleEndian.Pack(z6, dst.Data, 24); - LittleEndian.Pack(z7, dst.Data, 28); - LittleEndian.Pack(z8, dst.Data, 32); - LittleEndian.Pack(z9, dst.Data, 36); - LittleEndian.Pack(za, dst.Data, 40); - LittleEndian.Pack(zb, dst.Data, 44); - LittleEndian.Pack(zc, dst.Data, 48); - LittleEndian.Pack(zd, dst.Data, 52); - LittleEndian.Pack(ze, dst.Data, 56); - LittleEndian.Pack(zf, dst.Data, 60); + LittleEndian.Pack (z0, dst.Data, 0); + LittleEndian.Pack (z1, dst.Data, 4); + LittleEndian.Pack (z2, dst.Data, 8); + LittleEndian.Pack (z3, dst.Data, 12); + LittleEndian.Pack (z4, dst.Data, 16); + LittleEndian.Pack (z5, dst.Data, 20); + LittleEndian.Pack (z6, dst.Data, 24); + LittleEndian.Pack (z7, dst.Data, 28); + LittleEndian.Pack (z8, dst.Data, 32); + LittleEndian.Pack (z9, dst.Data, 36); + LittleEndian.Pack (za, dst.Data, 40); + LittleEndian.Pack (zb, dst.Data, 44); + LittleEndian.Pack (zc, dst.Data, 48); + LittleEndian.Pack (zd, dst.Data, 52); + LittleEndian.Pack (ze, dst.Data, 56); + LittleEndian.Pack (zf, dst.Data, 60); } - public void Decrypt(byte[] input, int input_pos, byte[] output, int output_pos, int length) + public void Decrypt (byte[] input, int input_pos, byte[] output, int output_pos, int length) { var state = new State (); var num_block = length / 64; @@ -893,19 +894,19 @@ namespace GameRes.Formats.KiriKiri internal class HxIndexDeserializer { - public static object Deserialize(Stream stream) + public static object Deserialize (Stream stream) { - using (var reader = new BinaryReader(stream, Encoding.ASCII, true)) + using (var reader = new BinaryReader (stream, Encoding.ASCII, true)) { - var obj = ReadObject(reader); - Debug.Assert(stream.Position == stream.Length); + var obj = ReadObject (reader); + Debug.Assert (stream.Position == stream.Length); return obj; } } - static object ReadObject(BinaryReader reader) + static object ReadObject (BinaryReader reader) { - var type = reader.ReadByte(); + var type = reader.ReadByte (); switch (type) { @@ -919,88 +920,88 @@ namespace GameRes.Formats.KiriKiri } case 0x02: { - return ReadString(reader); + return ReadString (reader); } case 0x03: { - return ReadByteArray(reader); + return ReadByteArray (reader); } case 0x04: { - return ReadInt64(reader); + return ReadInt64 (reader); } case 0x05: { - return ReadInt64(reader); + return ReadInt64 (reader); } case 0x81: { - return ReadArray(reader); + return ReadArray (reader); } case 0xC1: { - return ReadDictionary(reader); + return ReadDictionary (reader); } default: { - throw new Exception("unknown object type"); + throw new Exception ("unknown object type"); } } } - static object ReadByteArray(BinaryReader reader) + static object ReadByteArray (BinaryReader reader) { - var count = ReadInt32(reader); - var array = reader.ReadBytes(count); + var count = ReadInt32 (reader); + var array = reader.ReadBytes (count); return array; } - static object ReadArray(BinaryReader reader) + static object ReadArray (BinaryReader reader) { - var count = ReadInt32(reader); + var count = ReadInt32 (reader); - var array = new List(count); + var array = new List (count); for (int i = 0; i < count; i++) { - var obj = ReadObject(reader); - array.Add(obj); + var obj = ReadObject (reader); + array.Add (obj); } - return array.ToArray(); + return array.ToArray (); } - static object ReadDictionary(BinaryReader reader) + static object ReadDictionary (BinaryReader reader) { - var count = ReadInt32(reader); + var count = ReadInt32 (reader); - var dictionary = new Dictionary(count); + var dictionary = new Dictionary (count); for (int i = 0; i < count; i++) { - var name = ReadString(reader); - var obj = ReadObject(reader); - dictionary.Add(name, obj); + var name = ReadString (reader); + var obj = ReadObject (reader); + dictionary.Add (name, obj); } return dictionary; } - static int ReadInt32(BinaryReader reader) + static int ReadInt32 (BinaryReader reader) { - return Binary.BigEndian(reader.ReadInt32()); + return Binary.BigEndian (reader.ReadInt32 ()); } - static long ReadInt64(BinaryReader reader) + static long ReadInt64 (BinaryReader reader) { - return Binary.BigEndian(reader.ReadInt64()); + return Binary.BigEndian (reader.ReadInt64 ()); } - static string ReadString(BinaryReader reader) + static string ReadString (BinaryReader reader) { - var length = ReadInt32(reader); - var buffer = reader.ReadBytes(sizeof(short) * length); - return Encoding.Unicode.GetString(buffer); + var length = ReadInt32 (reader); + var buffer = reader.ReadBytes (sizeof(short) * length); + return Encoding.Unicode.GetString (buffer); } } } diff --git a/ArcFormats/KiriKiri/ImageTLG.cs b/ArcFormats/KiriKiri/ImageTLG.cs index f63f0d52..47d208c0 100644 --- a/ArcFormats/KiriKiri/ImageTLG.cs +++ b/ArcFormats/KiriKiri/ImageTLG.cs @@ -8,21 +8,26 @@ // C# port by morkt // -using System; -using System.IO; -using System.ComponentModel.Composition; -using System.Windows.Media; +using GameRes.Formats.QoiCodec; using GameRes.Utility; +using K4os.Compression.LZ4; +using System; using System.Collections.Generic; +using System.ComponentModel.Composition; using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; namespace GameRes.Formats.KiriKiri { internal class TlgMetaData : ImageMetaData { public int Version; - public int DataOffset; + public long DataOffset; + public int LayerIndex; } [Export(typeof(ImageFormat))] @@ -35,7 +40,7 @@ namespace GameRes.Formats.KiriKiri public TlgFormat () { Extensions = new string[] { "tlg", "tlg5", "tlg6" }; - Signatures = new uint[] { 0x30474C54, 0x35474C54, 0x36474C54, 0x35474CAB, 0x584D4B4A }; + Signatures = new uint[] { 0x30474C54, 0x35474C54, 0x36474C54, 0x35474CAB, 0x584D4B4A, 0x6D474C54, 0x71474C54, 0x72474C54 }; } public override ImageMetaData ReadMetaData (IBinaryStream stream) @@ -45,7 +50,7 @@ namespace GameRes.Formats.KiriKiri if (!header.AsciiEqual ("TLG0.0\x00sds\x1a")) offset = 0; int version; - if (!header.AsciiEqual (offset+6, "\x00raw\x1a")) + if (!header.AsciiEqual (offset+6, "\x00raw\x1a") && !header.AsciiEqual (offset+6, "\x00idx\x1a")) return null; if (0xAB == header[offset]) header[offset] = (byte)'T'; @@ -53,6 +58,12 @@ namespace GameRes.Formats.KiriKiri version = 6; else if (header.AsciiEqual (offset, "TLG5.0")) version = 5; + else if (header.AsciiEqual (offset, "TLGmux")) + version = 0; + else if (header.AsciiEqual (offset, "TLGqoi")) + version = 1; + else if (header.AsciiEqual (offset, "TLGref")) + version = 2; else if (header.AsciiEqual (offset, "XXXYYY")) { version = 5; @@ -102,6 +113,9 @@ namespace GameRes.Formats.KiriKiri { var meta = (TlgMetaData)info; + if (2 == meta.Version) + return ReadREF (file, meta); + var image = ReadTlg (file, meta); int tail_size = (int)Math.Min (file.Length - file.Position, 512); @@ -137,6 +151,10 @@ namespace GameRes.Formats.KiriKiri src.Position = info.DataOffset; if (6 == info.Version) return ReadV6 (src, info); + else if (0 == info.Version) + return ReadMUX (src, info); + else if (1 == info.Version) + return ReadQOI (src, info); else return ReadV5 (src, info); } @@ -1085,6 +1103,400 @@ namespace GameRes.Formats.KiriKiri } } } + + byte[] DecodeQOI (IBinaryStream src, uint width, uint height) + { + var count = 4*width*height; + var output = new byte [count]; + var qoi = new QoiDecodeStream (src); + uint pixel = 0; + var run = 0; + var dst = 0; + while (dst < count) + { + if (run > 1) + --run; + else + { + run = qoi.Read (out pixel); + } + output[dst ] = (byte)pixel; + output[dst+1] = (byte)(pixel >> 8); + output[dst+2] = (byte)(pixel >> 16); + output[dst+3] = (byte)(pixel >> 24); + dst += 4; + } + return output; + } + + class Lz4DecodeStream + { + readonly IBinaryStream m_input; + readonly byte[][] m_buffer; + readonly int[] m_size; + int m_index; + int m_pos; + + const int BUFFER_SIZE = 0x8000; + + public Lz4DecodeStream (IBinaryStream input) + { + m_input = input; + m_buffer = new byte[2][]; + m_size = new int[2]; + for (var i = 0; i < m_buffer.Length; i++) + m_buffer[i] = new byte[BUFFER_SIZE]; + } + + bool FillBuffer () + { + if (m_input.Position == m_input.Length) + return false; + var v1 = m_input.ReadInt32 (); + var v2 = (int)((uint)v1 >> 16); + var input = m_input.ReadBytes (v2); + if (v2 != input.Length) + throw new EndOfStreamException (); + var size = v1 & 0x7FFF; + if (0 == size) + size = 0x8000; + var dst = m_index ^ 1; + var dic = m_index; + int num; + if (0 != (v1 & 0x8000)) + { + if (0 == m_size[dic]) + throw new InvalidFormatException (); + num = LZ4Codec.Decode (input, m_buffer[dst], m_buffer[dic].AsSpan (0, m_size[dic])); + } + else + num = LZ4Codec.Decode (input, m_buffer[dst]); + if (-1 == num || size != num) + throw new InvalidFormatException (); + m_size[dst] = num; + m_index ^= 1; + m_pos = 0; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadByte () + { + if (m_pos == m_size[m_index]) + { + if (!FillBuffer ()) + return -1; + } + var idx = m_index; + if (m_pos == m_size[idx]) + return -1; + return m_buffer[idx][m_pos++]; + } + } + + class RunDecodeStream + { + readonly Lz4DecodeStream m_input; + + public RunDecodeStream (IBinaryStream input) + { + m_input = new Lz4DecodeStream (input); + } + + public int Read () + { + var value = 0; + var shift = 0; + while (shift < 32) + { + var b = m_input.ReadByte (); + if (-1 == b) + throw new EndOfStreamException (); + value |= (int)(b & 0x7F) << shift; + if (0 == (b & 0x80)) + break; + shift += 7; + } + return value; + } + } + + class QoiBlockDecoder + { + readonly QoiDecodeStream m_qoi; + readonly RunDecodeStream m_run; + readonly int m_pixel_count; + readonly int m_layer_index; + readonly int m_layer_count; + readonly byte[] m_output; + readonly int m_dst; + + public QoiBlockDecoder (byte[] qoi, byte[] run, int pixel_count, int layer_index, int layer_count, byte[] output, int dst) + { + m_qoi = new QoiDecodeStream (new BinMemoryStream (qoi)); + m_run = new RunDecodeStream (new BinMemoryStream (run)); + m_pixel_count = pixel_count; + m_layer_index = layer_index; + m_layer_count = layer_count; + m_output = output; + m_dst = dst; + } + + public void Decode () + { + m_qoi.Read (out var p0); + m_qoi.Read (out var p1); + if (0 != p0 || 0xFF000000 != p1) + throw new InvalidFormatException (); + var r0 = m_run.Read (); + if (0 != r0) + throw new InvalidFormatException (); + var dst = m_dst; + var count = m_pixel_count; + var skip = m_layer_index; + while (count --> 0) + { + var r1 = m_qoi.Read (out var pixel); + var r2 = m_run.Read (); + var run = r1 + r2; + while (run --> 0) + { + if (skip > 0) + --skip; + else + { + skip = m_layer_count-1; + m_output[dst ] = (byte)pixel; + m_output[dst+1] = (byte)(pixel >> 8); + m_output[dst+2] = (byte)(pixel >> 16); + m_output[dst+3] = (byte)(pixel >> 24); + dst += 4; + } + } + } + } + } + + long[] DecodeArray (ReadOnlySpan input) + { + var output = new List (input.Length); + var i = 0; + while (i < input.Length) + { + long value = 0; + var shift = 0; + while (shift < 64) + { + var b = input[i++]; + value |= (long)(b & 0x7F) << shift; + if (0 == (b & 0x80)) + break; + shift += 7; + if (i >= input.Length) + throw new EndOfStreamException (); + } + output.Add (value); + } + return output.ToArray (); + } + + long[] ReadArray (IBinaryStream src, long offset, int signature) + { + src.Position = offset; + if (signature != src.ReadInt32 ()) + throw new InvalidFormatException (); + var length = src.ReadInt32 (); + var input = src.ReadBytes (length); + if (input.Length != length) + throw new EndOfStreamException (); + return DecodeArray (input); + } + + byte[] DecodeMultiLayerQOI (IBinaryStream src, uint width, uint height, byte[] qhdr, int layer) + { + var data_offset = src.Position; + + var layer_count = qhdr.ToInt32 (4); + var block_height = qhdr.ToInt32 (8); + var block_count = qhdr.ToInt32 (12); + var dtbl_offset = qhdr.ToInt64 (24); + var rtbl_offset = qhdr.ToInt64 (32); + + if (layer_count < 1) + throw new InvalidFormatException (); + + if (0 == block_count) + throw new NotImplementedException (); + + if (layer >= layer_count) + throw new ArgumentOutOfRangeException (); + + long[] dtbl = ReadArray (src, data_offset+dtbl_offset, 0x4C425444); + if (0 == dtbl.Length || dtbl.Length != 1+2*block_count || dtbl[0] != dtbl.Length-1) + throw new InvalidFormatException (); + + long[] rtbl = ReadArray (src, data_offset+rtbl_offset, 0x4C425452); + if (0 == rtbl.Length || rtbl.Length != 1+block_count || rtbl[0] != rtbl.Length-1) + throw new InvalidFormatException (); + + var tasks = new List (block_count); + var output = new byte[4*width*height]; + + var qoi_offset = data_offset; + var run_offset = src.Position; + + for (var i = 0; i < block_count; i++) + { + var num_pixels = (int) dtbl[1+2*i+1]; + var qoi_size = (int) dtbl[1+2*i ]; + var run_size = (int) rtbl[1+i]; + + src.Position = qoi_offset; + var qoi = src.ReadBytes (qoi_size); + if (qoi_size != qoi.Length) + throw new EndOfStreamException (); + + src.Position = run_offset; + var run = src.ReadBytes (run_size); + if (run_size != run.Length) + throw new EndOfStreamException (); + + qoi_offset += qoi_size; + run_offset += run_size; + + var dst = block_height*i * 4*(int)width; + + var decoder = new QoiBlockDecoder (qoi, run, num_pixels, layer, layer_count, output, dst); + + var task = Task.Run (() => decoder.Decode ()); + tasks.Add (task); + } + Task.WhenAll (tasks).Wait (); + return output; + } + + byte[] ReadQOI (IBinaryStream src, TlgMetaData info) + { + var qhdr = Array.Empty (); + while (true) + { + var entry_signature = src.ReadInt32 (); + var entry_size = src.ReadInt32 (); + if (0x52444851 == entry_signature) // 'QHDR' + { + if (0x30 != entry_size) + throw new InvalidFormatException (); + qhdr = src.ReadBytes (entry_size); + if (qhdr.Length != entry_size) + throw new EndOfStreamException (); + } + else if (0 == entry_signature && 0 == entry_size) + break; + else + throw new InvalidFormatException (); + } + if (0 != qhdr.Length) + return DecodeMultiLayerQOI (src, info.Width, info.Height, qhdr, info.LayerIndex); + return DecodeQOI (src, info.Width, info.Height); + } + + byte[] ReadMUX (IBinaryStream src, TlgMetaData info) + { + src.Position = info.DataOffset; + var slices = new List (); + while (true) + { + var entry_signature = src.ReadInt32 (); + var entry_size = src.ReadInt32 (); + if (0x58554D43 == entry_signature) // 'CMUX' + { + var entry = src.ReadBytes (entry_size); + var count = entry.ToInt32 (0); + if (0 == count) + throw new InvalidFormatException (); + var offset = 4; + for (var i = 0; i < count; i++) + { + slices.Add (new TlgMetaData + { + OffsetX = entry.ToInt32 (offset), + OffsetY = entry.ToInt32 (offset+4), + Width = entry.ToUInt32 (offset+8), + Height = entry.ToUInt32 (offset+12), + DataOffset = entry.ToInt64 (offset+16) + }); + offset += 24; + } + } + else if (0 == entry_signature && 0 == entry_size) + break; + else + throw new InvalidFormatException (); + } + var data_offset = src.Position; + var image = new byte [4*info.Width*info.Height]; + foreach (var slice_info in slices) + { + src.Position = data_offset + slice_info.DataOffset; + byte[] slice; + var header = src.ReadBytes (11); + if (header.AsciiEqual (0, "TLGqoi") && header.AsciiEqual (7, "raw")) + { + var channels = src.ReadByte (); + var width = src.ReadUInt32 (); + var height = src.ReadUInt32 (); + if (3 != channels && 4 != channels) + throw new InvalidFormatException (); + if (width != slice_info.Width || height != slice_info.Height) + throw new InvalidFormatException (); + slice = ReadQOI (src, slice_info); + } + else + throw new NotImplementedException (); + BlendImage (image, info, slice, slice_info, 0); + } + return image; + } + + ImageData ReadREF (IBinaryStream src, TlgMetaData info) + { + src.Position = info.DataOffset; + var qref = Array.Empty (); + while (true) + { + var entry_signature = src.ReadInt32 (); + var entry_size = src.ReadInt32 (); + if (0x46455251 == entry_signature) // 'QREF' + { + if (entry_size < 16) + throw new InvalidFormatException (); + qref = src.ReadBytes (entry_size); + if (qref.Length != entry_size) + throw new EndOfStreamException (); + } + else if (0 == entry_signature && 0 == entry_size) + break; + else + throw new InvalidFormatException (); + } + if (0 == qref.Length) + throw new InvalidFormatException (); + var layer_index = qref.ToInt32 (4); + var name_length = qref.ToInt32 (12); + if (name_length < 2) + throw new InvalidFormatException (); + var name = Encoding.Unicode.GetString (qref, 16, name_length); + if (string.IsNullOrEmpty (name)) + throw new InvalidFormatException (); + using (var ref_tlg = VFS.OpenBinaryStream (name)) + { + var ref_meta = ReadMetaData (ref_tlg) as TlgMetaData; + if (1 != ref_meta.Version) + throw new InvalidFormatException (); + ref_meta.LayerIndex = layer_index; + return Read (ref_tlg, ref_meta); + } + } } internal class TagsParser diff --git a/ArcFormats/Leaf/ArcPAK.cs b/ArcFormats/Leaf/ArcPAK.cs index 9435f88a..2ca92fa4 100644 --- a/ArcFormats/Leaf/ArcPAK.cs +++ b/ArcFormats/Leaf/ArcPAK.cs @@ -23,10 +23,14 @@ // IN THE SOFTWARE. // +using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Linq; +using System.Text; using GameRes.Compression; +using GameRes.Utility; using GameRes.Utility.Serialization; namespace GameRes.Formats.Leaf @@ -38,7 +42,7 @@ namespace GameRes.Formats.Leaf public override string Description { get { return "Leaf resource archive"; } } public override uint Signature { get { return 0x5041434B; } } // 'KCAP' public override bool IsHierarchic { get { return false; } } - public override bool CanWrite { get { return false; } } + public override bool CanWrite { get { return true; } } public KcapOpener () { @@ -160,6 +164,87 @@ namespace GameRes.Formats.Leaf input.Position = 0; return ImageFormatDecoder.Create (input); } + + // --- REPACK IMPLEMENTATION --- + + public override void Create (Stream output, IEnumerable entries, ResourceOptions options, EntryCallback callback) + { + int count = entries.Count(); + using (var writer = new BinaryWriter (output, Encoding.ASCII, true)) + { + // 1. Write Header (KCAP v2) + writer.Write (0x5041434B); // "KCAP" + writer.Write (0xFFFFFFFF); + writer.Write (0x0000FFFF); + writer.Write (count); + + long tableOffset = 16; + long dataOffset = tableOffset + (count * 44); + + writer.BaseStream.Position = dataOffset; + + var entryInfos = new List(); + int i = 0; + + foreach (var entry in entries) + { + if (callback != null) + callback (i + 1, entry, null); + + long currentOffset = writer.BaseStream.Position; + + using (var input = File.OpenRead(entry.Name)) + { + byte[] rawData = new byte[input.Length]; + input.Read (rawData, 0, (int)input.Length); + + // Compress + byte[] compressedData = LeafLzss.Compress (rawData); + + // Write Data (Prefix + LZSS) + writer.Write ((uint)compressedData.Length); + writer.Write ((uint)rawData.Length); + writer.Write (compressedData); + + uint totalSize = (uint)(compressedData.Length + 8); + + entryInfos.Add (new EntryInfo + { + Name = Path.GetFileName (entry.Name), + UnpackedSize = (uint)rawData.Length, + Offset = (uint)currentOffset, + PackedSize = totalSize + }); + } + i++; + } + + // Write File Table + writer.BaseStream.Position = tableOffset; + foreach (var info in entryInfos) + { + writer.Write (1); // Type + + byte[] nameBytes = Encodings.cp932.GetBytes (info.Name); + if (nameBytes.Length > 23) Array.Resize (ref nameBytes, 23); + writer.Write (nameBytes); + for (int k = nameBytes.Length; k < 24; k++) writer.Write ((byte)0); + + writer.Write (0xFFFFFFFF); // CRC + writer.Write (info.UnpackedSize); + writer.Write (info.Offset); + writer.Write (info.PackedSize); + } + } + } + + struct EntryInfo + { + public string Name; + public uint UnpackedSize; + public uint Offset; + public uint PackedSize; + } } [Export(typeof(ScriptFormat))] @@ -228,4 +313,168 @@ namespace GameRes.Formats.Leaf public bool IsPacked { get { return _is_packed != 0; } } } #pragma warning restore 649,169 -} + + // --- LEAF LZSS COMPRESSION --- + internal static class LeafLzss + { + const int N = 4096; + const int F = 18; + const int THR = 2; + const int NIL = N; + + public static byte[] Compress (byte[] input) + { + if (input.Length == 0) return new byte[0]; + + using (var outStream = new MemoryStream (input.Length)) + { + // Arrays size = N + 257 to handle Root Nodes (N+1..N+256) safely + int[] lson = new int[N + 257]; + int[] rson = new int[N + 257]; + int[] dad = new int[N + 257]; + byte[] text_buf = new byte[N + F - 1]; + + for (int j = N + 1; j <= N + 256; j++) rson[j] = NIL; + for (int j = 0; j < N; j++) dad[j] = NIL; + + int match_position = 0, match_length = 0; + + void InsertNode (int r_node) + { + int i, p, cmp; + int key_pos = r_node; + + // Root for this character + p = N + 1 + text_buf[key_pos]; + + rson[r_node] = lson[r_node] = NIL; + match_length = 0; + + cmp = 1; // Initial state + + for (; ; ) + { + if (cmp >= 0) + { + if (rson[p] != NIL) p = rson[p]; + else { rson[p] = r_node; dad[r_node] = p; return; } + } + else + { + if (lson[p] != NIL) p = lson[p]; + else { lson[p] = r_node; dad[r_node] = p; return; } + } + + // Compare bytes only after finding a valid child node 'p' (buffer index) + for (i = 1; i < F; i++) + if ((cmp = text_buf[key_pos + i] - text_buf[p + i]) != 0) break; + + if (i > match_length) + { + match_position = p; + match_length = i; + if (match_length >= F) break; + } + } + + // Replace node logic + dad[r_node] = dad[p]; lson[r_node] = lson[p]; rson[r_node] = rson[p]; + dad[lson[p]] = r_node; dad[rson[p]] = r_node; + if (rson[dad[p]] == p) rson[dad[p]] = r_node; + else lson[dad[p]] = r_node; + dad[p] = NIL; + } + + void DeleteNode (int p) + { + int q; + if (dad[p] == NIL) return; + if (rson[p] == NIL) q = lson[p]; + else if (lson[p] == NIL) q = rson[p]; + else + { + q = lson[p]; + if (rson[q] != NIL) + { + do { q = rson[q]; } while (rson[q] != NIL); + rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q]; + lson[q] = lson[p]; dad[lson[p]] = q; + } + rson[q] = rson[p]; dad[rson[p]] = q; + } + dad[q] = dad[p]; + if (rson[dad[p]] == p) rson[dad[p]] = q; + else lson[dad[p]] = q; + dad[p] = NIL; + } + + int code_buf_ptr = 1; + byte[] code_buf = new byte[17]; + byte mask = 1; + int s = 0, r = N - F; + int len = 0; + + for (int j = 0; j < r; j++) text_buf[j] = 0x20; + + int bytes_read = 0; + for (len = 0; len < F && bytes_read < input.Length; len++) + text_buf[r + len] = input[bytes_read++]; + + if (len == 0) return new byte[0]; + + for (int j = 1; j <= F; j++) InsertNode (r - j); + InsertNode (r); + + do + { + if (match_length > len) match_length = len; + if (match_length <= THR) + { + match_length = 1; + code_buf[0] |= mask; + code_buf[code_buf_ptr++] = text_buf[r]; + } + else + { + code_buf[code_buf_ptr++] = (byte)(match_position & 0xFF); + code_buf[code_buf_ptr++] = (byte)(((match_position >> 4) & 0xF0) | (match_length - (THR + 1))); + } + + if ((mask <<= 1) == 0) + { + outStream.Write (code_buf, 0, code_buf_ptr); + code_buf[0] = 0; code_buf_ptr = 1; mask = 1; + } + + int last_match_length = match_length; + + int i; + for (i = 0; i < last_match_length && bytes_read < input.Length; i++) + { + DeleteNode (s); + byte c = input[bytes_read++]; + text_buf[s] = c; + if (s < F - 1) text_buf[s + N] = c; + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode (r); + } + + while (bytes_read == input.Length && i++ < last_match_length) + { + DeleteNode (s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + if (--len != 0) InsertNode (r); + } + + } while (len > 0); + + if (code_buf_ptr > 1) + outStream.Write (code_buf, 0, code_buf_ptr); + + return outStream.ToArray(); + } + } + } +} \ No newline at end of file diff --git a/ArcFormats/Nexas/ArcPAC.cs b/ArcFormats/Nexas/ArcPAC.cs index 2aa5fded..ea2da638 100644 --- a/ArcFormats/Nexas/ArcPAC.cs +++ b/ArcFormats/Nexas/ArcPAC.cs @@ -81,7 +81,7 @@ namespace GameRes.Formats.NeXAS Settings = new[] { PacEncoding }; } - EncodingSetting PacEncoding = new EncodingSetting ("NexasEncodingCP", "DefaultEncoding"); + readonly EncodingSetting PacEncoding = new EncodingSetting ("NexasEncodingCP", "DefaultEncoding"); public override ArcFile TryOpen (ArcView file) { @@ -89,17 +89,17 @@ namespace GameRes.Formats.NeXAS return null; List dir = null; - INexasIndexReader reader = new IndexReader (file, PacEncoding.Get()); + INexasIndexReader reader = new IndexReaderV1 (file, PacEncoding.Get ()); try { - dir = reader.Read(); + dir = reader.Read (); } catch {} if (null == dir) { - reader = new OldIndexReader (file); - dir = reader.Read(); + reader = new IndexReaderV0 (file); + dir = reader.Read (); if (null == dir) return null; @@ -110,18 +110,18 @@ namespace GameRes.Formats.NeXAS return new PacArchive (file, this, dir, reader.PackType); } - internal sealed class IndexReader : INexasIndexReader + internal sealed class IndexReaderV1 : INexasIndexReader { - ArcView m_file; - int m_count; - int m_pack_type; - Encoding m_encoding; + readonly ArcView m_file; + readonly int m_count; + readonly int m_pack_type; + readonly Encoding m_encoding; const int MaxNameLength = 0x40; public Compression PackType { get { return (Compression)m_pack_type; } } - public IndexReader (ArcView file, Encoding enc) + public IndexReaderV1 (ArcView file, Encoding enc) { m_file = file; m_count = file.View.ReadInt32 (4); @@ -139,15 +139,15 @@ namespace GameRes.Formats.NeXAS bool success = false; try { - success = ReadOld(); + success = ReadV0 (); } catch { /* ignore parse errors */ } - if (!success && !ReadNew()) + if (!success && !ReadV1 ()) return null; return m_dir; } - bool ReadNew () + bool ReadV1 () { uint index_size = m_file.View.ReadUInt32 (m_file.MaxOffset-4); int unpacked_size = m_count*0x4C; @@ -163,9 +163,9 @@ namespace GameRes.Formats.NeXAS return ReadFromStream (input, 0x40); } - bool ReadOld () + bool ReadV0 () { - using (var input = m_file.CreateStream()) + using (var input = m_file.CreateStream ()) { input.Position = 0xC; if (ReadFromStream (input, 0x20)) @@ -177,16 +177,16 @@ namespace GameRes.Formats.NeXAS bool ReadFromStream (IBinaryStream index, int name_length) { - m_dir.Clear(); + m_dir.Clear (); for (int i = 0; i < m_count; ++i) { var name = index.ReadCString (name_length, m_encoding); if (string.IsNullOrWhiteSpace (name)) return false; var entry = FormatCatalog.Instance.Create (name); - entry.Offset = index.ReadUInt32(); - entry.UnpackedSize = index.ReadUInt32(); - entry.Size = index.ReadUInt32(); + entry.Offset = index.ReadUInt32 (); + entry.UnpackedSize = index.ReadUInt32 (); + entry.Size = index.ReadUInt32 (); if (!entry.CheckPlacement (m_file.MaxOffset)) return false; switch (m_pack_type) @@ -212,14 +212,14 @@ namespace GameRes.Formats.NeXAS } } - internal sealed class OldIndexReader : INexasIndexReader + internal sealed class IndexReaderV0 : INexasIndexReader { - ArcView m_file; - uint m_header_size; + readonly ArcView m_file; + readonly uint m_header_size; public Compression PackType { get { return Compression.NeedDecryptionOnly; } } - public OldIndexReader (ArcView file) + public IndexReaderV0 (ArcView file) { m_file = file; m_header_size = file.View.ReadUInt32 (3); @@ -230,25 +230,25 @@ namespace GameRes.Formats.NeXAS public List Read () { m_dir = new List (); - using (var input = m_file.CreateStream()) + using (var input = m_file.CreateStream ()) { input.Position = 7; while (input.Position < m_header_size) { byte c; - List name_buffer = new List(); + List name_buffer = new List (); while (true) { - c = (byte)input.ReadByte(); + c = (byte)input.ReadByte (); if (c == 0) break; name_buffer.Add ((byte)~c); } - var name = Binary.GetCString (name_buffer.ToArray(), 0); + var name = Binary.GetCString (name_buffer.ToArray (), 0); if (string.IsNullOrWhiteSpace (name)) return null; var entry = FormatCatalog.Instance.Create (name); - entry.Offset = input.ReadUInt32() + m_header_size; - entry.Size = input.ReadUInt32(); + entry.Offset = input.ReadUInt32 () + m_header_size; + entry.Size = input.ReadUInt32 (); if (!entry.CheckPlacement (m_file.MaxOffset)) return null; m_dir.Add (entry); @@ -268,11 +268,14 @@ namespace GameRes.Formats.NeXAS return input; if (Compression.NeedDecryptionOnly == pac.PackType) { - var data = new byte[entry.Size]; - input.Read (data, 0, data.Length); - for (int i = 0; i < Math.Min (3, data.Length); i++) - data[i] = (byte)~data[i]; - return new BinMemoryStream (data, entry.Name); + using (input) + { + var data = new byte[entry.Size]; + input.Read (data, 0, data.Length); + for (int i = 0; i < Math.Min (3, data.Length); i++) + data[i] = (byte)~data[i]; + return new BinMemoryStream (data, entry.Name); + } } if (null == pent || !pent.IsPacked) return input; @@ -296,8 +299,11 @@ namespace GameRes.Formats.NeXAS case Compression.Zstd: case Compression.ZstdOrNone: { - var unpacked = ZstdDecompress (input, pent.UnpackedSize); - return new BinMemoryStream (unpacked, entry.Name); + using (input) + { + var unpacked = ZstdDecompress (input, pent.UnpackedSize); + return new BinMemoryStream (unpacked, entry.Name); + } } default: return input; @@ -308,15 +314,24 @@ namespace GameRes.Formats.NeXAS { var dst = new byte[unpacked_size]; var decoder = new HuffmanDecoder (packed, dst); - return decoder.Unpack(); + return decoder.Unpack (); } static private byte[] ZstdDecompress (Stream s, uint unpackedSize) { - using (var ds = new ZstdNet.DecompressionStream (s)) + using (var ds = new ZstdSharp.DecompressionStream (s)) { var dst = new byte[unpackedSize]; - ds.Read (dst, 0, dst.Length); + int decompressedSize = 0; + + while (decompressedSize < unpackedSize) + { + var count = ds.Read (dst, decompressedSize, (int)unpackedSize-decompressedSize); + if (0 == count) + return dst; + decompressedSize += count; + } + return dst; } } diff --git a/ArcFormats/Nexas/ImageGRP.cs b/ArcFormats/Nexas/ImageGRP.cs index 12fd3c22..94b396de 100644 --- a/ArcFormats/Nexas/ImageGRP.cs +++ b/ArcFormats/Nexas/ImageGRP.cs @@ -104,7 +104,7 @@ namespace GameRes.Formats.NeXAS switch (info.BPP) { case 8: Format = PixelFormats.Indexed8; break; - case 16: Format = PixelFormats.Bgr555; break; + case 16: Format = info.Version == 2 ? PixelFormats.Bgr565 : PixelFormats.Bgr555; break; case 24: Format = PixelFormats.Bgr24; break; case 32: Format = PixelFormats.Bgr32; break; default: throw new NotSupportedException ("Not supported GRP image color depth"); diff --git a/ArcFormats/NitroPlus/ArcNPK.cs b/ArcFormats/NitroPlus/ArcNPK.cs index 19e038d8..98ab1728 100644 --- a/ArcFormats/NitroPlus/ArcNPK.cs +++ b/ArcFormats/NitroPlus/ArcNPK.cs @@ -527,7 +527,7 @@ namespace GameRes.Formats.NitroPlus m_stream = new DeflateStream (m_stream, CompressionMode.Decompress); break; case 3: - m_stream = new ZstdNet.DecompressionStream (m_stream); + m_stream = new ZstdSharp.DecompressionStream (m_stream); break; } } diff --git a/ArcFormats/PkWare/ArcZIP.cs b/ArcFormats/PkWare/ArcZIP.cs index 86c72c20..9d254de1 100644 --- a/ArcFormats/PkWare/ArcZIP.cs +++ b/ArcFormats/PkWare/ArcZIP.cs @@ -126,8 +126,8 @@ namespace GameRes.Formats.PkWare internal ArcFile OpenZipArchive (ArcView file, Stream input) { - SharpZip.ZipStrings.CodePage = Properties.Settings.Default.ZIPEncodingCP; - var zip = new SharpZip.ZipFile (input); + var sc = SharpZip.StringCodec.FromCodePage (Properties.Settings.Default.ZIPEncodingCP); + var zip = new SharpZip.ZipFile (input, false, sc); try { var files = zip.Cast().Where (z => !z.IsDirectory); diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs index 2c870f61..8fba100a 100644 --- a/ArcFormats/Properties/Settings.Designer.cs +++ b/ArcFormats/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace GameRes.Formats.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -585,20 +585,7 @@ namespace GameRes.Formats.Properties { this["BELLTitle"] = value; } } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string BELLDATATitle - { - get { - return ((string)(this["BELLDATATitle"])); - } - set { - this["BELLDATATitle"] = value; - } - } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("")] @@ -862,5 +849,17 @@ namespace GameRes.Formats.Properties { this["TIM2AlphaFormat"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string BELLDATATitle { + get { + return ((string)(this["BELLDATATitle"])); + } + set { + this["BELLDATATitle"] = value; + } + } } } diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings index 50457412..35cbe992 100644 --- a/ArcFormats/Properties/Settings.settings +++ b/ArcFormats/Properties/Settings.settings @@ -209,5 +209,8 @@ RGBA + + + \ No newline at end of file diff --git a/ArcFormats/QoiCodec.cs b/ArcFormats/QoiCodec.cs new file mode 100644 index 00000000..377448bf --- /dev/null +++ b/ArcFormats/QoiCodec.cs @@ -0,0 +1,118 @@ +//! \file QoiCodec.cs +//! \date Sun Apr 12 2026 +//! \brief Quite OK Image Format +// +// Copyright (C) 2016 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.IO; + +namespace GameRes.Formats.QoiCodec +{ + static class QoiConst + { + public const int Index = 0x00; + public const int Diff = 0x40; + public const int Luma = 0x80; + public const int Run = 0xC0; + public const int Rgb = 0xFE; + public const int Rgba = 0xFF; + public const int Mask2 = 0xC0; + public const int HashTableSize = 64; + } + + public class QoiDecodeStream + { + readonly IBinaryStream m_input; + readonly byte[] m_table; + uint m_pixel; + + public QoiDecodeStream (IBinaryStream input) + { + m_input = input; + m_table = new byte [4*QoiConst.HashTableSize]; + m_pixel = 0xFF000000; + } + + public int Read (out uint output) + { + var r = (byte)m_pixel; + var g = (byte)(m_pixel >> 8); + var b = (byte)(m_pixel >> 16); + var a = (byte)(m_pixel >> 24); + var run = 1; + var b1 = m_input.ReadByte (); + if (-1 == b1) + throw new EndOfStreamException (); + if (QoiConst.Rgb == b1) + { + var rgb = m_input.ReadInt24 (); + r = (byte)rgb; + g = (byte)(rgb >> 8); + b = (byte)(rgb >> 16); + } + else if (QoiConst.Rgba == b1) + { + var rgba = m_input.ReadInt32 (); + r = (byte)rgba; + g = (byte)(rgba >> 8); + b = (byte)(rgba >> 16); + a = (byte)(rgba >> 24); + } + else if (QoiConst.Index == (b1 & QoiConst.Mask2)) + { + var p1 = (b1 & ~QoiConst.Mask2) * 4; + r = m_table[p1 ]; + g = m_table[p1+1]; + b = m_table[p1+2]; + a = m_table[p1+3]; + } + else if (QoiConst.Diff == (b1 & QoiConst.Mask2)) + { + r += (byte)(((b1 >> 4) & 0x03) - 2); + g += (byte)(((b1 >> 2) & 0x03) - 2); + b += (byte)((b1 & 0x03) - 2); + } + else if (QoiConst.Luma == (b1 & QoiConst.Mask2)) + { + var b2 = m_input.ReadByte (); + if (-1 == b2) + throw new EndOfStreamException (); + var vg = (b1 & 0x3F) - 32; + r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F)); + g += (byte)vg; + b += (byte)(vg - 8 + (b2 & 0x0F)); + } + else if (QoiConst.Run == (b1 & QoiConst.Mask2)) + { + run = (b1 & 0x3F) + 1; + } + var p2 = (r*3 + g*5 + b*7 + a*11) % QoiConst.HashTableSize*4; + m_table[p2 ] = r; + m_table[p2+1] = g; + m_table[p2+2] = b; + m_table[p2+3] = a; + m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24)); + output = (uint)(b | (g << 8) | (r << 16) | (a << 24)); + return run; + } + } +} diff --git a/ArcFormats/Resources/Formats.dat b/ArcFormats/Resources/Formats.dat index 8c747207..7907722b 100644 Binary files a/ArcFormats/Resources/Formats.dat and b/ArcFormats/Resources/Formats.dat differ diff --git a/ArcFormats/Silky/ArcAi6Win.cs b/ArcFormats/Silky/ArcAi6Win.cs index 72e8269e..05843132 100644 --- a/ArcFormats/Silky/ArcAi6Win.cs +++ b/ArcFormats/Silky/ArcAi6Win.cs @@ -27,7 +27,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Linq; using GameRes.Compression; +using GameRes.Formats.Strings; using GameRes.Utility; namespace GameRes.Formats.Silky @@ -39,7 +41,7 @@ namespace GameRes.Formats.Silky public override string Description { get { return "AI6WIN engine resource archive"; } } public override uint Signature { get { return 0; } } public override bool IsHierarchic { get { return true; } } - public override bool CanWrite { get { return false; } } + public override bool CanWrite { get { return true; } } public Ai6Opener () { @@ -96,5 +98,108 @@ namespace GameRes.Formats.Silky var input = arc.File.CreateStream (entry.Offset, entry.Size); return new LzssStream (input); } + + public override void Create (Stream output, IEnumerable list, ResourceOptions options, EntryCallback callback) + { + int count = list.Count (); + if (0 == count) + throw new InvalidOperationException ("Archive is empty"); + + uint index_size = (uint)(count * (0x104 + 12)); + uint data_offset = 4 + index_size; + + if (null != callback) + callback (count + 1, null, null); + + var entries = new List (count); + uint current_offset = data_offset; + int callback_count = 0; + + foreach (var entry in list) + { + if (null != callback) + callback (callback_count++, entry, arcStrings.MsgAddingFile); + + string name = entry.Name; + if (name.Contains ("\\")) + name = name.Replace ("\\", "/"); + + try + { + long file_size = 0; + using (var input = File.OpenRead (entry.Name)) + { + file_size = input.Length; + } + + if (file_size > uint.MaxValue) + throw new FileLoadException ("File is too large for this format."); + + var index_entry = new IndexEntry + { + SourcePath = entry.Name, + ArchiveName = name, + Size = (uint)file_size, + UnpackedSize = (uint)file_size, + Offset = current_offset + }; + + entries.Add (index_entry); + current_offset += (uint)file_size; + } + catch (Exception X) + { + // 修复点:此处改用硬编码字符串或尝试 MsgFileError + throw new InvalidFileName (entry.Name, "Error opening file", X); + } + } + + if (null != callback) + callback (callback_count++, null, arcStrings.MsgWritingIndex); + + using (var writer = new BinaryWriter (output)) + { + writer.Write ((uint)count); + + foreach (var entry in entries) + { + var name_bytes = Encodings.cp932.GetBytes (entry.ArchiveName); + var name_buf = new byte[0x104]; + + int copyLength = Math.Min (name_bytes.Length, name_buf.Length); + Array.Copy (name_bytes, name_buf, copyLength); + + byte key = (byte)(name_bytes.Length + 1); + + for (int i = 0; i < copyLength; ++i) + { + name_buf[i] = (byte)((name_buf[i] + key) & 0xFF); + key--; + } + + writer.Write (name_buf); + writer.Write (Binary.BigEndian (entry.Size)); + writer.Write (Binary.BigEndian (entry.UnpackedSize)); + writer.Write (Binary.BigEndian (entry.Offset)); + } + + foreach (var entry in entries) + { + using (var input = File.OpenRead (entry.SourcePath)) + { + input.CopyTo (output); + } + } + } + } + + private class IndexEntry + { + public string SourcePath { get; set; } + public string ArchiveName { get; set; } + public uint Size { get; set; } + public uint UnpackedSize { get; set; } + public uint Offset { get; set; } + } } } diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs index accb3959..1a032887 100644 --- a/ArcFormats/Strings/arcStrings.Designer.cs +++ b/ArcFormats/Strings/arcStrings.Designer.cs @@ -754,14 +754,14 @@ namespace GameRes.Formats.Strings { /// /// Looks up a localized string similar to Choose Tim2 image alpha format. - /// It can't be read correctly from the file. + ///It can't be read correctly from the file.. /// public static string Tim2AlphaFormat { get { return ResourceManager.GetString("Tim2AlphaFormat", resourceCulture); } } - + /// /// Looks up a localized string similar to Hex number. /// diff --git a/ArcFormats/Strings/arcStrings.zh-Hant.resx b/ArcFormats/Strings/arcStrings.zh-Hant.resx new file mode 100644 index 00000000..b7b18e6b --- /dev/null +++ b/ArcFormats/Strings/arcStrings.zh-Hant.resx @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 原壓縮檔案 + + + 新的壓縮檔案將會包含原檔案中的項目。 + + + 請選擇原壓縮檔案 + + + Amaterasu Translations Muv-Luv 壓縮檔案 + + + 未找到適合AMI壓縮檔案的項目。 + + + 壓縮檔案已加密。 +請選擇正確的加密方式。 + + + 沒有加密。 + + + 重置 + + + âge專用圖像格式 + + + Liar-soft專用腳本格式 + + + 尚不支持創建加密壓縮檔案。 + + + CatSystem2引擎壓縮檔案 + + + 密鑰是一個32位的16進制整數 + + + 密鑰 + + + 壓縮檔案目錄已加密。 + + + 工畫堂引擎壓縮檔案 + + + 密碼 + + + 方式 + + + Liar-soft圖像檔案 + + + 正在添加檔案…… + + + 正在計算校驗和…… + + + 正在壓縮索引…… + + + 創建壓縮檔案需要密鑰。 + + + 加密方式暫不支持。 + + + 檔案擴展名太長。 + + + 檔案名太長。 + + + 檔案名中包含非法字符。 + + + {0}: 無法識別圖像格式。 + + + 無效的檔案版本。 + + + 檔案名需要包含擴展名。 + + + 檔案數目超過壓縮檔案限制。 + + + 正在更新索引…… + + + 正在建立索引…… + + + 壓縮內容 + + + Nitro+壓縮檔案 + + + 密鑰 +(未加密內容仍需要) + + + Nitro+ Steins;Gate壓縮檔案 + + + NScripter引擎壓縮檔案 + + + 壓縮檔案類型 + + + 壓縮方式 + + + + + + Flying Shine壓縮檔案 + + + 打亂內容 + + + Ren'Py引擎壓縮檔案 + + + 32位密鑰 + + + Amaterasu Translations Muv-Luv腳本檔案 + + + 檔案名編碼 + + + 十六進制數 + + + 檔案名最大長度 +(不包括擴展名) + + + Liar-soft遊戲壓縮檔案 + + + 壓縮內容 + + + 壓縮目錄 + + + KiriKiri引擎壓縮檔案 + + + 版本 + + + 保持目錄結構 + + + Yu-Ris引擎壓縮檔案 + + + 8位密鑰 + + + 壓縮檔案版本 + + + 壓縮檔案的目錄已加密。 +請選擇正確的加密方式。 + + + 加密方式 + + + 密鑰 + + + 預設 + + + 32位十六進制數 + + + 預設 + + + 壓縮檔案包含已加密的腳本。 +請選擇加密方式或輸入密碼。 + + + 請選擇遊戲名稱或輸入密碼 + + + 檔案與RPM壓縮檔案格式相似, +但無法猜測正確的密鑰。 +請選擇正確的加密方式。 + + + 使用預設加密方式 + + + 請選擇遊戲的EXE檔案 + + + 檢查EXE + + + 可執行檔案 + + + 請按「檢查EXE」按鈕在遊戲程序檔案中尋找密鑰。 + + + 或者,您也可以輸入壓縮檔案的密鑰或者選擇一個現成的加密方式。 + + + 在{0}當中沒有找到密鑰。 + + + 忽略加密。 + + + 圖像已加密。 + + + 請選擇遊戲名稱或輸入密鑰 + + + 8位加密密鑰 + + + 壓縮檔案的內容已被加密。 + + + 嘗試猜測 + + + 壓縮檔案可能被加密,選擇適當的加密方案。 + + + 壓縮檔案可能被加密,選擇適當的加密方案。 + + + 請選擇遊戲名稱或輸入密鑰 + + + 圖像已加密。 +請選擇正確的加密方式。 + + + 壓縮檔案包含已加密的腳本。 +請選擇加密方式或輸入密碼。 + + + 從RC8位圖中加載透明度數據 + + + 自動組合增量幀 + + + 壓縮檔案的內容已被加密。 + + + 修復Ogg檔案校驗和 + + + 預設檔案名編碼 + + + 預設音頻採樣率 + + + 選擇Tim2圖片透明度格式。 +這無法從檔案中正確獲取。 + + \ No newline at end of file diff --git a/ArcFormats/VnMaker/ArcZIP.cs b/ArcFormats/VnMaker/ArcZIP.cs index e8ac81f5..4b13bf3b 100644 --- a/ArcFormats/VnMaker/ArcZIP.cs +++ b/ArcFormats/VnMaker/ArcZIP.cs @@ -102,8 +102,8 @@ namespace GameRes.Formats.VnMaker internal ArcFile OpenZipArchive (ArcView file, Stream input) { - SharpZip.ZipStrings.CodePage = Properties.Settings.Default.ZIPEncodingCP; - var zip = new SharpZip.ZipFile (input); + var sc = SharpZip.StringCodec.FromCodePage (Properties.Settings.Default.ZIPEncodingCP); + var zip = new SharpZip.ZipFile (input, false, sc); try { var files = zip.Cast().Where (z => !z.IsDirectory); diff --git a/ArcFormats/WebPCodec.cs b/ArcFormats/WebPCodec.cs new file mode 100644 index 00000000..a13bd0d7 --- /dev/null +++ b/ArcFormats/WebPCodec.cs @@ -0,0 +1,64 @@ +//! \file WebPCodec.cs +//! \date Sun Apr 12 2026 +//! \brief Google WebP image format. +// +// Copyright (C) 2016 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; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace GameRes.Formats +{ + public class WebPCodec + { + [DllImport("libwebp.dll", EntryPoint = "WebPGetInfo", CallingConvention = CallingConvention.Cdecl)] + public static extern int WebPGetInfo ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, ref int width, ref int height); + + [DllImport("libwebp.dll", EntryPoint = "WebPDecodeBGRAInto", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr WebPDecodeBGRAInto ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride); + + [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hReservedNull, uint dwFlags); + + static bool loaded = false; + + const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100; + const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; + + public static void Load () + { + if (loaded) + return; + var dir = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location); + dir = Path.Combine (dir, (IntPtr.Size == 4) ? "x86" : "x64"); + var path = Path.Combine (dir, "libwebp.dll"); + path = Path.GetFullPath (path); + var lib = LoadLibraryEx (path, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); + if (IntPtr.Zero == lib) + throw new Win32Exception (Marshal.GetLastWin32Error ()); + loaded = true; + } + } +} diff --git a/ArcFormats/YuRis/ArcYPF.cs b/ArcFormats/YuRis/ArcYPF.cs index bc864628..c9c12131 100644 --- a/ArcFormats/YuRis/ArcYPF.cs +++ b/ArcFormats/YuRis/ArcYPF.cs @@ -31,7 +31,6 @@ using System.ComponentModel.Composition; using GameRes.Compression; using GameRes.Formats.Strings; using GameRes.Utility; -using Snappy; namespace GameRes.Formats.YuRis { @@ -185,7 +184,7 @@ namespace GameRes.Formats.YuRis { var compress_data = new byte[entry.Size]; input.Read(compress_data, 0, compress_data.Length); - var decomprrss_data = SnappyCodec.Uncompress(compress_data); + var decomprrss_data = Snappier.Snappy.DecompressToArray (compress_data); input = new BinMemoryStream(decomprrss_data, entry.Name); break; } diff --git a/ArcFormats/YuRis/ImageYDG.cs b/ArcFormats/YuRis/ImageYDG.cs new file mode 100644 index 00000000..6f408479 --- /dev/null +++ b/ArcFormats/YuRis/ImageYDG.cs @@ -0,0 +1,237 @@ +//! \file ImageYDG.cs +//! \date Sun Apr 12 15:08:10 2026 +//! \brief YU-RIS compressed image. +// +// Copyright (C) 2026 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.QoiCodec; +using GameRes.Utility; +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace GameRes.Formats.YuRis +{ + internal class YdgMetaData : ImageMetaData + { + public int HeaderLength; + } + + internal class YdgSlice + { + public uint Offset; + public uint Size; + public byte[] Data; + public int X; + public int Y; + public int Height; + } + + [Export(typeof(ImageFormat))] + public class YdgFormat : ImageFormat + { + public override string Tag { get { return "YDG"; } } + public override string Description { get { return "YU-RIS compressed image format"; } } + public override uint Signature { get { return 0x00474459; } } // 'YDG\0' + + public YdgFormat () + { + Extensions = new string[] { "ydg" }; + } + + public override ImageMetaData ReadMetaData (IBinaryStream stream) + { + var header = stream.ReadHeader (0x30); + if (!header.AsciiEqual ("YDG\0")) + return null; + if (!header.AsciiEqual (4, "YU-RIS")) + return null; + return new YdgMetaData + { + Width = header.ToUInt16 (0x20), + Height = header.ToUInt16 (0x22), + BPP = 32, + HeaderLength = header.ToInt32 (0x10), + }; + } + + public override ImageData Read (IBinaryStream stream, ImageMetaData info) + { + var meta = (YdgMetaData) info; + var reader = new YdgReader (stream, meta); + reader.Unpack (); + return ImageData.Create (meta, reader.Format, null, reader.Data); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("YdgFormat.Write not implemented"); + } + } + + internal sealed class YdgReader + { + readonly IBinaryStream m_input; + readonly YdgMetaData m_info; + readonly byte[] m_output; + + public PixelFormat Format { get; private set; } + public byte[] Data { get { return m_output; } } + + public YdgReader (IBinaryStream input, YdgMetaData info) + { + m_input = input; + m_info = info; + m_output = new byte[4*(int)info.Width*(int)info.Height]; + Format = PixelFormats.Bgra32; + } + + public void Unpack () + { + var slices = ReadSlices (); + var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; + Parallel.ForEach (slices, options, slice => + { + var pixels = DecodeSlice (slice.Data, out int slice_width, out int slice_height); + if (slice_height > slice.Height) + throw new InvalidFormatException (); + var height = Math.Min (slice_height, slice.Height); + CopySlice (pixels, slice_width, height, slice.X, slice.Y); + }); + } + + YdgSlice[] ReadSlices () + { + m_input.Position = m_info.HeaderLength; + var count = m_input.ReadInt32 (); + if (count <= 0) + throw new InvalidFormatException (); + var slices = new YdgSlice[count]; + var y = 0; + for (var i = 0; i < slices.Length; i++) + { + var slice = new YdgSlice + { + Offset = m_input.ReadUInt32 (), + Size = m_input.ReadUInt32 (), + X = m_input.ReadUInt16 (), + Y = y, + Height = m_input.ReadUInt16 (), + }; + y += slice.Height; + m_input.ReadUInt32 (); + slices[i] = slice; + } + foreach (var slice in slices) + { + m_input.Position = slice.Offset; + slice.Data = m_input.ReadBytes ((int)slice.Size); + } + return slices; + } + + byte[] DecodeSlice (byte[] data, out int width, out int height) + { + if (Binary.AsciiEqual (data, 0, "RIFF") && Binary.AsciiEqual (data, 8, "WEBP")) + return DecodeWebP (data, out width, out height); + return DecodeQoi (data, out width, out height); + } + + void CopySlice (byte[] pixels, int src_width, int src_height, int dst_x, int dst_y) + { + var copy_width = Math.Min (src_width, (int)m_info.Width-dst_x); + var copy_height = Math.Min (src_height, (int)m_info.Height-dst_y); + if (copy_width <= 0 || copy_height <= 0) + return; + var src_stride = 4*src_width; + var dst_stride = 4*(int)m_info.Width; + var dst = dst_y*dst_stride + 4*dst_x; + var row_bytes = 4*copy_width; + var src = 0; + for (var y = 0; y < copy_height; y++) + { + Buffer.BlockCopy (pixels, src, m_output, dst, row_bytes); + src += src_stride; + dst += dst_stride; + } + } + + byte[] DecodeWebP (byte[] data, out int width, out int height) + { + width = 0; + height = 0; + WebPCodec.Load (); + if (1 != WebPCodec.WebPGetInfo (data, (UIntPtr)data.Length, ref width, ref height)) + throw new InvalidFormatException ("WebP image decoder failed."); + var stride = 4*width; + var output = new byte[stride*height]; + var handle = GCHandle.Alloc (output, GCHandleType.Pinned); + try + { + if (IntPtr.Zero == WebPCodec.WebPDecodeBGRAInto (data, (UIntPtr)data.Length, handle.AddrOfPinnedObject (), (UIntPtr)output.Length, stride)) + throw new InvalidFormatException ("WebP image decoder failed."); + } + finally + { + handle.Free (); + } + return output; + } + + byte[] DecodeQoi (byte[] data, out int width, out int height) + { + using (var input = new BinMemoryStream (data)) + { + if (0x66696F71 != input.ReadInt32 ()) // "qoif" + throw new InvalidFormatException ("Invalid QOI signature"); + width = (int)Binary.BigEndian (input.ReadUInt32 ()); + height = (int)Binary.BigEndian (input.ReadUInt32 ()); + var channels = input.ReadByte (); + var colorspace = input.ReadByte (); + var count = 4*width*height; + var output = new byte[count]; + var qoi = new QoiDecodeStream (input); + uint pixel = 0; + var run = 0; + var dst = 0; + while (dst < count) + { + if (run > 1) + --run; + else + { + run = qoi.Read (out pixel); + } + output[dst ] = (byte)pixel; + output[dst+1] = (byte)(pixel >> 8); + output[dst+2] = (byte)(pixel >> 16); + output[dst+3] = (byte)(pixel >> 24); + dst += 4; + } + return output; + } + } + } +} diff --git a/ArcFormats/app.config b/ArcFormats/app.config index ef1cc161..3443b678 100644 --- a/ArcFormats/app.config +++ b/ArcFormats/app.config @@ -31,9 +31,6 @@ shift-jis - - None - @@ -209,37 +206,50 @@ 932 - RGBA + RGBA + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ArcFormats/packages.config b/ArcFormats/packages.config index 74a50b67..a9a3a8fd 100644 --- a/ArcFormats/packages.config +++ b/ArcFormats/packages.config @@ -1,18 +1,31 @@  - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console/App.config b/Console/App.config index 45437954..9b3a103e 100644 --- a/Console/App.config +++ b/Console/App.config @@ -1,6 +1,42 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Console/GARbro.Console.csproj b/Console/GARbro.Console.csproj index 1e0788c2..c09babd2 100644 --- a/Console/GARbro.Console.csproj +++ b/Console/GARbro.Console.csproj @@ -9,7 +9,7 @@ Properties GARbro GARbro.Console - v4.6.1 + v4.7.2 512 publish\ true @@ -41,7 +41,7 @@ AnyCPU - pdbonly + none true ..\bin\Release\ TRACE diff --git a/Experimental/Experimental.csproj b/Experimental/Experimental.csproj index 4b5690a3..8b8cff1c 100644 --- a/Experimental/Experimental.csproj +++ b/Experimental/Experimental.csproj @@ -9,7 +9,7 @@ Properties GameRes.Extra ArcExtra - v4.6.1 + v4.7.2 512 @@ -49,11 +49,11 @@ MinimumRecommendedRules.ruleset - - ..\packages\Concentus.1.1.7\lib\portable-net45+win+wpa81+wp80\Concentus.dll + + ..\packages\Concentus.2.2.2\lib\net452\Concentus.dll - - ..\packages\Concentus.Oggfile.1.0.4\lib\net45\Concentus.Oggfile.dll + + ..\packages\Concentus.Oggfile.1.0.6\lib\net452\Concentus.Oggfile.dll ..\packages\MSFTCompressionCab.1.0.0\lib\Microsoft.Deployment.Compression.dll @@ -72,70 +72,31 @@ - - ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll - - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll - - - ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\lib\net46\System.Data.SQLite.dll + + ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.119.0\lib\net46\System.Data.SQLite.dll - - ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll - - - ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll - - - ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll - - - ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll - - - ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - - ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll - - - ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll - - - ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll - - ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll - - - ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll - - - ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll @@ -144,12 +105,9 @@ - - ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll - - - ..\packages\ZstdNet.1.4.5\lib\net45\ZstdNet.dll + + ..\packages\ZstdSharp.Port.0.8.7\lib\net462\ZstdSharp.dll @@ -183,6 +141,8 @@ + + PreserveNewest @@ -216,16 +176,6 @@ - - - PreserveNewest - - - - - PreserveNewest - - ipt @@ -242,15 +192,6 @@ exit 0 $(ProjectDir)Artemis\IPT - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - @@ -263,21 +204,11 @@ exit 0 - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + - - - \ No newline at end of file diff --git a/Experimental/Opus/AudioOPUS.cs b/Experimental/Opus/AudioOPUS.cs index 41bab8f6..b8742c0f 100644 --- a/Experimental/Opus/AudioOPUS.cs +++ b/Experimental/Opus/AudioOPUS.cs @@ -26,6 +26,7 @@ using System; using System.ComponentModel.Composition; using System.IO; +using Concentus; using Concentus.Oggfile; using Concentus.Structs; @@ -58,7 +59,7 @@ namespace GameRes.Formats.Opus // int rate = header.ToInt32 (header_pos+0xC); int rate = 48000; file.Position = 0; - var decoder = OpusDecoder.Create (rate, channels); + var decoder = OpusCodecFactory.CreateDecoder (rate, channels); var ogg_in = new OpusOggReadStream (decoder, file.AsStream); var pcm = new MemoryStream(); try diff --git a/Experimental/SakanaGL/ArcSX.cs b/Experimental/SakanaGL/ArcSX.cs index 44ef1e65..a36fe961 100644 --- a/Experimental/SakanaGL/ArcSX.cs +++ b/Experimental/SakanaGL/ArcSX.cs @@ -123,10 +123,10 @@ namespace GameRes.Formats.Sakana internal static byte[] UnpackZstd (byte[] data) { int unpacked_size = BigEndian.ToInt32 (data, 0); - using (var dec = new ZstdNet.Decompressor()) + using (var dec = new ZstdSharp.Decompressor()) { - var packed = new ArraySegment (data, 4, data.Length - 4); - return dec.Unwrap (packed, unpacked_size); + var packed = new Span (data, 4, data.Length - 4); + return dec.Unwrap (packed, unpacked_size).ToArray(); } } diff --git a/Experimental/app.config b/Experimental/app.config index 741af0df..a276a4f5 100644 --- a/Experimental/app.config +++ b/Experimental/app.config @@ -1,15 +1,46 @@  - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/packages.config b/Experimental/packages.config index 2a604b3b..477f0315 100644 --- a/Experimental/packages.config +++ b/Experimental/packages.config @@ -1,34 +1,16 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/Experimental/x64/libzstd.dll b/Experimental/x64/libzstd.dll deleted file mode 100644 index a5def68e..00000000 Binary files a/Experimental/x64/libzstd.dll and /dev/null differ diff --git a/Experimental/x86/libzstd.dll b/Experimental/x86/libzstd.dll deleted file mode 100644 index 55c5669c..00000000 Binary files a/Experimental/x86/libzstd.dll and /dev/null differ diff --git a/GUI/App.config b/GUI/App.config index c702f329..125162e6 100644 --- a/GUI/App.config +++ b/GUI/App.config @@ -1,12 +1,12 @@ - +
- - + + @@ -99,8 +99,36 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GUI/AutoComplete.cs b/GUI/AutoComplete.cs index 72c713d5..af90dd0d 100644 --- a/GUI/AutoComplete.cs +++ b/GUI/AutoComplete.cs @@ -48,11 +48,11 @@ namespace GARbro.GUI public bool IsTextBoxFocused { - get { return (bool)GetValue (HasFocusProperty); } - private set { SetValue (HasFocusProperty, value); } + get { return (bool)GetValue (IsTextBoxFocusedProperty); } + private set { SetValue (IsTextBoxFocusedProperty, value); } } - public static readonly DependencyProperty HasFocusProperty = + public static readonly DependencyProperty IsTextBoxFocusedProperty = DependencyProperty.RegisterAttached ("IsTextBoxFocused", typeof(bool), typeof(ExtAutoCompleteBox), new UIPropertyMetadata()); protected override void OnKeyDown (KeyEventArgs e) diff --git a/GUI/GARbro.GUI.csproj b/GUI/GARbro.GUI.csproj index 8291753f..024db021 100644 --- a/GUI/GARbro.GUI.csproj +++ b/GUI/GARbro.GUI.csproj @@ -9,7 +9,7 @@ Properties GARbro.GUI GARbro.GUI - v4.6.1 + v4.7.2 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 @@ -89,22 +89,61 @@ - - ..\packages\WindowsAPICodePack-Core.1.1.2\lib\Microsoft.WindowsAPICodePack.dll + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll - - ..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.dll - - ..\packages\NAudio.1.10.0\lib\net35\NAudio.dll + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.Core.dll + + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.ExtendedLinguisticServices.dll + + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.Sensors.dll + + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.Shell.dll + + + ..\packages\WindowsAPICodePack.8.0.14\lib\net472\Microsoft.WindowsAPICodePack.ShellExtensions.dll + + + ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll + + + ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll + + + ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll + + + ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll + + + ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll + + + ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll + + + ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll + + ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + - ..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll + ..\packages\WPFToolkits.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll - ..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Layout.Toolkit.dll + ..\packages\WPFToolkits.3.5.50211.1\lib\System.Windows.Controls.Layout.Toolkit.dll @@ -112,14 +151,12 @@ - - 4.0 - + - ..\packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll + ..\packages\WPFToolkits.3.5.50211.1\lib\WPFToolkit.dll @@ -277,6 +314,7 @@ + @@ -309,23 +347,6 @@ False - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - @@ -347,11 +368,4 @@ exit 0 - \ No newline at end of file diff --git a/GUI/MainWindow.xaml b/GUI/MainWindow.xaml index 1d9d6285..0794df06 100644 --- a/GUI/MainWindow.xaml +++ b/GUI/MainWindow.xaml @@ -5,7 +5,7 @@ xmlns:jv="clr-namespace:JustView" xmlns:s="clr-namespace:GARbro.GUI.Strings" xmlns:p="clr-namespace:GARbro.GUI.Properties" - Title="GARbro" MinHeight="250" ResizeMode="CanResizeWithGrip" + Title="GARbro-Mod" MinHeight="250" ResizeMode="CanResizeWithGrip" Loaded="WindowLoaded" KeyDown="WindowKeyDown" AllowDrop="True" Drop="OnDropEvent" diff --git a/GUI/Properties/Settings.Designer.cs b/GUI/Properties/Settings.Designer.cs index ec0b8a0c..bfc80ad0 100644 --- a/GUI/Properties/Settings.Designer.cs +++ b/GUI/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace GARbro.GUI.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/GUI/Strings/guiStrings.Designer.cs b/GUI/Strings/guiStrings.Designer.cs index 9ffc54d4..af8b1103 100644 --- a/GUI/Strings/guiStrings.Designer.cs +++ b/GUI/Strings/guiStrings.Designer.cs @@ -790,7 +790,7 @@ namespace GARbro.GUI.Strings { } /// - /// Looks up a localized string similar to Image {0} x {1} x {2}bpp. + /// Looks up a localized string similar to Image {0} x {1} x {2}bpp [{3}]. /// public static string MsgImageSize { get { @@ -1210,6 +1210,15 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to OK. + /// + public static string TextOK { + get { + return ResourceManager.GetString("TextOK", resourceCulture); + } + } + /// /// Looks up a localized string similar to Archive parameters. /// @@ -1318,6 +1327,24 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Archive. + /// + public static string Type_archive { + get { + return ResourceManager.GetString("Type_archive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Audio. + /// + public static string Type_audio { + get { + return ResourceManager.GetString("Type_audio", resourceCulture); + } + } + /// /// Looks up a localized string similar to <DIR>. /// @@ -1328,7 +1355,16 @@ namespace GARbro.GUI.Strings { } /// - /// Looks up a localized string similar to none. + /// Looks up a localized string similar to Image. + /// + public static string Type_image { + get { + return ResourceManager.GetString("Type_image", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to NONE. /// public static string Type_NONE { get { @@ -1336,6 +1372,24 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Script. + /// + public static string Type_script { + get { + return ResourceManager.GetString("Type_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Video. + /// + public static string Type_video { + get { + return ResourceManager.GetString("Type_video", resourceCulture); + } + } + /// /// Looks up a localized string similar to Scale large images to fit window. /// diff --git a/GUI/Strings/guiStrings.ja-JP.resx b/GUI/Strings/guiStrings.ja-JP.resx index 4ff1dda7..c6e30bfa 100644 --- a/GUI/Strings/guiStrings.ja-JP.resx +++ b/GUI/Strings/guiStrings.ja-JP.resx @@ -560,7 +560,7 @@ Overwrite? Assign file type - none + NONE none @@ -697,4 +697,22 @@ Overwrite? Scale large images to fit window + + アーカイブ + + + オーディオ + + + 画像 + + + スクリプト + + + 確認 + + + ビデオ + \ No newline at end of file diff --git a/GUI/Strings/guiStrings.ko-KR.resx b/GUI/Strings/guiStrings.ko-KR.resx index df00a764..45b7ed68 100644 --- a/GUI/Strings/guiStrings.ko-KR.resx +++ b/GUI/Strings/guiStrings.ko-KR.resx @@ -388,7 +388,7 @@ 미디어 변환 - 파일 변환 + 파일 변환 {0} 멀티미디어 변환 오류 @@ -545,6 +545,12 @@ 환경설정 + + 이미지 뷰어 + + + 큰 이미지 파일을 창 크기에 맞춤 + 아카이브 @@ -557,10 +563,10 @@ 스크립트 - - 이미지 뷰어 + + OK - - 큰 이미지 파일을 창 크기에 맞춤 + + Video \ No newline at end of file diff --git a/GUI/Strings/guiStrings.resx b/GUI/Strings/guiStrings.resx index 484755bf..ee9f5567 100644 --- a/GUI/Strings/guiStrings.resx +++ b/GUI/Strings/guiStrings.resx @@ -447,7 +447,7 @@ Overwrite? Assign file type - none + NONE Destination directory @@ -551,4 +551,22 @@ Overwrite? Scale large images to fit window + + Archive + + + Audio + + + Image + + + Script + + + OK + + + Video + \ No newline at end of file diff --git a/GUI/Strings/guiStrings.ru-RU.resx b/GUI/Strings/guiStrings.ru-RU.resx index c19cdebd..3473f0f0 100644 --- a/GUI/Strings/guiStrings.ru-RU.resx +++ b/GUI/Strings/guiStrings.ru-RU.resx @@ -123,6 +123,9 @@ Извлечь + + OK + _Закрыть @@ -216,6 +219,9 @@ {0} извлечён в {1} + + Извлечён {0} файл + Извлекаются файлы из {0} @@ -270,6 +276,9 @@ Ошибка при создании архива + + <DIR> + Кодировка текста @@ -282,9 +291,6 @@ Извлечь из архива - - OK - Параметры архива @@ -315,15 +321,6 @@ Создание архива {0} - - архив - - - изображение - - - сценарий - Открыть... @@ -342,24 +339,15 @@ _Справка - - Извлечён {0} файл - Извлечено {0} файла - - Извлечено {0} файлов - {0} файл {0} файла - - {0} файлов - Вы действительно хотите удалить эти файлы? @@ -372,9 +360,6 @@ Удалено {0} файла - - Удалено {0} файлов - Подогнать размер окна под изображение @@ -426,21 +411,18 @@ Лицензия - - звук - не удалось интерпретировать формат аудио - - Пропускать файлы, не поддавшиеся конверсии. - Среди выбранных файлов нет мультимедиа. Аудио-файлы будут преобразованы в WAV, MP3 или OGG. + + Пропускать файлы, не поддавшиеся конверсии. + Нет файлов, подходящих под маску "{0}" @@ -450,9 +432,6 @@ {0} файла добавлено к выбранным - - {0} файлов добавлено к выбранным - Маска для имён файлов @@ -527,9 +506,6 @@ Проверить обновления... - - Не удалось обновить базу форматов. - Обновления недоступны. @@ -554,6 +530,9 @@ Перейти на страницу загрузки + + Не удалось обновить базу форматов. + Применить @@ -572,4 +551,34 @@ Масштабировать большие изображения + + архив + + + звук + + + изображение + + + сценарий + + + OK + + + Извлечено {0} файлов + + + {0} файлов + + + Удалено {0} файлов + + + {0} файлов добавлено к выбранным + + + Video + \ No newline at end of file diff --git a/GUI/Strings/guiStrings.zh-Hans.resx b/GUI/Strings/guiStrings.zh-Hans.resx index fd99d7d7..24e62f37 100644 --- a/GUI/Strings/guiStrings.zh-Hans.resx +++ b/GUI/Strings/guiStrings.zh-Hans.resx @@ -439,8 +439,7 @@ 选择文件 - 提取{0}时 - 发生错误。 + 提取{0}时发生错误。 {1} @@ -456,6 +455,9 @@ 正在播放 {0} / {3} / {2}bps / {1}Hz {0}=filename, {1}=sampling rate, {2}=bitrate, {3}=total time + + 项目主页 + 用代号选择文件…… @@ -503,9 +505,6 @@ 检查更新…… - - 更新下载失败。 - 无可用更新。 @@ -530,6 +529,9 @@ 访问下载页面 + + 更新下载失败。 + 应用 @@ -542,25 +544,28 @@ 偏好设置 + + 图像查看器 + + + 缩放大图以适应窗口 + 档案 音频 - - 视频 - 图像 脚本 - - 图像查看器 + + 确认 - - 缩放大图以适应窗口 + + 视频 - + \ No newline at end of file diff --git a/GUI/Strings/guiStrings.zh-Hant.resx b/GUI/Strings/guiStrings.zh-Hant.resx new file mode 100644 index 00000000..a7be2404 --- /dev/null +++ b/GUI/Strings/guiStrings.zh-Hant.resx @@ -0,0 +1,571 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 取消 + + + 提取 + + + 確定 + + + _關閉 + + + 複製 + + + 創建壓縮檔案... + + + 剪切 + + + _刪除 + + + 在_資源管理器中瀏覽 + + + 提取 + + + 打開 + + + 粘貼 + + + 刷新 + + + _重命名 + + + 排序方式 + + + 名稱 + + + 大小 + + + 類型 + + + 未分類 + + + 名稱 + + + 大小 + + + 類型 + + + 壓縮檔案格式 + + + 壓縮檔案名稱 + + + 壓縮選項 + + + 從{0}中提取檔案到 + + + 將{0}解壓到 + + + 關於遊戲資源瀏覽器(GARbro) + + + 已刪除{0} + + + 未找到資料夾。 + + + 壓縮包無內容。 + + + 提取檔案失敗。 + + + 打開檔案失敗。 + + + 已將{0}提取至{1}。 + + + 已提取檔案:{0}。 + + + 正在從{0}中提取檔案…… + + + 正在從{0}中提取檔案…… + + + 正在從{0}中提取檔案至{1}…… + + + 圖像 {0} x {1} x {2}bpp [{3}] + + + 沒有可以提取的檔案。 + + + 就緒 + + + 無法讀取圖像格式。 + + + 版本 {0} + + + [內置] + + + 壓縮檔案 + + + 圖像 + + + 關於遊戲資源瀏覽器(GARbro) + + + 全部檔案 + + + 保持原樣 + + + 選擇壓縮檔案位置 + + + 選擇目標資料夾 + + + 創建壓縮檔案 + + + 創建壓縮檔案失敗。 + + + <資料夾> + + + 文本編碼 + + + 提取圖像 + + + 提取文字 + + + 從壓縮檔案中提取 + + + 壓縮檔案參數 + + + 另存為 + + + 將圖像另存為 + + + 遊戲資源瀏覽器(GARbro) + + + 後退 + + + 前進 + + + 檔案{0} +已經存在。 + +是否覆蓋? + + + 確認覆蓋 + + + 正在創建壓縮檔案{0} + + + 打開... + + + 最近使用過的檔案 + + + 請選擇需要提取的檔案 + + + _退出 + + + _檔案 + + + _幫助 + + + 已提取{0}個檔案 + + + {0}個檔案 + + + {0}個檔案 + + + 您確定要刪除這些檔案嗎? + + + 刪除檔案 + + + 已刪除{0}個檔案。 + + + 已刪除{0}個檔案。 + + + 讓預覽視窗適應圖像 + + + 顯示/隱藏_主選單爛 + + + 顯示/隱藏_狀態欄 + + + 顯示/隱藏_工具欄 + + + _視圖 + + + 轉換多媒體檔案... + + + 轉換 + + + 請選擇目標檔案類型 + + + 媒體轉換 + + + 正在轉換檔案{0} + + + 轉換錯誤。 + + + 正在編碼 + + + 將音頻轉換為常規格式 + + + 提取音頻 + + + WAV,MP3或OGG + + + 音頻 + + + 許可證書 + + + 無法讀取音頻格式。 + + + 沒有選擇媒體檔案。 + + + 音頻檔案將被轉換為WAV,MP3或OGG格式。 + + + 跳過不可轉換的檔案 + + + 沒有與「{0}」匹配的項目。 + + + 選中{0}個檔案。 + + + 選中{0}個檔案。 + + + 請輸入通配符掩碼 + + + 選擇檔案 + + + 提取{0}時發生錯誤。 +{1} + + + 選擇檔案類型 + + + + + + 目標資料夾 + + + 正在播放 {0} / {3} / {2}bps / {1}Hz + {0}=filename, {1}=sampling rate, {2}=bitrate, {3}=total time + + + 項目主頁 + + + 用代號選擇檔案…… + + + 中止 + + + 繼續 + + + 忽略錯誤 + + + 檔案提取錯誤 + + + 覆蓋 + + + 重命名 + + + 跳過 + + + 應用於所有重複檔案 + + + 應該做什麼? + + + 轉換檔案時發生錯誤 +{0} +{1} + + + 檔案{0}已經存在。 + + + 下載 + + + 發行註記 + + + 檢查更新…… + + + 無可用更新。 + + + 有可用格式數據庫更新。 + + + 格式數據庫已更新。 + + + 更新檢查失敗。 + + + GARbro 已是最新版本。 + + + 新版本已可用: + + + 應用程序更新 + + + 訪問下載頁面 + + + 更新下載失敗。 + + + 應用 + + + 偏好設置 + + + 格式 + + + 偏好設置 + + + 圖像查看器 + + + 縮放大圖以適應視窗 + + + 檔案 + + + 音頻 + + + 圖像 + + + 腳本 + + + 確認 + + + 影片 + + \ No newline at end of file diff --git a/GUI/packages.config b/GUI/packages.config index e54fbbaf..3e3cc0f5 100644 --- a/GUI/packages.config +++ b/GUI/packages.config @@ -1,7 +1,15 @@  - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/GameRes/ByteArray.cs b/GameRes/ByteArray.cs index 0b89268c..26abebb8 100644 --- a/GameRes/ByteArray.cs +++ b/GameRes/ByteArray.cs @@ -26,6 +26,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using GameRes.Utility; namespace GameRes @@ -142,6 +143,74 @@ namespace GameRes } public static class ByteArrayExt + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ToUInt16 (this byte[] arr, int index) + { + return (ushort)(arr[index] | arr[index+1] << 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short ToInt16 (this byte[] arr, int index) + { + return (short)(arr[index] | arr[index+1] << 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToInt24 (this byte[] arr, int index) + { + return arr[index] | arr[index+1] << 8 | arr[index+2] << 16; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ToUInt32 (this byte[] arr, int index) + { + return (uint)(arr[index] | arr[index+1] << 8 | arr[index+2] << 16 | arr[index+3] << 24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToInt32 (this byte[] arr, int index) + { + return (int)ToUInt32 (arr, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong ToUInt64 (this byte[] arr, int index) + { + return (ulong)ToUInt32 (arr, index) | ((ulong)ToUInt32 (arr, index+4) << 32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long ToInt64 (this byte[] arr, int index) + { + return (long)ToUInt64 (arr, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AsciiEqual (this byte[] arr, int index, string str) + { + if (arr.Length-index < str.Length) + return false; + for (int i = 0; i < str.Length; ++i) + if ((char)arr[index+i] != str[i]) + return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AsciiEqual (this byte[] arr, string str) + { + return arr.AsciiEqual (0, str); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetCString (this byte[] arr, int index, int length_limit) + { + return Binary.GetCString (arr, index, length_limit); + } + } + + public static class CowArrayExt { public static ushort ToUInt16 (this TArray arr, int index) where TArray : IList { diff --git a/GameRes/FormatCatalog.cs b/GameRes/FormatCatalog.cs index 6b6f0335..06d06721 100644 --- a/GameRes/FormatCatalog.cs +++ b/GameRes/FormatCatalog.cs @@ -364,7 +364,7 @@ namespace GameRes { var scheme = format.Scheme; if (null != scheme) - db.SchemeMap[format.Tag] = scheme; + db.SchemeMap.Add (format.Tag, scheme); } SerializeScheme (output, db); } diff --git a/GameRes/GameRes.csproj b/GameRes/GameRes.csproj index 90d24440..f5f212fe 100644 --- a/GameRes/GameRes.csproj +++ b/GameRes/GameRes.csproj @@ -9,7 +9,7 @@ Properties GameRes GameRes - v4.6.1 + v4.7.2 512 ..\ @@ -46,17 +46,57 @@ MinimumRecommendedRules.ruleset - - ..\packages\NAudio.1.10.0\lib\net35\NAudio.dll + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll + + + ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll + + + ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll + + + ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll + + + ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll + + + ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll + + + ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll + + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + @@ -117,6 +157,7 @@ + @@ -131,11 +172,4 @@ perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName) exit 0 - \ No newline at end of file diff --git a/GameRes/Properties/Settings.Designer.cs b/GameRes/Properties/Settings.Designer.cs index 03225883..46fe0780 100644 --- a/GameRes/Properties/Settings.Designer.cs +++ b/GameRes/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace GameRes.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/GameRes/Strings/garStrings.zh-Hant.resx b/GameRes/Strings/garStrings.zh-Hant.resx new file mode 100644 index 00000000..19182edc --- /dev/null +++ b/GameRes/Strings/garStrings.zh-Hant.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 啟用各種擴展,如透明度支持 + + + 啟用 BMP 格式擴展 + + + JPEG 壓縮品質 + + + 檔案為空。 + + + 檔案尺寸太大。 + + + 不正確的加密方式。 + + + 無效的檔案名。 + + + 無效的檔案格式。 + + + 未知的加密方式。 + + + 無法作為壓縮包打開檔案。 + + \ No newline at end of file diff --git a/GameRes/Utility.cs b/GameRes/Utility.cs index 2310f934..e15da871 100644 --- a/GameRes/Utility.cs +++ b/GameRes/Utility.cs @@ -24,33 +24,45 @@ // using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Text; namespace GameRes.Utility { public static class Binary { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint BigEndian (uint u) { return u << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BigEndian (int i) { return (int)BigEndian ((uint)i); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort BigEndian (ushort u) { return (ushort)(u << 8 | u >> 8); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short BigEndian (short i) { return (short)BigEndian ((ushort)i); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong BigEndian (ulong u) { return (ulong)BigEndian((uint)(u & 0xffffffff)) << 32 | (ulong)BigEndian((uint)(u >> 32)); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long BigEndian (long i) { return (long)BigEndian ((ulong)i); @@ -123,36 +135,42 @@ namespace GameRes.Utility return GetCString (data, index, data.Length - index, Encodings.cp932); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotR (uint v, int count) { count &= 0x1F; return v >> count | v << (32-count); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotL (uint v, int count) { count &= 0x1F; return v << count | v >> (32-count); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotR (ulong v, int count) { count &= 0x3F; return v >> count | v << (64-count); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotL (ulong v, int count) { count &= 0x3F; return v << count | v >> (64-count); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte RotByteR (byte v, int count) { count &= 7; return (byte)(v >> count | v << (8-count)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte RotByteL (byte v, int count) { count &= 7; @@ -162,32 +180,62 @@ namespace GameRes.Utility public static class BigEndian { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ToUInt16 (TArray value, int index) where TArray : IList { return (ushort)(value[index] << 8 | value[index+1]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ToInt16 (TArray value, int index) where TArray : IList { return (short)(value[index] << 8 | value[index+1]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ToUInt32 (TArray value, int index) where TArray : IList { return (uint)(value[index] << 24 | value[index+1] << 16 | value[index+2] << 8 | value[index+3]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToInt32 (TArray value, int index) where TArray : IList { return (int)ToUInt32 (value, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ToUInt16 (byte[] value, int index) + { + return (ushort)(value[index] << 8 | value[index+1]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short ToInt16 (byte[] value, int index) + { + return (short)(value[index] << 8 | value[index+1]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ToUInt32 (byte[] value, int index) + { + return (uint)(value[index] << 24 | value[index+1] << 16 | value[index+2] << 8 | value[index+3]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToInt32 (byte[] value, int index) + { + return (int)ToUInt32 (value, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (ushort value, byte[] buf, int index) { buf[index] = (byte)(value >> 8); buf[index+1] = (byte)(value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (uint value, byte[] buf, int index) { buf[index] = (byte)(value >> 24); @@ -196,22 +244,26 @@ namespace GameRes.Utility buf[index+3] = (byte)(value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (ulong value, byte[] buf, int index) { Pack ((uint)(value >> 32), buf, index); Pack ((uint)value, buf, index+4); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (short value, byte[] buf, int index) { Pack ((ushort)value, buf, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (int value, byte[] buf, int index) { Pack ((uint)value, buf, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (long value, byte[] buf, int index) { Pack ((ulong)value, buf, index); @@ -220,42 +272,86 @@ namespace GameRes.Utility public static class LittleEndian { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ToUInt16 (TArray value, int index) where TArray : IList { return (ushort)(value[index] | value[index+1] << 8); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ToInt16 (TArray value, int index) where TArray : IList { return (short)(value[index] | value[index+1] << 8); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ToUInt32 (TArray value, int index) where TArray : IList { return (uint)(value[index] | value[index+1] << 8 | value[index+2] << 16 | value[index+3] << 24); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToInt32 (TArray value, int index) where TArray : IList { return (int)ToUInt32 (value, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong ToUInt64 (TArray value, int index) where TArray : IList { return (ulong)ToUInt32 (value, index) | ((ulong)ToUInt32 (value, index+4) << 32); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ToInt64 (TArray value, int index) where TArray : IList { return (long)ToUInt64 (value, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ToUInt16 (byte[] value, int index) + { + return (ushort)(value[index] | value[index+1] << 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short ToInt16 (byte[] value, int index) + { + return (short)(value[index] | value[index+1] << 8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ToUInt32 (byte[] value, int index) + { + return (uint)(value[index] | value[index+1] << 8 | value[index+2] << 16 | value[index+3] << 24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToInt32 (byte[] value, int index) + { + return (int)ToUInt32 (value, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong ToUInt64 (byte[] value, int index) + { + return (ulong)ToUInt32 (value, index) | ((ulong)ToUInt32 (value, index+4) << 32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long ToInt64 (byte[] value, int index) + { + return (long)ToUInt64 (value, index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (ushort value, byte[] buf, int index) { buf[index] = (byte)(value); buf[index+1] = (byte)(value >> 8); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (uint value, byte[] buf, int index) { buf[index] = (byte)(value); @@ -264,22 +360,26 @@ namespace GameRes.Utility buf[index+3] = (byte)(value >> 24); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (ulong value, byte[] buf, int index) { Pack ((uint)value, buf, index); Pack ((uint)(value >> 32), buf, index+4); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (short value, byte[] buf, int index) { Pack ((ushort)value, buf, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (int value, byte[] buf, int index) { Pack ((uint)value, buf, index); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pack (long value, byte[] buf, int index) { Pack ((ulong)value, buf, index); diff --git a/GameRes/app.config b/GameRes/app.config index 7c7361cd..8df6e0fc 100644 --- a/GameRes/app.config +++ b/GameRes/app.config @@ -1,4 +1,4 @@ - + @@ -18,4 +18,43 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GameRes/packages.config b/GameRes/packages.config index 07609041..e4bf58c9 100644 --- a/GameRes/packages.config +++ b/GameRes/packages.config @@ -1,5 +1,18 @@  - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Image.Convert/App.config b/Image.Convert/App.config index 45437954..9b3a103e 100644 --- a/Image.Convert/App.config +++ b/Image.Convert/App.config @@ -1,6 +1,42 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Image.Convert/Image.Convert.csproj b/Image.Convert/Image.Convert.csproj index 644fc445..aa71b947 100644 --- a/Image.Convert/Image.Convert.csproj +++ b/Image.Convert/Image.Convert.csproj @@ -9,7 +9,7 @@ Properties Image.Convert Image.Convert - v4.6.1 + v4.7.2 512 @@ -26,7 +26,7 @@ AnyCPU - pdbonly + none true ..\bin\Release\ TRACE @@ -69,11 +69,4 @@ - \ No newline at end of file diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj index e360bb32..9cfcd323 100644 --- a/Legacy/Legacy.csproj +++ b/Legacy/Legacy.csproj @@ -9,7 +9,7 @@ Properties GameRes.Legacy ArcLegacy - v4.6.1 + v4.7.2 512 @@ -24,7 +24,7 @@ true - pdbonly + none true ..\bin\Release\ @@ -44,14 +44,30 @@ true - - ..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll + + ..\packages\SharpZipLib.1.4.2\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll + + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll + @@ -365,11 +381,4 @@ perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName) exit 0 - \ No newline at end of file diff --git a/Legacy/app.config b/Legacy/app.config index 741af0df..f60fb278 100644 --- a/Legacy/app.config +++ b/Legacy/app.config @@ -1,15 +1,42 @@  - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Legacy/packages.config b/Legacy/packages.config index 8803282e..7e3839a4 100644 --- a/Legacy/packages.config +++ b/Legacy/packages.config @@ -1,4 +1,9 @@  - + + + + + + \ No newline at end of file diff --git a/Net20/Net20.csproj b/Net20/Net20.csproj index 7191d81b..76c921d8 100644 --- a/Net20/Net20.csproj +++ b/Net20/Net20.csproj @@ -24,7 +24,7 @@ 301989888 - pdbonly + none true ..\bin\Release\ diff --git a/README.md b/README.md index feb94ad5..69951819 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ GARbro Visual Novels resource browser. -Requires .NET Framework v4.6 or newer (https://www.microsoft.com/net) +Requires .NET Framework v4.7.2 or newer (https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472) [Supported formats](https://morkt.github.io/GARbro/supported.html) -[Download latest release](https://github.com/morkt/GARbro/releases) +[Download latest release](https://github.com/crskycode/GARbro/releases) Operation --------- @@ -69,6 +69,6 @@ Japanese translation by [haniwa55](https://github.com/haniwa55) Contributors ------ - - - \ No newline at end of file + + + diff --git a/SchemeTool/App.config b/SchemeTool/App.config index 18d3b8dd..f60fb278 100644 --- a/SchemeTool/App.config +++ b/SchemeTool/App.config @@ -1,22 +1,42 @@  - - + + - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SchemeTool/SchemeTool.csproj b/SchemeTool/SchemeTool.csproj index 40d4c469..dd3fb366 100644 --- a/SchemeTool/SchemeTool.csproj +++ b/SchemeTool/SchemeTool.csproj @@ -8,10 +8,11 @@ Exe SchemeTool SchemeTool - v4.6.1 + v4.7.2 512 true true + AnyCPU @@ -25,7 +26,7 @@ AnyCPU - pdbonly + none true ..\bin\Release\ TRACE @@ -34,19 +35,19 @@ - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll - - ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll diff --git a/SchemeTool/packages.config b/SchemeTool/packages.config index 5324bbbe..1af333b8 100644 --- a/SchemeTool/packages.config +++ b/SchemeTool/packages.config @@ -1,7 +1,7 @@  - - - - + + + + \ No newline at end of file