diff --git a/build-all.bat b/build-all.bat index 7bb085a..32dcf62 100644 --- a/build-all.bat +++ b/build-all.bat @@ -1,5 +1,10 @@ echo on +:: Delete builds folder and contents +:: /s Deletes a directory tree (the specified directory and all its subdirectories, including all files). +:: /q Specifies quiet mode. Does not prompt for confirmation when deleting a directory tree. The /q parameter works only if /s is also specified. +rmdir /s /q builds + :: Move to project folder pushd . cd src/gfz-cli @@ -30,7 +35,7 @@ robocopy .\src\gfz-cli\bin\Debug\net6.0\win-x64\publish\ .\builds\win-x64\ /s :: Compress folders as ZIP pushd . -cd builds\ +cd builds/ tar.exe -a -c -f linux-x64.zip linux-x64 tar.exe -a -c -f osx10.10-x64.zip osx10.10-x64 diff --git a/src/gfz-cli/Konsole.cs b/src/gfz-cli/Konsole.cs index 4b31156..353ad54 100644 --- a/src/gfz-cli/Konsole.cs +++ b/src/gfz-cli/Konsole.cs @@ -17,7 +17,7 @@ private static void Write(Action consoleWrite, ConsoleColor foregroundColor, Con var fgColor = Console.ForegroundColor; var bgColor = Console.BackgroundColor; Console.ForegroundColor = foregroundColor; - Console.ForegroundColor = backgroundColor; + Console.BackgroundColor = backgroundColor; consoleWrite.Invoke(); Console.ForegroundColor = fgColor; Console.BackgroundColor = bgColor; diff --git a/src/gfz-cli/Options.cs b/src/gfz-cli/Options.cs index cd7c9d2..dc9c97c 100644 --- a/src/gfz-cli/Options.cs +++ b/src/gfz-cli/Options.cs @@ -6,126 +6,133 @@ namespace Manifold.GFZCLI { public class Options { - internal static class ArgsShort - { - public const char InputPath = 'i'; - public const char OutputPath = 'o'; - public const char SearchSubdirectories = 's'; - public const char SearchPattern = 'p'; - } + //internal static class ArgsShort + //{ + // public const char SearchPattern = 'p'; + // public const char SearchSubdirectories = 's'; + //} internal static class Args { - public const string OverwriteFiles = "overwrite"; public const string Verbose = "verbose"; - public const string InputPath = "input-path"; - public const string OutputPath = "output-path"; - public const string SearchSubdirectories = "search-subdirs"; + public const string OverwriteFiles = "overwrite"; public const string SearchPattern = "search-pattern"; - public const string CarDataBinPath = "cardata-bin-to-tsv"; - public const string CarDataTsvPath = "cardata-tsv-to-bin"; + public const string SearchSubdirectories = "search-subdirs"; public const string SerializationFormat = "format"; + public const string LzDecompressTarget = "lzd"; public const string LzCompressTarget = "lzc"; + public const string TplUnpack = "tpl-unpack"; public const string TplUnpackMipmaps = "tpl-unpack-mipmaps"; public const string TplUnpackSaveCorruptedTextures = "tpl-unpack-corrupted-cmpr"; public const string TplPack = "tpl-pack"; + + public const string CarDataBinPath = "cardata-bin-to-tsv"; + public const string CarDataTsvPath = "cardata-tsv-to-bin"; } internal static class Help { public const string Verbose = - "Set output to verbose messages."; - - public const string InputPath = - "Input path to source image file."; + "Output all messages to console.\n" + + "\tEnabled only when called."; - public const string OutputPath = - "Output path to source image file."; - - public const string SearchSubdirectories = - "(true|false) Whether or not to search subdirectories for files when using the directory mode."; + public const string OverwriteFiles = + "Allow output files to overwrite existing files.\n" + + "\tEnabled only when called."; public const string SearchPattern = - "The search pattern used to find files.\n\tEx: \"*.png\""; + "The search pattern used to find files.\n" + + "\tEx: \"*.tpl.lz\" (find all compressed TPL files in any directory, if permitted.)\n" + + "\tEx: \"st??.gma\" (find GMA files with 2 digit stage index in same directory.)"; + public const string SearchSubdirectories = + "Whether or not to search subdirectories for files when using the directory mode.\n" + + "\tEnabled only when called."; - public const string RemoveAllExtensions = - "Removes all extensions from input file. True by default.\n\tEx: \"spr.ci8.png\" -> \"spr.sprite\" "; + public const string SerializationFormat = + "The format used when serializing.\n" + + "\tOptions: \"ax\", \"gx\". Set to \"gx\" by default."; - public const string CarDataToTSV = - "Creates a TSV spreadsheet from the values of cardata.lz binary."; - public const string CarDataFromTSV = - "Creates a binary from the values of cardata.tsv spreadsheet."; - - - public const string Format = - "The format used when serializing. Either AX or GX."; public const string LzDecompressTarget = - "File or directory to decompress."; + "The target path to decompress. Can be file or directory."; public const string LzCompressTarget = - "File or directory to compress."; + "The target path to compress. Can be file or directory\n" + + "\tUse --" + Args.SerializationFormat + " to set output format."; - public const string TplUnpack = "TPL Unpack"; - public const string TplPack = "TPL Pack"; - } + public const string TplUnpack = + "Creates a folder containing the image contents of target .tpl file(s).\n" + + "\tInput path can be a file or directory.\n" + + "\tOutput folder is created in the same directory as the file."; + public const string TplPack = + "TODO"; + public const string TplUnpackMipmaps = + "Export mipmap textures.\n" + + "\t--" + Args.TplUnpack + " must be called."; + public const string TplUnpackSaveCorruptedTextures = + "Export corrupted CMPR mipmap textures.\n" + + "\t--" + Args.TplUnpack + " must be called.\n" + + "\t--" + Args.TplUnpackMipmaps + " must be called."; - [Option(Args.OverwriteFiles, Required = false, HelpText = "TODO")] - public bool OverwriteFiles { get; set; } + public const string CarDataToTSV = + "Creates a TSV spreadsheet from the values of cardata.lz binary.\n" + + "\tApplies only to F-Zero GX. File location: ./game/cardata.lz"; + public const string CarDataFromTSV = + "Creates a binary from the values of a cardata TSV spreadsheet."; + } + [Option(Args.Verbose, Required = false, HelpText = Help.Verbose)] public bool Verbose { get; set; } - [Option(ArgsShort.InputPath, Args.InputPath, Required = false, HelpText = Help.InputPath)] - public string InputPath { get; set; } = string.Empty; - - [Option(ArgsShort.OutputPath, Args.OutputPath, Required = false, HelpText = Help.OutputPath)] - public string OutputPath { get; set; } = string.Empty; - - - [Option(ArgsShort.SearchSubdirectories, Args.SearchSubdirectories, Required = false, HelpText = Help.SearchSubdirectories)] - public bool SearchSubdirectories { get; set; } + [Option(Args.OverwriteFiles, Required = false, HelpText = Help.OverwriteFiles)] + public bool OverwriteFiles { get; set; } - [Option(ArgsShort.SearchPattern, Args.SearchPattern, Required = false, HelpText = Help.SearchPattern)] + [Option(Args.SearchPattern, Required = false, HelpText = Help.SearchPattern)] public string SearchPattern { get; set; } = string.Empty; + [Option(Args.SearchSubdirectories, Required = false, HelpText = Help.SearchSubdirectories)] + public bool SearchSubdirectories { get; set; } - [Option(Args.CarDataBinPath, Required = false, HelpText = Help.CarDataToTSV)] - public string CarDataBinPath { get; set; } = string.Empty; - - [Option(Args.CarDataTsvPath, Required = false, HelpText = Help.CarDataFromTSV)] - public string CarDataTsvPath { get; set; } = string.Empty; - - - [Option(Args.SerializationFormat, Required = false, HelpText = Help.CarDataFromTSV)] + [Option(Args.SerializationFormat, Required = false, HelpText = Help.SerializationFormat)] public string SerializationFormat { get; set; } = string.Empty; - [Option(Args.LzDecompressTarget, Required = false, HelpText = Help.CarDataFromTSV)] + // LZ + [Option(Args.LzDecompressTarget, Required = false, HelpText = Help.LzDecompressTarget)] public string LzDecompressTarget { get; set; } = string.Empty; - [Option(Args.LzCompressTarget, Required = false, HelpText = Help.CarDataFromTSV)] + [Option(Args.LzCompressTarget, Required = false, HelpText = Help.LzCompressTarget)] public string LzCompressTarget { get; set; } = string.Empty; + // TPL [Option(Args.TplUnpack, Required = false, HelpText = Help.TplUnpack)] public string TplUnpack { get; set; } = string.Empty; - [Option(Args.TplUnpackSaveCorruptedTextures, Required = false, HelpText = "TODO")] - public bool TplUnpackSaveCorruptedTextures { get; set; } - - [Option(Args.TplUnpackMipmaps, Required = false, HelpText = "TODO")] + [Option(Args.TplUnpackMipmaps, Required = false, HelpText = Help.TplUnpackMipmaps)] public bool TplUnpackMipmaps { get; set; } + [Option(Args.TplUnpackSaveCorruptedTextures, Required = false, HelpText = Help.TplUnpackSaveCorruptedTextures)] + public bool TplUnpackSaveCorruptedTextures { get; set; } + // TEMP: disable PACK for release //[Option(Args.TplPack, Required = false, HelpText = Help.TplPack)] public string TplPack { get; set; } = string.Empty; - // TODO: implement/parse image-sharp enum for texture output types (use n64-mksprite impl.) + // CARDATA + [Option(Args.CarDataBinPath, Required = false, HelpText = Help.CarDataToTSV)] + public string CarDataBinPath { get; set; } = string.Empty; + + [Option(Args.CarDataTsvPath, Required = false, HelpText = Help.CarDataFromTSV)] + public string CarDataTsvPath { get; set; } = string.Empty; + + + public SearchOption SearchOption => SearchSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; public SerializeFormat SerializeFormat => GetSerializeFormat(SerializationFormat); public GxGame AvGame => GetAvFormat(SerializeFormat); @@ -133,16 +140,15 @@ internal static class Help public void PrintState() { - Console.WriteLine("Options:"); - Console.WriteLine($"{nameof(Verbose)}: {Verbose}"); - Console.WriteLine($"{nameof(InputPath)}: {InputPath}"); - Console.WriteLine($"{nameof(OutputPath)}: {OutputPath}"); - Console.WriteLine($"{nameof(SearchSubdirectories)}: {SearchSubdirectories}"); - Console.WriteLine($"{nameof(SearchOption)}: {SearchOption}"); - Console.WriteLine($"{nameof(SearchPattern)}: {SearchPattern}"); + //Console.WriteLine("Options:"); + //Console.WriteLine($"{nameof(Verbose)}: {Verbose}"); + //Console.WriteLine($"{nameof(InputPath)}: {InputPath}"); + //Console.WriteLine($"{nameof(OutputPath)}: {OutputPath}"); + //Console.WriteLine($"{nameof(SearchSubdirectories)}: {SearchSubdirectories}"); + //Console.WriteLine($"{nameof(SearchOption)}: {SearchOption}"); + //Console.WriteLine($"{nameof(SearchPattern)}: {SearchPattern}"); } - private static SerializeFormat GetSerializeFormat(string serializeFormat) { serializeFormat = serializeFormat.ToLower(); @@ -152,8 +158,12 @@ private static SerializeFormat GetSerializeFormat(string serializeFormat) return SerializeFormat.AX; case "gx": - default: + case "": return SerializeFormat.GX; + + default: + string msg = $"No {nameof(SerializationFormat)} matches input \"{serializeFormat}\""; + throw new ArgumentException(msg); } } private static GxGame GetAvFormat(SerializeFormat serializeFormat) @@ -162,7 +172,9 @@ private static GxGame GetAvFormat(SerializeFormat serializeFormat) { case SerializeFormat.AX: return GxGame.FZeroAX; case SerializeFormat.GX: return GxGame.FZeroGX; - default: throw new ArgumentException(); + default: + string msg = $"No {nameof(SerializationFormat)} \"{serializeFormat}\" defined."; + throw new ArgumentException(msg); } } diff --git a/src/gfz-cli/Program.cs b/src/gfz-cli/Program.cs index 756a49d..e299986 100644 --- a/src/gfz-cli/Program.cs +++ b/src/gfz-cli/Program.cs @@ -26,9 +26,29 @@ public static void Main(string[] args) var encodingProvider = System.Text.CodePagesEncodingProvider.Instance; System.Text.Encoding.RegisterProvider(encodingProvider); + // If user did not pass any arguments, tell them how to use application. + // This will happen when users double-click application. + bool noArgumentsPassed = args.Length == 0; + if (noArgumentsPassed) + { + string msg = "You must call this program using arguments via the Console/Terminal. "; + Konsole.WriteLine(msg, ConsoleColor.Black, ConsoleColor.Red); + Konsole.WriteLine(); + args = new string[] { "--help" }; + } + // Run program with options Parser.Default.ParseArguments(args) .WithParsed(RunOptions); + + // If user did not pass any arguments, pause application so they can read Console. + // This will happen when users double-click application. + if (noArgumentsPassed) + { + string msg = "Press ENTER to continue."; + Konsole.WriteLine(msg, ConsoleColor.Black, ConsoleColor.Red); + Console.Read(); + } } public static void RunOptions(Options options) @@ -352,14 +372,14 @@ public static void TplUnpackFile(Options options, string filePath) var writeMsg = isOverwritingFile ? "Overwrote" : "Wrote"; lock (lock_ConsoleWrite) { - Konsole.Write($"TPL: Unpacking file: "); - Konsole.Write(tplFileName, FileNameColor); - Konsole.Write($" Texture {tplIndex},"); - Konsole.Write($" Mipmap {mipmapIndex}. "); - Konsole.Write(writeMsg, writeColor); - Konsole.Write($" file: "); - Konsole.Write(outputPath, FileNameColor); - Konsole.WriteLine(); + Konsole.Write($"TPL: Unpacking file: "); + Konsole.Write(tplFileName, FileNameColor); + Konsole.Write($" Texture {tplIndex},"); + Konsole.Write($" Mipmap {mipmapIndex}. "); + Konsole.Write(writeMsg, writeColor); + Konsole.Write($" file: "); + Konsole.Write(outputPath, FileNameColor); + Konsole.WriteLine(); } } } @@ -470,9 +490,7 @@ public static string[] GetFilesInDirectory(Options options, string path) $"Make sure to use --{Options.Args.SearchPattern} when providing directory paths."; throw new ArgumentException(msg); } - - var searchOption = options.SearchSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - files = Directory.GetFiles(path, options.SearchPattern, searchOption); + files = Directory.GetFiles(path, options.SearchPattern, options.SearchOption); } return files; } diff --git a/src/gfz-cli/gfz-cli.csproj b/src/gfz-cli/gfz-cli.csproj index d310061..a9d8aec 100644 --- a/src/gfz-cli/gfz-cli.csproj +++ b/src/gfz-cli/gfz-cli.csproj @@ -6,6 +6,7 @@ Manifold.GFZCLI enable enable + 0.2.1