From e17b876116dce7e7b4add2f139effabaee850171 Mon Sep 17 00:00:00 2001 From: Daoluong Goro Date: Thu, 23 Jul 2020 19:25:45 +0700 Subject: [PATCH] add code from pull requests #388 #394 of original fork --- ArcFormats/Circus/ImageCRX.cs | 293 ++++++++- ArcFormats/Properties/AssemblyInfo.cs | 6 +- ArcFormats/Strings/arcStrings.Designer.cs | 2 +- Console/ConsoleBrowser.cs | 690 ++++++++++++++++------ Console/GARbro.Console.Core.csproj | 3 +- Console/README.md | 32 +- Experimental/Experimental.Core.csproj | 2 +- Experimental/Properties/AssemblyInfo.cs | 6 +- GARbro.sln | 20 - GUI/AutoComplete.cs | 4 +- GUI/GARbro.GUI.Core.csproj | 9 +- GUI/Properties/AssemblyInfo.cs | 6 +- GameRes/Properties/AssemblyInfo.cs | 6 +- Legacy/Properties/AssemblyInfo.cs | 6 +- inc-revision.csx | 14 +- 15 files changed, 856 insertions(+), 243 deletions(-) diff --git a/ArcFormats/Circus/ImageCRX.cs b/ArcFormats/Circus/ImageCRX.cs index b204f154..d3e6fcb9 100644 --- a/ArcFormats/Circus/ImageCRX.cs +++ b/ArcFormats/Circus/ImageCRX.cs @@ -26,6 +26,7 @@ using System; using System.ComponentModel.Composition; using System.IO; +using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using GameRes.Compression; @@ -47,6 +48,7 @@ public class CrxFormat : ImageFormat public override string Tag { get { return "CRX"; } } public override string Description { get { return "Circus image format"; } } public override uint Signature { get { return 0x47585243; } } // 'CRXG' + public override bool CanWrite { get { return true; } } public override ImageMetaData ReadMetaData (IBinaryStream stream) { @@ -81,7 +83,77 @@ public override ImageData Read (IBinaryStream stream, ImageMetaData info) public override void Write (Stream file, ImageData image) { - throw new NotImplementedException ("CrxFormat.Write not implemented"); + //throw new NotImplementedException ("CrxFormat.Write not implemented"); + var header = new byte[0x14]; + + var depth = (short)(24 == image.BPP ? 0 : 32 == image.BPP ? 1 : 2); + var compression = (ushort)3; + var flags = (ushort)17; + var mode = (ushort)0; + + using (var memeStream = new MemoryStream(header)) + { + using (var binaryWriter = new BinaryWriter(memeStream)) + { + binaryWriter.Write(Signature); + binaryWriter.Write((ushort)image.OffsetX); + binaryWriter.Write((ushort)image.OffsetY); + binaryWriter.Write((ushort)image.Width); + binaryWriter.Write((ushort)image.Height); + binaryWriter.Write(compression); + binaryWriter.Write(flags); + binaryWriter.Write(depth); + binaryWriter.Write(mode); + } + } + + var metaData = ReadMetaData(BinaryStream.FromArray(header, "")); + + var bitmap = image.Bitmap; + var pixelFormat = CheckFormat(image.BPP); + + int stride = (int)(image.Width * pixelFormat.BitsPerPixel / 8 + 3) & ~3; + + if (pixelFormat != bitmap.Format) + { + var converted_bitmap = new FormatConvertedBitmap(); + converted_bitmap.BeginInit(); + converted_bitmap.Source = image.Bitmap; + converted_bitmap.DestinationFormat = pixelFormat; + converted_bitmap.EndInit(); + bitmap = converted_bitmap; + } + + var data = new byte[image.Height * stride]; + var row_data = new byte[stride]; + var rect = new Int32Rect(0, 0, (int)image.Width, 1); + + for (uint row = 0; row < image.Height; ++row) + { + bitmap.CopyPixels(rect, row_data, stride, 0); + rect.Y++; + row_data.CopyTo(data, row * stride); + } + + using (var binaryWriter = new BinaryWriter(file)) + { + binaryWriter.Write(header); + using (var writer = new Writer(data, + binaryWriter, (CrxMetaData)metaData)) + { + writer.Write(); + } + } + } + private static PixelFormat CheckFormat(int bpp) + { + switch (bpp) + { + case 24: return PixelFormats.Bgr24; + case 32: return PixelFormats.Bgra32; + case 8: return PixelFormats.Indexed8; + default: throw new InvalidFormatException(); + } } internal sealed class Reader : IDisposable @@ -116,6 +188,7 @@ public Reader (IBinaryStream input, CrxMetaData info) case 8: Format = PixelFormats.Indexed8; break; default: throw new InvalidFormatException(); } + Format = CheckFormat(m_bpp); m_stride = (m_width * m_bpp / 8 + 3) & ~3; m_output = new byte[m_height*m_stride]; m_input = input; @@ -335,6 +408,224 @@ public void Dispose () { } #endregion + + } + + internal sealed class Writer : IDisposable + { + BinaryWriter m_output; + byte[] m_input; + int m_width; + int m_height; + int m_stride; + int m_bpp; + int m_compression; + int m_flags; + int m_mode; + + public byte[] Data { get { return m_input; } } + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + public int Stride { get { return m_stride; } } + + public Writer(byte[] input, + BinaryWriter output, + CrxMetaData info) + { + m_width = (int)info.Width; + m_height = (int)info.Height; + m_bpp = info.BPP; + m_compression = info.Compression; + m_flags = info.CompressionFlags; + m_mode = info.Mode; + Format = CheckFormat(m_bpp); + m_stride = (m_width * m_bpp / 8 + 3) & ~3; + + m_input = input; + m_output = output; + + m_output.Seek(0x14, SeekOrigin.Begin); + + if (8 == m_bpp) + ReadPalette(info.Colors); + } + + private void ReadPalette(int colors) + { + throw new NotImplementedException("CrxFormat.Write 8bit bpp not implemented"); + } + + + public void Write(bool isDiff = false) + { + int compressed_size_position = 40; + + if (m_compression >= 3) + { + var count = 1; + m_output.Write(count); + m_output.Seek(count * 0x10, SeekOrigin.Current); + + } + if (0 != (m_flags & 0x10)) + { + compressed_size_position = (int)m_output.BaseStream.Position + 4; + m_output.Seek(compressed_size_position, SeekOrigin.Begin); + } + + if (32 == m_bpp && m_mode != 1) + { + int alpha_flip = 2 == m_mode ? 0 : 0xFF; + int line = 0; + for (int h = 0; h < m_height; h++) + { + for (int w = 0; w < m_width; w++) + { + int pixel = line + w * 4; + + var b = m_input[pixel]; + var g = m_input[pixel + 1]; + var r = m_input[pixel + 2]; + var alpha = m_input[pixel + 3]; + + m_input[pixel] = (byte)(alpha ^ alpha_flip); + m_input[pixel + 1] = b; + m_input[pixel + 2] = g; + m_input[pixel + 3] = r; + + } + line += m_stride; + } + } + + if (1 == m_compression) + WriteV1(); + else + WriteV2(); + + m_output.Seek(compressed_size_position - 4, SeekOrigin.Begin); + var compressed_size = (int)m_output.BaseStream.Length; // compressed_size + m_output.Write(compressed_size); + } + + private void WriteV2() + { + int pixel_size = m_bpp / 8; + int src_stride = m_width * pixel_size; + m_output.Flush(); + + using (var zlib = new ZLibStream(m_output.BaseStream, CompressionMode.Compress, true)) + using (var output = new BinaryWriter(zlib)) + { + + if (m_bpp >= 24) + { + for (int y = 0; y < m_height; ++y) + { + + byte ctl = 0; + output.Write(ctl); + + int dst = y * m_stride; + int prev_row = dst - m_stride; + switch (ctl) + { + case 0: + output.Write(m_input, dst, pixel_size); + for (int x = pixel_size; x < src_stride; ++x) + { + output.Write((byte)(m_input[dst + x] - m_input[dst + x - pixel_size])); + } + break; + case 1: + for (int x = 0; x < src_stride; ++x) + { + output.Write((byte)(m_input[dst + x] - m_input[prev_row + x])); + } + break; + case 2: + output.Write(m_input, dst, pixel_size); + + for (int x = pixel_size; x < src_stride; ++x) + { + output.Write((byte)(m_input[dst + x] - m_input[prev_row + x - pixel_size])); + } + break; + case 3: + for (int x = src_stride - pixel_size; x > 0; --x) + { + output.Write((byte)(m_input[dst++] - m_input[prev_row++ + pixel_size])); + } + output.Write(m_input, dst, pixel_size); + break; + case 4: + for (int i = 0; i < pixel_size; ++i) + { + int w = m_width; + byte val = m_input[dst]; + output.Write(val); + while (w > 0) + { + dst += pixel_size; + if (0 == --w) + break; + + byte next = m_input[dst]; + output.Write(next); + + if (val == next) + { + var count = 255; + + output.Write(count); + + dst += pixel_size * count; + w -= count; + + if (w > 0) + { + val = m_input[dst]; + output.Write(val); + } + + } + else + { + val = next; + } + + } + dst -= src_stride - 1; + } + break; + default: + break; + } + } + } + else + { + int dst = 0; + for (int y = 0; y < m_height; ++y) + { + m_output.Write(m_input, dst, src_stride); + dst += m_stride; + } + } + } + + } + + private void WriteV1() + { + throw new NotImplementedException("CrxFormat.Write version 1 not implemented"); + } + + #region IDisposable Members + public void Dispose() + { + } + #endregion } } } diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs index df1ffa68..9922ad20 100644 --- a/ArcFormats/Properties/AssemblyInfo.cs +++ b/ArcFormats/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.2.48.2153")] -[assembly: AssemblyFileVersion ("1.2.48.2153")] +[assembly: AssemblyVersion ("1.2.48.2178")] +[assembly: AssemblyFileVersion ("1.2.48.2178")] diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs index c362541b..223154b9 100644 --- a/ArcFormats/Strings/arcStrings.Designer.cs +++ b/ArcFormats/Strings/arcStrings.Designer.cs @@ -39,7 +39,7 @@ internal arcStrings() { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ArcFormats.Strings.arcStrings", typeof(arcStrings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GameRes.Formats.Strings.arcStrings", typeof(arcStrings).Assembly); resourceMan = temp; } return resourceMan; diff --git a/Console/ConsoleBrowser.cs b/Console/ConsoleBrowser.cs index b5fd3691..1772a5b2 100644 --- a/Console/ConsoleBrowser.cs +++ b/Console/ConsoleBrowser.cs @@ -1,202 +1,504 @@ -//! \file Program.cs -//! \date Mon Jun 30 20:12:13 2014 -//! \brief game resources browser. -// - -using System; +using System; +using System.Collections.Generic; using System.IO; -using System.IO.MemoryMappedFiles; using System.Text; using System.Linq; -using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Media.Imaging; using GameRes; +// ReSharper disable LocalizableElement namespace GARbro { - class ConsoleBrowser - { - private string m_arc_name; - private ImageFormat m_image_format; - private bool m_extract_all; - - void ListFormats () - { - Console.WriteLine ("Recognized resource formats:"); - foreach (var impl in FormatCatalog.Instance.ArcFormats) - { - Console.WriteLine ("{0,-4} {1}", impl.Tag, impl.Description); - } - } - - void ExtractAll (ArcFile arc) - { - arc.ExtractFiles ((i, entry, msg) => { - if (null != entry) - { - Console.WriteLine ("Extracting {0} ...", entry.Name); - } - else if (null != msg) - { - Console.WriteLine (msg); - } - return ArchiveOperation.Continue; - }); - } - - void ExtractFile (ArcFile arc, string name) - { - Entry entry = arc.Dir.FirstOrDefault (e => e.Name.Equals (name, StringComparison.OrdinalIgnoreCase)); - if (null == entry) - { - Console.Error.WriteLine ("'{0}' not found within {1}", name, m_arc_name); - return; - } - Console.WriteLine ("Extracting {0} ...", entry.Name); - arc.Extract (entry); - } - - void TestArc (string[] args) - { -/* - if (args.Length > 1) - { - uint pass = GameRes.Formats.IntOpener.EncodePassPhrase (args[1]); - Console.WriteLine ("{0:X8}", pass); - } -*/ - } - ImageFormat FindFormat(string format) - { - var range = FormatCatalog.Instance.LookupExtension(format); - return range.FirstOrDefault(); - } - - void Run (string[] args) - { - int argn = 0; - while (argn < args.Length) - { - if (args[argn].Equals ("-l")) - { - ListFormats(); - return; - } - else if (args[argn].Equals ("-t")) - { - TestArc (args); - return; - } - else if (args[argn].Equals ("-c")) - { - if (argn+1 >= args.Length) - { - Usage(); - return; - } - var tag = args[argn+1]; - m_image_format = FindFormat (tag); - if (null == m_image_format) - { - Console.Error.WriteLine ("{0}: unknown format specified", tag); - return; - } - argn += 2; - } - else if (args[argn].Equals ("-x")) - { - m_extract_all = true; - ++argn; - if (args.Length <= argn) - { - Usage(); - return; - } - } - else - { - break; - } - } - if (argn >= args.Length) - { - Usage(); - return; - } - DeserializeGameData(); - foreach (var file in VFS.GetFiles (args[argn])) - { - m_arc_name = file.Name; - try - { - VFS.ChDir (m_arc_name); - } - catch (Exception X) - { - Console.Error.WriteLine ("{0}: unknown format", m_arc_name); - continue; - } - var arc = (ArchiveFileSystem)VFS.Top; - if (args.Length > argn+1) - { - for (int i = argn+1; i < args.Length; ++i) - ExtractFile (arc.Source, args[i]); - } - else if (m_extract_all) - { - ExtractAll (arc.Source); - } - else - { - foreach (var entry in arc.Source.Dir.OrderBy (e => e.Offset)) - { - Console.WriteLine ("{0,9} [{2:X8}] {1}", entry.Size, entry.Name, entry.Offset); - } - } - } - } - - void DeserializeGameData () - { - string scheme_file = Path.Combine (FormatCatalog.Instance.DataDirectory, "Formats.dat"); - try - { - using (var file = File.OpenRead (scheme_file)) - FormatCatalog.Instance.DeserializeScheme (file); - } - catch (Exception X) - { - Console.Error.WriteLine ("Scheme deserialization failed: {0}", X.Message); - } - } - - static void Usage () - { - Console.WriteLine ("Usage: gameres [OPTIONS] ARC [ENTRIES]"); - Console.WriteLine (" -l list recognized archive formats"); - Console.WriteLine (" -x extract all files"); - Console.WriteLine ("Without options displays contents of specified archive."); - } - - static void Main (string[] args) - { - Console.OutputEncoding = Encoding.UTF8; - if (0 == args.Length) - { - Usage(); - return; - } - var listener = new TextWriterTraceListener (Console.Error); - Trace.Listeners.Add(listener); - try - { - var browser = new ConsoleBrowser(); - browser.Run (args); - } - catch (Exception X) - { - Console.Error.WriteLine (X.Message); - } - } - } -} + public enum ExistingFileAction + { + Ask, + Skip, + Overwrite, + Rename + } + + class ConsoleBrowser + { + private string outputDirectory; + + private Regex fileFilter; + private ImageFormat imageFormat; + private bool autoImageFormat = false; + private bool ignoreErrors = true; + private bool skipImages; + private bool skipScript; + private bool skipAudio; + private bool convertAudio; + private bool adjustImageOffset; + + private ExistingFileAction existingFileAction = ExistingFileAction.Ask; + + public static readonly HashSet CommonAudioFormats = new HashSet { "wav", "mp3", "ogg" }; + public static readonly HashSet CommonImageFormats = new HashSet { "jpeg", "png", "bmp", "tga" }; + + private void ListFormats() + { + Console.WriteLine("Recognized resource formats:\n"); + foreach (var format in FormatCatalog.Instance.ArcFormats.OrderBy(format => format.Tag)) + { + Console.WriteLine("{0,-20} {1}", format.Tag, format.Description); + } + } + + private void ListFiles(Entry[] fileList) + { + Console.WriteLine(" Offset Size Name"); + Console.WriteLine(" ---------- -------- ------------------------------------------------------"); + foreach (var entry in fileList) + { + Console.WriteLine(" [{1:X8}] {0,9} {2}", entry.Offset, entry.Size, entry.Name); + } + + Console.WriteLine(" ---------- -------- ------------------------------------------------------"); + Console.WriteLine($" {fileList.Length} files"); + } + + private void ExtractFiles(Entry[] fileList, ArcFile arc) + { + Directory.CreateDirectory(outputDirectory); + + var iSkipped = 0; + for (var i = 0; i < fileList.Length; i++) + { + var entry = fileList[i]; + Console.WriteLine(string.Format("[{0}/{1}] {2}", i + 1, fileList.Length, entry.Name)); + + try + { + if (imageFormat != null && entry.Type == "image") + { + ExtractImage(arc, entry, imageFormat); + } + else if (convertAudio && entry.Type == "audio") + { + ExtractAudio(arc, entry); + } + else + { + using (var input = arc.OpenEntry(entry)) + using (var output = CreateNewFile(entry.Name)) + input.CopyTo(output); + } + } + catch (TargetException) + { + iSkipped++; + } +#if !DEBUG + catch (Exception e) { + PrintError(string.Format($"Failed to extract {entry.Name}: {e.Message}")); + if (!ignoreErrors) return; + + iSkipped++; + } +#endif + } + + Console.WriteLine(); + Console.WriteLine(iSkipped > 0 ? iSkipped + " files were skipped" : "All OK"); + } + + void ExtractImage(ArcFile arc, Entry entry, ImageFormat targetFormat) + { + using (var decoder = arc.OpenImage(entry)) + { + var src_format = decoder.SourceFormat; // could be null + + if (autoImageFormat && src_format != null) targetFormat = CommonImageFormats.Contains(src_format.Tag.ToLower()) ? src_format : ImageFormat.Png; + + var target_ext = targetFormat.Extensions.FirstOrDefault() ?? ""; + var outputName = Path.ChangeExtension(entry.Name, target_ext); + if (src_format == targetFormat) + { + // source format is the same as a target, copy file as is + using (var output = CreateNewFile(outputName)) decoder.Source.CopyTo(output); + return; + } + + var image = decoder.Image; + if (adjustImageOffset) image = AdjustImageOffset(image); + + using (var outfile = CreateNewFile(outputName)) + { + targetFormat.Write(outfile, image); + } + } + } + + static ImageData AdjustImageOffset(ImageData image) + { + if (0 == image.OffsetX && 0 == image.OffsetY) return image; + var width = (int)image.Width + image.OffsetX; + var height = (int)image.Height + image.OffsetY; + if (width <= 0 || height <= 0) return image; + + var x = Math.Max(image.OffsetX, 0); + var y = Math.Max(image.OffsetY, 0); + var src_x = image.OffsetX < 0 ? Math.Abs(image.OffsetX) : 0; + var src_y = image.OffsetY < 0 ? Math.Abs(image.OffsetY) : 0; + var src_stride = (int)image.Width * (image.BPP + 7) / 8; + var dst_stride = width * (image.BPP + 7) / 8; + var pixels = new byte[height * dst_stride]; + var offset = y * dst_stride + x * image.BPP / 8; + var rect = new Int32Rect(src_x, src_y, (int)image.Width - src_x, 1); + for (var row = src_y; row < image.Height; ++row) + { + rect.Y = row; + image.Bitmap.CopyPixels(rect, pixels, src_stride, offset); + offset += dst_stride; + } + + var bitmap = BitmapSource.Create(width, height, image.Bitmap.DpiX, image.Bitmap.DpiY, + image.Bitmap.Format, image.Bitmap.Palette, pixels, dst_stride); + return new ImageData(bitmap); + } + + void ExtractAudio(ArcFile arc, Entry entry) + { + using (var file = arc.OpenBinaryEntry(entry)) + using (var sound = AudioFormat.Read(file)) + { + if (sound == null) throw new InvalidFormatException("Unable to interpret audio format"); + ConvertAudio(entry.Name, sound); + } + } + + public void ConvertAudio(string filename, SoundInput input) + { + var source_format = input.SourceFormat; + if (CommonAudioFormats.Contains(source_format)) + { + var output_name = Path.ChangeExtension(filename, source_format); + using (var output = CreateNewFile(output_name)) + { + input.Source.Position = 0; + input.Source.CopyTo(output); + } + } + else + { + var output_name = Path.ChangeExtension(filename, "wav"); + using (var output = CreateNewFile(output_name)) AudioFormat.Wav.Write(input, output); + } + } + + protected Stream CreateNewFile(string filename) + { + var path = Path.Combine(outputDirectory, filename); + path = Path.GetFullPath(path); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + if (File.Exists(path)) + { + path = OverwritePrompt(path); + if (path == null) throw new TargetException(); + } + + return File.Open(path, FileMode.Create); + } + + void Run(string[] args) + { + var command = args.Length < 1 ? "h" : args[0]; + + switch (command) + { + case "h": + case "-h": + case "--help": + case "/?": + case "-?": + Usage(); + return; + case "f": + ListFormats(); + return; + } + + if (command.Length != 1) + { + PrintError(File.Exists(command) ? "No command specified. Use -h command line parameter to show help." : "Invalid command: " + command); + return; + } + if (args.Length < 2) + { + PrintError("No archive file specified"); + return; + } + + var inputFile = args[args.Length - 1]; + if (!File.Exists(inputFile)) + { + PrintError("Input file " + inputFile + " does not exist"); + return; + } + + var argLength = args.Length - 1; + outputDirectory = Directory.GetCurrentDirectory(); + for (var i = 1; i < argLength; i++) + { + switch (args[i]) + { + case "-o": + i++; + if (i >= argLength) + { + PrintError("No output directory specified"); + return; + } + outputDirectory = args[i]; + if (File.Exists(outputDirectory)) + { + PrintError("Invalid output directory"); + return; + } + + //Directory.SetCurrentDirectory(outputDirectory); + break; + case "-f": + i++; + if (i >= argLength) + { + PrintError("No filter specified"); + return; + } + + try + { + fileFilter = new Regex(args[i]); + } + catch (ArgumentException e) + { + PrintError("Invalid filter: " + e.Message); + return; + } + + break; + case "-if": + i++; + var formatTag = args[i].ToUpper(); + if (formatTag == "JPG") formatTag = "JPEG"; + + imageFormat = ImageFormat.FindByTag(formatTag); + if (imageFormat == null) + { + PrintError("Unknown image format specified: " + args[i]); + return; + } + break; + case "-ca": + convertAudio = true; + break; + case "-na": + skipAudio = true; + break; + case "-ni": + skipImages = true; + break; + case "-ns": + skipScript = true; + break; + case "-aio": + adjustImageOffset = true; + break; + case "-ocu": + autoImageFormat = true; + break; + default: + Console.WriteLine("Warning: Unknown command line parameter: " + args[i]); + return; + } + } + + if (autoImageFormat && imageFormat == null) + { + PrintError("The parameter -ocu requires the image format (-if parameter) to be set"); + return; + } + + DeserializeGameData(); + + try + { + VFS.ChDir(inputFile); + } + catch (Exception) + { + PrintError("Input file has an unknown format"); + return; + } + + var m_fs = (ArchiveFileSystem)VFS.Top; + var fileList = m_fs.GetFilesRecursive().Where(e => e.Offset >= 0); + + if (skipImages || skipScript || skipAudio || fileFilter != null) + { + fileList = fileList.Where(f => !(skipImages && f.Type == "image") && + !(skipScript && f.Type == "script") && + !(skipAudio && f.Type == "audio") && + (fileFilter == null || fileFilter.IsMatch(f.Name))); + } + + if (!fileList.Any()) + { + var hasFilter = skipAudio || skipImages || skipScript || fileFilter != null; + PrintError(hasFilter ? "No files match the given filter" : "Archive is empty"); + return; + } + + var fileArray = fileList.OrderBy(e => e.Offset).ToArray(); + + Console.WriteLine(fileArray[0].Offset); + + switch (command) + { + case "i": + Console.WriteLine(m_fs.Source.Tag); + break; + case "l": + ListFiles(fileArray); + break; + case "x": + ExtractFiles(fileArray, m_fs.Source); + break; + } + } + + void DeserializeGameData() + { + var scheme_file = Path.Combine(FormatCatalog.Instance.DataDirectory, "Formats.dat"); + try + { + using (var file = File.OpenRead(scheme_file)) FormatCatalog.Instance.DeserializeScheme(file); + } + catch (Exception) + { + //Console.Error.WriteLine("Scheme deserialization failed: {0}", e.Message); + } + } + + static void Usage() + { + Console.WriteLine(string.Format("Usage: {0} [...] ", Process.GetCurrentProcess().ProcessName)); + Console.WriteLine("\nCommands:"); + Console.WriteLine(" i Identify archive format"); + Console.WriteLine(" f List supported formats"); + Console.WriteLine(" l List contents of archive"); + Console.WriteLine(" x Extract files from archive"); + Console.WriteLine("\nSwitches:"); + Console.WriteLine(" -o Set output directory for extraction"); + Console.WriteLine(" -f Only process files matching the regular expression "); + Console.WriteLine(" -if Set image output format (e.g. 'png', 'jpg', 'bmp')"); + Console.WriteLine(" -ca Convert audio files to wav format"); + Console.WriteLine(" -na Ignore audio files"); + Console.WriteLine(" -ni Ignore image files"); + Console.WriteLine(" -ns Ignore scripts"); + Console.WriteLine(" -aio Adjust image offset"); + Console.WriteLine(" -ocu Set -if switch to only convert unknown image formats"); + Console.WriteLine(); + //Console.WriteLine(FormatCatalog.Instance.ArcFormats.Count() + " supported formats"); + } + + static void PrintError(string msg) + { + Console.WriteLine("Error: " + msg); + } + + string OverwritePrompt(string filename) + { + switch (existingFileAction) + { + + case ExistingFileAction.Skip: + return null; + case ExistingFileAction.Overwrite: + return filename; + case ExistingFileAction.Rename: + return GetPathToRename(filename); + } + + Console.WriteLine(string.Format($"The file {filename} already exists. Overwrite? [Y]es | [N]o | [A]lways | n[E]ver | [R]ename | A[l]ways rename")); + + while (true) + { + switch (Console.Read()) + { + case 'y': + case 'Y': + return filename; + case 'n': + case 'N': + return null; + case 'a': + case 'A': + existingFileAction = ExistingFileAction.Overwrite; + return filename; + case 'e': + case 'E': + existingFileAction = ExistingFileAction.Skip; + return null; + case 'r': + case 'R': + return GetPathToRename(filename); + case 'l': + case 'L': + existingFileAction = ExistingFileAction.Rename; + return GetPathToRename(filename); + } + } + } + + string GetPathToRename(string path) + { + var directory = Path.GetDirectoryName(path); + var fileName = Path.GetFileNameWithoutExtension(path); + var fileExtension = Path.GetExtension(path); + + var i = 2; + do + { + path = Path.Combine(directory, string.Format($"{fileName} ({i}){fileExtension}")); + i++; + } while (File.Exists(path)); + + return path; + } + + private static void OnParametersRequest(object sender, ParametersRequestEventArgs eventArgs) + { + // Some archives are encrypted or require parameters to be set. + // Let's just use the default values for now. + var format = (IResource)sender; + //Console.WriteLine(eventArgs.Notice); + eventArgs.InputResult = true; + eventArgs.Options = format.GetDefaultOptions(); + } + + static void Main(string[] args) + { + Console.OutputEncoding = Encoding.UTF8; + Console.WriteLine(string.Format("GARbro - Game Resource browser, version {0}\n2014-2019 by mørkt, published under a MIT license", Assembly.GetAssembly(typeof(FormatCatalog)).GetName().Version)); + Console.WriteLine("-----------------------------------------------------------------------------\n"); + + FormatCatalog.Instance.ParametersRequest += OnParametersRequest; + //var listener = new TextWriterTraceListener(Console.Error); + //Trace.Listeners.Add(listener); + + var browser = new ConsoleBrowser(); + browser.Run(args); + +#if DEBUG + Console.Read(); +#endif + } + } +} \ No newline at end of file diff --git a/Console/GARbro.Console.Core.csproj b/Console/GARbro.Console.Core.csproj index c6ea3bbe..b79274db 100644 --- a/Console/GARbro.Console.Core.csproj +++ b/Console/GARbro.Console.Core.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,6 +6,7 @@ false GARbro.Console GARbro + true diff --git a/Console/README.md b/Console/README.md index 9570838b..f74b9991 100644 --- a/Console/README.md +++ b/Console/README.md @@ -1,7 +1,31 @@ -GameRes.Console +GARbro.Console =============== -Console utility that extracts files from game archives. Used as a testing -playground for GameRes library. +Standalone command line version of GARbro, which can list and extract files from supported archives. -No longer developed. +### Usage + +`GARbro.Console.exe [...] ` + +###### Commands: + +| Command | Description | +| ------- | ------------------------------------------------------------ | +| i | Identify archive format | +| f | Display supported formats. This prints a list of all formats GARbro can recognize. | +| l | List contents of archive | +| x | Extract files from archive | + +###### Switches: + +| Switch | Description | +| -------------- | ------------------------------------------------------------ | +| -o | Set output directory for extraction | +| -f | Only process files matching the regular expression | +| -if | Set image output format (e.g. 'png', 'jpg', 'bmp'). This converts all image files to the specified format. Caution: conversion might reduce the image quality and transparency can be lost (depending on the output format). Use the `-ocu` switch to skip common image formats. | +| -ca | Convert audio files to wav format; without this switch the original format is retained | +| -na | Skip audio files | +| -ni | Skip image files | +| -ns | Skip scripts | +| -aio | Adjust image offset | +| -ocu | Set -if switch to only convert unknown image formats.
The default behavior of `-if` is to convert all images to the specified format. This might not desirable because it reduces the image quality or transparency information can be lost. With the `-ocu` switch only uncommon formats are converted - for example jpg and png files are kept as is, while proprietary formats are converted. | \ No newline at end of file diff --git a/Experimental/Experimental.Core.csproj b/Experimental/Experimental.Core.csproj index 4853877e..4145b455 100644 --- a/Experimental/Experimental.Core.csproj +++ b/Experimental/Experimental.Core.csproj @@ -23,6 +23,7 @@ + @@ -31,7 +32,6 @@ - diff --git a/Experimental/Properties/AssemblyInfo.cs b/Experimental/Properties/AssemblyInfo.cs index 99544193..0b8878c9 100644 --- a/Experimental/Properties/AssemblyInfo.cs +++ b/Experimental/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.0.16.39")] -[assembly: AssemblyFileVersion ("1.0.16.39")] +[assembly: AssemblyVersion ("1.0.16.42")] +[assembly: AssemblyFileVersion ("1.0.16.42")] diff --git a/GARbro.sln b/GARbro.sln index 9235bb65..89e7b995 100644 --- a/GARbro.sln +++ b/GARbro.sln @@ -19,16 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Legacy.Core", "Legacy\Legac EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GARbro.GUI.Core", "GUI\GARbro.GUI.Core.csproj", "{AA001966-9A17-4843-84E1-19142C3BE1C5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Deployment.Compression.Core", "Cab\Microsoft.Deployment.Compression\Microsoft.Deployment.Compression.Core.csproj", "{72457009-3C21-4510-80DD-A69A1E95E2C8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Deployment.Compression.Cab.Core", "Cab\Microsoft.Deployment.Compression.Cab\Microsoft.Deployment.Compression.Cab.Core.csproj", "{E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Image.Convert.Core", "Image.Convert\Image.Convert.Core.csproj", "{96C528AB-1794-44FF-B656-C5BF8552A712}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GARbro.Console.Core", "Console\GARbro.Console.Core.csproj", "{62AACEB7-B1F3-421B-A3CE-0107B5C44041}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cab", "Cab", "{5207F2E8-E05D-4155-8B1A-A1644CC76BB7}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -76,18 +70,6 @@ Global {AA001966-9A17-4843-84E1-19142C3BE1C5}.Prerelease|Any CPU.Build.0 = Debug|Any CPU {AA001966-9A17-4843-84E1-19142C3BE1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA001966-9A17-4843-84E1-19142C3BE1C5}.Release|Any CPU.Build.0 = Release|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Prerelease|Any CPU.Build.0 = Debug|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {72457009-3C21-4510-80DD-A69A1E95E2C8}.Release|Any CPU.Build.0 = Release|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Prerelease|Any CPU.Build.0 = Debug|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F}.Release|Any CPU.Build.0 = Release|Any CPU {96C528AB-1794-44FF-B656-C5BF8552A712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {96C528AB-1794-44FF-B656-C5BF8552A712}.Debug|Any CPU.Build.0 = Debug|Any CPU {96C528AB-1794-44FF-B656-C5BF8552A712}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU @@ -106,8 +88,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD} = {015EA4FF-2464-4B20-B041-C4318FBC2696} - {72457009-3C21-4510-80DD-A69A1E95E2C8} = {5207F2E8-E05D-4155-8B1A-A1644CC76BB7} - {E1D5E2AA-DBFB-4219-BADE-72CF6ABA923F} = {5207F2E8-E05D-4155-8B1A-A1644CC76BB7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9DCBA24E-9BB5-49C5-B311-ADF3B5C274CC} diff --git a/GUI/AutoComplete.cs b/GUI/AutoComplete.cs index b256f36b..6fce684e 100644 --- a/GUI/AutoComplete.cs +++ b/GUI/AutoComplete.cs @@ -42,13 +42,13 @@ public class ExtAutoCompleteBox : AutoCompleteTextBox.Editors.AutoCompleteTextBo public event EnterKeyDownEvent EnterKeyDown; public event TextChangedEventHandler TextChanged; - public TextBox Editor { get; set; } + //public TextBox Editor { get; set; } public override void OnApplyTemplate() { base.OnApplyTemplate(); - Editor = Template.FindName(PartEditor, this) as TextBox; + //Editor = Template.FindName(PartEditor, this) as TextBox; if (Editor != null) { diff --git a/GUI/GARbro.GUI.Core.csproj b/GUI/GARbro.GUI.Core.csproj index 41c2621b..5e5d6b69 100644 --- a/GUI/GARbro.GUI.Core.csproj +++ b/GUI/GARbro.GUI.Core.csproj @@ -7,6 +7,8 @@ GARbro.GUI GARbro.GUI false + Images\sample.ico + Properties\app.manifest
@@ -33,10 +35,13 @@ + + + @@ -81,9 +86,9 @@ Never - + Never - + diff --git a/GUI/Properties/AssemblyInfo.cs b/GUI/Properties/AssemblyInfo.cs index abb91452..6a8b735d 100644 --- a/GUI/Properties/AssemblyInfo.cs +++ b/GUI/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -51,5 +51,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.5.44.2904")] -[assembly: AssemblyFileVersion ("1.5.44.2904")] +[assembly: AssemblyVersion ("1.5.44.2934")] +[assembly: AssemblyFileVersion ("1.5.44.2934")] diff --git a/GameRes/Properties/AssemblyInfo.cs b/GameRes/Properties/AssemblyInfo.cs index 67bc98cf..d508ecdd 100644 --- a/GameRes/Properties/AssemblyInfo.cs +++ b/GameRes/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.5.44.319")] -[assembly: AssemblyFileVersion ("1.5.44.319")] +[assembly: AssemblyVersion ("1.5.44.324")] +[assembly: AssemblyFileVersion ("1.5.44.324")] diff --git a/Legacy/Properties/AssemblyInfo.cs b/Legacy/Properties/AssemblyInfo.cs index d039f8ff..2b29d4f2 100644 --- a/Legacy/Properties/AssemblyInfo.cs +++ b/Legacy/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion ("1.0.10.196")] -[assembly: AssemblyFileVersion ("1.0.10.196")] +[assembly: AssemblyVersion ("1.0.10.199")] +[assembly: AssemblyFileVersion ("1.0.10.199")] diff --git a/inc-revision.csx b/inc-revision.csx index d1a998c6..93ce8fbf 100644 --- a/inc-revision.csx +++ b/inc-revision.csx @@ -115,7 +115,6 @@ if (version_changed) } catch (System.Exception) { - throw; throw new Exception(assembly_info); } try @@ -124,7 +123,18 @@ if (version_changed) } catch (System.Exception) { - throw; + throw new Exception(tmp_filename); + } +} +else +{ + try + { + File.Delete($"{assembly_info}~"); + File.Delete(tmp_filename); + } + catch (System.Exception) + { throw new Exception(tmp_filename); } } \ No newline at end of file