Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 61 additions & 50 deletions Interface/Actions/ConvertFromXnbAction.cs
Original file line number Diff line number Diff line change
@@ -1,78 +1,90 @@

using System.CommandLine;

using FEZRepacker.Core.Conversion;
using FEZRepacker.Core.FileSystem;
using FEZRepacker.Core.XNB;

using static FEZRepacker.Interface.CommandLineOptions;

namespace FEZRepacker.Interface.Actions
{
internal class ConvertFromXnbAction : CommandLineAction
internal class ConvertFromXnbAction : ICommandLineAction
{
private const string XnbInput = "xnb-input";

private const string FileOutput = "file-output";

private const string UseLegacyAo = "use-legacy-ao";

private const string UseLegacyTs = "use-legacy-ts";

public string Name => "--convert-from-xnb";

public string[] Aliases => new[] { "-x" };
public string[] Aliases => ["-x"];

public string Description =>
"Attempts to convert given XNB input (this can be a path to a single asset or an entire directory) " +
"and save it at given output directory. If input is a directory, dumps all converted files in specified " +
"path recursively. If output directory is not given, outputs next to the input file(s).";

public CommandLineArgument[] Arguments => new[] {
new CommandLineArgument(XnbInput),
new CommandLineArgument(FileOutput, ArgumentType.OptionalPositional),
new CommandLineArgument(UseLegacyAo, ArgumentType.Flag),
new CommandLineArgument(UseLegacyTs, ArgumentType.Flag)
"Attempts to convert given XNB input and save it at given output directory";

public Argument[] Arguments => [_inputSource, _outputDirectory];

public Option[] Options => [UseTrileSetLegacyBundles, UseArtObjectLegacyBundles];

private readonly Argument<FileSystemInfo> _inputSource = new("input-source")
{
Description = "Given XNB input to convert (this can be a path to a single file or an entire directory).\n"
+ " If input is a directory, dumps all converted files in specified path recursively."
};

private readonly Argument<DirectoryInfo> _outputDirectory = new("output-directory")
{
Arity = ArgumentArity.ZeroOrOne,
Description = "Output directory for saving converted file(s).\n"
+ " If output directory is not given, outputs next to the input file(s)."
};

private List<string> FindXnbFilesAtPath(string path)
private static List<string> FindXnbFilesAtPath(FileSystemInfo fileSystem)
{
if (Directory.Exists(path))
if (fileSystem is DirectoryInfo { Exists: true } directoryInfo)
{
var xnbFiles = Directory.GetFiles(path, "*.xnb", SearchOption.AllDirectories).ToList();
Console.WriteLine($"Found {xnbFiles.Count()} XNB files in given directory.");
return xnbFiles;
var xnbFiles = directoryInfo.GetFiles("*.xnb", SearchOption.AllDirectories);
Console.WriteLine($"Found {xnbFiles.Length} XNB files in given directory.");
return xnbFiles.Select(f => f.FullName).ToList();
}
else if (File.Exists(path))

if (fileSystem is not FileInfo fileInfo)
{
if (Path.GetExtension(path) != ".xnb")
{
throw new Exception("An input file must be an .XNB file.");
}
return new List<string> { path };
return [fileSystem.FullName];
}
else

if (!fileInfo.Exists)
{
throw new FileNotFoundException("Specified input path does not lead to any file or a directory");
}

return fileInfo.Extension != ".xnb"
? throw new Exception("An input file must be an .XNB file.")
: [fileSystem.FullName];
}

public void Execute(Dictionary<string, string> args)
public void Execute(ParseResult result)
{
var inputPath = args[XnbInput];
var outputPath = args.GetValueOrDefault(FileOutput, inputPath);
var inputSource = result.GetRequiredValue(_inputSource);
var outputDirectory = result.GetValue(_outputDirectory);

if (File.Exists(outputPath))
var outputPath = inputSource switch
{
outputPath = Path.GetDirectoryName(outputPath) ?? "";
}
Directory.CreateDirectory(outputPath);
FileInfo inputFile => inputFile.DirectoryName!,
DirectoryInfo inputDirectory => inputDirectory.FullName,
_ => throw new ArgumentException(nameof(inputSource))
};

var xnbFilesToConvert = FindXnbFilesAtPath(inputPath);
if (outputDirectory != null)
{
if (!outputDirectory.Exists)
{
outputDirectory.Create();
}
outputPath = outputDirectory.FullName;
}

Console.WriteLine($"Converting {xnbFilesToConvert.Count()} XNB files...");
var xnbFilesToConvert = FindXnbFilesAtPath(inputSource);
Console.WriteLine($"Converting {xnbFilesToConvert.Count} XNB files...");

var filesDone = 0;
var settings = new FormatConverterSettings
{
UseLegacyArtObjectBundle = args.ContainsKey(UseLegacyAo),
UseLegacyTrileSetBundle = args.ContainsKey(UseLegacyTs)
UseLegacyArtObjectBundle = result.GetValue(UseArtObjectLegacyBundles),
UseLegacyTrileSetBundle = result.GetValue(UseTrileSetLegacyBundles)
};

foreach (var xnbPath in xnbFilesToConvert)
Expand All @@ -83,19 +95,18 @@ public void Execute(Dictionary<string, string> args)

using var outputBundle = UnpackAction.UnpackFile(".xnb", xnbStream, UnpackAction.UnpackingMode.Converted, settings);

var relativePathRaw = xnbPath == inputPath
var relativePathRaw = xnbPath == inputSource.FullName
? Path.GetFileName(xnbPath)
: Path.GetRelativePath(inputPath, xnbPath);
: Path.GetRelativePath(inputSource.FullName, xnbPath);

var relativePath = relativePathRaw
.Replace("/", "\\")
.Replace(".xnb", "", StringComparison.InvariantCultureIgnoreCase);

outputBundle.BundlePath = Path.Combine(outputPath, relativePath + outputBundle.MainExtension);
var outputDirectory = Path.GetDirectoryName(outputBundle.BundlePath) ?? "";

var outputDirectoryPath = Path.GetDirectoryName(outputBundle.BundlePath) ?? "";

Directory.CreateDirectory(outputDirectory);
Directory.CreateDirectory(outputDirectoryPath);
foreach (var outputFile in outputBundle.Files)
{
using var fileOutputStream = File.Open(outputBundle.BundlePath + outputFile.Extension, FileMode.Create);
Expand Down
68 changes: 39 additions & 29 deletions Interface/Actions/ConvertToXnbAction.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@

using System.IO;
using System.CommandLine;

using FEZRepacker.Core.Conversion;
using FEZRepacker.Core.FileSystem;
using FEZRepacker.Core.XNB;

namespace FEZRepacker.Interface.Actions
{
internal class ConvertToXnbAction : CommandLineAction
internal class ConvertToXnbAction : ICommandLineAction
{
private const string FileInput = "file-input";

private const string XnbOutput = "xnb-output";

private const string UseLegacyAo = "use-legacy-ao";

private const string UseLegacyTs = "use-legacy-ts";

public string Name => "--convert-to-xnb";

public string[] Aliases => new[] { "-X" };
public string[] Aliases => ["-X"];

public string Description =>
"Attempts to convert given input (this can be a path to a single file or an entire directory) " +
"into XNB file(s) and save it at given output directory. If input is a directory, dumps all converted files in" +
"specified path recursively. If output directory is not given, outputs next to the input file(s).";
"Attempts to convert given input into XNB file(s) and save it at given output directory";

public Argument[] Arguments => [_inputSource];

public CommandLineArgument[] Arguments => new[] {
new CommandLineArgument(FileInput),
new CommandLineArgument(XnbOutput, ArgumentType.OptionalPositional)
public Option[] Options => [_outputDirectory];

private readonly Argument<FileSystemInfo> _inputSource = new("input-source")
{
Description = "Given input to convert (this can be a path to a single file or an entire directory).\n"
+ " If input is a directory, dumps all converted files in specified path recursively."
};

private readonly Option<DirectoryInfo> _outputDirectory = new("output-directory")
{
Description = "Output directory for saving deconverted XNB file(s).\n"
+ " If output directory is not given, outputs next to the input file(s)."
};

public delegate void ConversionFunc(string path, string extension, Stream stream, bool converted);

public static void PerformBatchConversion(List<FileBundle> fileBundles, ConversionFunc processFileFunc)
{
Console.WriteLine($"Converting {fileBundles.Count()} assets...");
Console.WriteLine($"Converting {fileBundles.Count} assets...");
var filesDone = 0;
foreach (var fileBundle in fileBundles)
{
Expand Down Expand Up @@ -65,24 +65,34 @@ public static void PerformBatchConversion(List<FileBundle> fileBundles, Conversi
}
}

public void Execute(Dictionary<string, string> args)
public void Execute(ParseResult result)
{
var inputPath = args[FileInput];
var outputPath = args.GetValueOrDefault(XnbOutput, inputPath);
var inputSource = result.GetRequiredValue(_inputSource);
var outputDirectory = result.GetValue(_outputDirectory);

var outputPath = inputSource switch
{
FileInfo inputFile => inputFile.DirectoryName!,
DirectoryInfo inputDirectory => inputDirectory.FullName,
_ => throw new ArgumentException(nameof(inputSource))
};

if (File.Exists(outputPath))
if (outputDirectory != null)
{
outputPath = Path.GetDirectoryName(outputPath) ?? "";
if (!outputDirectory.Exists)
{
outputDirectory.Create();
}
outputPath = outputDirectory.FullName;
}
Directory.CreateDirectory(outputPath);

var fileBundles = FileBundle.BundleFilesAtPath(inputPath);
Console.WriteLine($"Found {fileBundles.Count()} file bundles.");
var fileBundles = FileBundle.BundleFilesAtPath(inputSource.FullName);
Console.WriteLine($"Found {fileBundles.Count} file bundles.");

PerformBatchConversion(fileBundles, (path, extension, stream, converted) =>
{
if (!converted) return;

var assetOutputFullPath = Path.Combine(outputPath, $"{path}{extension}");

Directory.CreateDirectory(Path.GetDirectoryName(assetOutputFullPath) ?? "");
Expand Down
63 changes: 0 additions & 63 deletions Interface/Actions/HelpAction.cs

This file was deleted.

32 changes: 20 additions & 12 deletions Interface/Actions/ListPackageContentAction.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
using FEZRepacker.Core.FileSystem;
using System.CommandLine;

using FEZRepacker.Core.FileSystem;

namespace FEZRepacker.Interface.Actions
{
internal class ListPackageContentAction : CommandLineAction
internal class ListPackageContentAction : ICommandLineAction
{
private const string PakPath = "pak-path";

public string Name => "--list";
public string[] Aliases => new[] { "-l" };
public string Description => "Lists all files contained withing given .PAK package.";
public CommandLineArgument[] Arguments => new[] {
new CommandLineArgument(PakPath)

public string[] Aliases => ["-l"];

public string Description => "Lists all files contained withing given .PAK package";

public Argument[] Arguments => [_pakFile];

public Option[] Options => [];

private readonly Argument<FileInfo> _pakFile = new("pak-file")
{
Description = "The PAK file to use for listing files."
};

public void Execute(Dictionary<string, string> args)
public void Execute(ParseResult result)
{
var pakPath = args[PakPath];
var pakPackage = PakPackage.ReadFromFile(pakPath);
var pakFile = result.GetRequiredValue(_pakFile);
var pakPackage = PakPackage.ReadFromFile(pakFile.FullName);

Console.WriteLine($"PAK package \"{pakPath}\" with {pakPackage.Entries.Count} files.");
Console.WriteLine($"PAK package \"{pakFile}\" with {pakPackage.Entries.Count} files.");
Console.WriteLine();

foreach (var entry in pakPackage.Entries)
Expand Down
Loading