Skip to content

Commit e760461

Browse files
committed
implement direct xnb conversion in cli
1 parent b76fcf2 commit e760461

2 files changed

Lines changed: 178 additions & 11 deletions

File tree

Interface/CommandLineActions.cs

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using FEZRepacker.Converter.FileSystem;
1+
using System.IO;
2+
3+
using FEZRepacker.Converter.FileSystem;
24
using FEZRepacker.Converter.XNB;
35

46
namespace FEZRepacker.Interface
@@ -173,12 +175,177 @@ public static void ListPackageContent(string pakPath)
173175

174176
public static void ConvertFromXNB(string inputPath, string outputPath)
175177
{
176-
Console.WriteLine("Feature currently not supported.");
178+
var xnbFilesToConvert = new List<string>();
179+
180+
if (Directory.Exists(inputPath))
181+
{
182+
xnbFilesToConvert = Directory.GetFiles(inputPath, "*.xnb", SearchOption.AllDirectories).ToList();
183+
Console.WriteLine($"Found {xnbFilesToConvert.Count()} XNB files in given directory.");
184+
}
185+
else if (File.Exists(inputPath))
186+
{
187+
xnbFilesToConvert.Add(inputPath);
188+
if (Path.GetExtension(inputPath) != ".xnb")
189+
{
190+
throw new Exception("An input file must be an .XNB file.");
191+
}
192+
inputPath = Path.GetDirectoryName(inputPath) ?? "";
193+
}
194+
else
195+
{
196+
throw new FileNotFoundException("Specified input path does not lead to any file or a directory");
197+
}
198+
199+
if (outputPath.Length == 0)
200+
{
201+
outputPath = inputPath;
202+
}
203+
if (!Directory.Exists(outputPath))
204+
{
205+
Directory.CreateDirectory(outputPath);
206+
}
207+
208+
var filesDone = 0;
209+
foreach (var xnbPath in xnbFilesToConvert)
210+
{
211+
Console.WriteLine($"({filesDone + 1}/{xnbFilesToConvert.Count}) {xnbPath}");
212+
213+
using var xnbStream = File.OpenRead(xnbPath);
214+
215+
var converter = new XnbConverter();
216+
var outputBundle = converter.Convert(xnbStream);
217+
var formatName = converter.HeaderValid ? converter.FileType.Name.Replace("Reader", "") : "";
218+
if (converter.Converted)
219+
{
220+
outputBundle.MainExtension = converter.FormatConverter!.FileFormat;
221+
var storageTypeName = (outputBundle.Count > 1 ? "bundle" : "file");
222+
Console.WriteLine($" Format {formatName} converted into {outputBundle.MainExtension} {storageTypeName}.");
223+
}
224+
else
225+
{
226+
Console.WriteLine(converter.HeaderValid ? $" Unknown format {formatName}." : $" Not a valid XNB file." + " Skipping.");
227+
continue;
228+
}
229+
230+
var relativePath = Path.GetRelativePath(inputPath, xnbPath)
231+
.Replace("/", "\\")
232+
.Replace(".xnb", "", StringComparison.InvariantCultureIgnoreCase);
233+
outputBundle.BundlePath = Path.Combine(outputPath, relativePath + outputBundle.MainExtension);
234+
var outputDirectory = Path.GetDirectoryName(outputBundle.BundlePath) ?? "";
235+
if (!Directory.Exists(outputDirectory))
236+
{
237+
Directory.CreateDirectory(outputDirectory);
238+
}
239+
240+
foreach (var outputFile in outputBundle)
241+
{
242+
using var fileOutputStream = File.Open(outputBundle.BundlePath + outputFile.Extension, FileMode.Create);
243+
outputFile.Data.CopyTo(fileOutputStream);
244+
}
245+
246+
filesDone++;
247+
}
177248
}
178249

179250
public static void ConvertIntoXNB(string inputPath, string outputPath)
180251
{
181-
Console.WriteLine("Feature currently not supported.");
252+
var fileNames = new string[0];
253+
254+
if (Directory.Exists(inputPath))
255+
{
256+
fileNames = Directory.GetFiles(inputPath, "*.*", SearchOption.AllDirectories);
257+
Console.WriteLine($"Found {fileNames.Length} files.");
258+
}
259+
else if (File.Exists(inputPath))
260+
{
261+
var fileName = Path.GetFileNameWithoutExtension(inputPath);
262+
inputPath = Path.GetDirectoryName(inputPath) ?? "";
263+
fileNames = Directory.GetFiles(inputPath, $"{fileName}.*");
264+
Console.WriteLine($"Found {fileNames.Length} linked files for a file bundle.");
265+
}
266+
else
267+
{
268+
throw new FileNotFoundException("Specified input path does not lead to any file or a directory");
269+
}
270+
271+
if (outputPath.Length == 0)
272+
{
273+
outputPath = inputPath;
274+
}
275+
if (!Directory.Exists(outputPath))
276+
{
277+
Directory.CreateDirectory(outputPath);
278+
}
279+
280+
// load and transform file list into bundles
281+
var fileList = new Dictionary<string, Stream>();
282+
foreach (var filePath in fileNames)
283+
{
284+
var relativePath = Path.GetRelativePath(inputPath, filePath).Replace("/", "\\").ToLower();
285+
fileList[relativePath] = File.OpenRead(filePath);
286+
}
287+
var fileBundles = FileBundle.BundleFiles(fileList);
288+
Console.WriteLine($"Converting {fileBundles.Count()} file bundles.");
289+
290+
291+
var xnbAssets = new Dictionary<(string Path, string Extension), Stream>();
292+
293+
// convert
294+
Console.WriteLine($"Converting {fileBundles.Count()} assets...");
295+
var filesDone = 0;
296+
foreach (var fileBundle in fileBundles)
297+
{
298+
Console.WriteLine($"({filesDone + 1}/{fileBundles.Count}) {fileBundle.BundlePath}");
299+
300+
try
301+
{
302+
var deconverter = new XnbDeconverter();
303+
304+
var deconverterStream = deconverter.Deconvert(fileBundle);
305+
306+
if (deconverter.Converted)
307+
{
308+
Console.WriteLine($" Format {fileBundle.MainExtension} deconverted into {deconverter.FormatConverter!.FormatName} XNB asset.");
309+
xnbAssets.Add((fileBundle.BundlePath, ".xnb"), deconverterStream);
310+
}
311+
else
312+
{
313+
Console.WriteLine($" Format {fileBundle.MainExtension} doesn't have a converter - packing asset as raw files.");
314+
315+
foreach (var file in fileBundle)
316+
{
317+
file.Data.Seek(0, SeekOrigin.Begin);
318+
var ext = fileBundle.MainExtension + file.Extension;
319+
xnbAssets.Add((fileBundle.BundlePath, ext), file.Data);
320+
}
321+
}
322+
}
323+
catch (Exception ex)
324+
{
325+
Console.Error.WriteLine($"Unable to convert asset {fileBundle.BundlePath} - {ex.Message}");
326+
}
327+
filesDone++;
328+
}
329+
330+
Console.WriteLine($"Saving {xnbAssets.Count()} XNB assets...");
331+
332+
foreach (var assetRecord in xnbAssets)
333+
{
334+
var assetPath = assetRecord.Key.Path;
335+
var assetExtension = assetRecord.Key.Extension;
336+
var asset = assetRecord.Value;
337+
338+
var assetOutputFullPath = Path.Combine(outputPath, $"{assetPath}{assetExtension}");
339+
340+
var directoryPath = Path.GetDirectoryName(assetOutputFullPath) ?? "";
341+
if (directoryPath.Length > 0 && !Directory.Exists(directoryPath))
342+
{
343+
Directory.CreateDirectory(directoryPath);
344+
}
345+
346+
using var assetFile = File.Create(assetOutputFullPath);
347+
asset.CopyTo(assetFile);
348+
}
182349
}
183350

184351
public static void AddToPackage(string inputPath, string outputPackagePath, string includePackagePath)

Interface/CommandLineInterface.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,19 @@ public Command()
9393
{
9494
Name = "--convert-from-xnb", Aliases = new[]{"-x"},
9595
HelpText = "Attempts to convert given XNB input (this can be a path to a single asset or an entire directory) " +
96-
"and save it at given output (if input is a directory, dumps all converted files in specified path). " +
97-
"Directories are treated recursively.",
98-
Arguments = new[]{ new Argument("xnb-input"), new Argument("file-output") },
99-
Operation = delegate(string[] args){ ConvertFromXNB(args[0], args[1]); }
96+
"and save it at given output directory. If input is a directory, dumps all converted files in specified path " +
97+
"recursively. If output directory is not given, outputs next to the input file(s).",
98+
Arguments = new[]{ new Argument("xnb-input"), new Argument("file-output", true) },
99+
Operation = delegate(string[] args){ ConvertFromXNB(args[0], args.Length > 1 ? args[1] : ""); }
100100
},
101101
new Command
102102
{
103103
Name = "--convert-to-xnb", Aliases = new[]{"-X"},
104104
HelpText = "Attempts to convert given input (this can be a path to a single file or an entire directory) " +
105-
"into XNB file(s) and save it at given output (if input is a directory, dumps all converted files in specified path). " +
106-
"Directories are treated recursively.",
107-
Arguments = new[]{ new Argument("file-input"), new Argument("xnb-output") },
108-
Operation = delegate(string[] args){ ConvertIntoXNB(args[0], args[1]); }
105+
"into XNB file(s) and save it at given output directory. If input is a directory, dumps all converted files in" +
106+
"specified path recursively. If output directory is not given, outputs next to the input file(s).",
107+
Arguments = new[]{ new Argument("file-input"), new Argument("xnb-output", true) },
108+
Operation = delegate(string[] args){ ConvertIntoXNB(args[0], args.Length > 1 ? args[1] : ""); }
109109
},
110110
};
111111

0 commit comments

Comments
 (0)