diff --git a/ConsoleTools/App.config b/ConsoleTools/App.config
new file mode 100644
index 00000000..86880fb0
--- /dev/null
+++ b/ConsoleTools/App.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ConsoleTools/ConsoleTools.csproj b/ConsoleTools/ConsoleTools.csproj
new file mode 100644
index 00000000..baf889fe
--- /dev/null
+++ b/ConsoleTools/ConsoleTools.csproj
@@ -0,0 +1,27 @@
+
+
+
+ Exe
+ net48
+ 9.0
+ disable
+ enable
+ ..\\bin\\$(Configuration)\\
+ false
+
+
+
+ echo Moving DLL Files
+ mkdir $(TargetDir)lib_console
+ xcopy $(TargetDir)*.dll $(TargetDir)lib_console /y
+ del *.dll
+ del *.cso
+
+
+
+
+
+
+
+
+
diff --git a/ConsoleTools/Program.cs b/ConsoleTools/Program.cs
new file mode 100644
index 00000000..6d0d8e49
--- /dev/null
+++ b/ConsoleTools/Program.cs
@@ -0,0 +1,343 @@
+using HelixToolkit.SharpDX.Core.Utilities;
+using SharpDX.WIC;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using xivModdingFramework.Cache;
+using xivModdingFramework.Models.DataContainers;
+using xivModdingFramework.Models.FileTypes;
+using xivModdingFramework.Mods;
+using xivModdingFramework.SqPack.FileTypes;
+using xivModdingFramework.Textures.DataContainers;
+using xivModdingFramework.Textures.FileTypes;
+using xivModdingFramework.VFX.FileTypes;
+
+namespace ConsoleTools
+{
+ public class ConsoleTools
+ {
+ private static string[] _Args;
+ public static int Main(string[] args)
+ {
+ // Manual lib loader because the app.config method isn't working for some reason.
+ var cwd = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "lib_console");
+ var referenceFiles = Directory.GetFiles(cwd, "*.dll", SearchOption.AllDirectories);
+
+ AppDomain.CurrentDomain.AssemblyResolve += (obj, arg) =>
+ {
+ var name = $"{new AssemblyName(arg.Name).Name}.dll";
+ var assemblyFile = referenceFiles.Where(x => x.EndsWith(name))
+ .FirstOrDefault();
+ if (assemblyFile != null)
+ return Assembly.LoadFrom(assemblyFile);
+ return null;
+ };
+
+ return Run(args).GetAwaiter().GetResult();
+ }
+
+
+ public static async Task Run(string[] args)
+ {
+ _Args = args;
+ await ConsoleConfig.InitCacheFromConfig();
+
+ return await HandleConsoleArgs();
+ }
+
+ private static bool GetFlag(string flag)
+ {
+ if (_Args.Any(x => x == flag))
+ {
+ return true;
+ }
+ return false;
+ }
+ private static string GetArg(string arg)
+ {
+ var idx = Array.IndexOf(_Args, arg);
+ if (idx >= 0)
+ {
+ return _Args[idx];
+ }
+ return null;
+ }
+ public static async Task HandleConsoleArgs()
+ {
+ if (_Args == null || _Args.Length < 1)
+ {
+ return await ShowHelp();
+ }
+ var cmd = _Args[0];
+
+ var code = -1;
+ if (cmd == "/?")
+ {
+ code = await ShowHelp();
+ }
+ else if (cmd == "/upgrade")
+ {
+ code = await HandleUpgrade();
+ }
+ else if(cmd == "/resave")
+ {
+ code = await HandleResaveModpack();
+ }
+ else if (cmd == "/extract")
+ {
+ code = await ExtractFile();
+ }
+ else if (cmd == "/wrap")
+ {
+ code = await WrapFile();
+ }
+ else if (cmd == "/unwrap")
+ {
+ code = await UnwrapFile();
+ }
+ else
+ {
+ await ShowHelp();
+ code = -1;
+ }
+
+ return code;
+ }
+
+ public static async Task HandleUpgrade()
+ {
+ if (_Args.Length < 3)
+ {
+ return -1;
+ }
+
+ var src = _Args[1];
+ var dest = _Args[2];
+ System.Console.Write("Upgrading Modpack: " + src);
+
+ try
+ {
+ await xivModdingFramework.Mods.ModpackUpgrader.UpgradeModpack(src, dest);
+
+ System.Console.Write("Upgraded Modpack saved to: " + dest);
+ }
+ catch (Exception ex)
+ {
+ Trace.WriteLine(ex);
+ return -1;
+ }
+ return 0;
+ }
+
+ public static async Task HandleResaveModpack()
+ {
+ if (_Args.Length < 3)
+ {
+ Console.WriteLine("Insufficient argument count for function.");
+ return -1;
+ }
+
+ var src = _Args[1];
+ var dest = _Args[2];
+ System.Console.WriteLine("Loading Modpack: " + src);
+ try
+ {
+ var data = await WizardData.FromModpack(src);
+ if(data == null)
+ {
+ Console.WriteLine("Failed to load Modpack at: " + src);
+ return -1;
+ }
+
+ await data.WriteModpack(dest);
+
+ System.Console.Write("Modpack Saved to: " + dest);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ return -1;
+ }
+ return 0;
+ }
+
+ public static async Task ExtractFile()
+ {
+ if (_Args.Length < 3)
+ {
+ Console.WriteLine("Insufficient argument count for function.");
+ return -1;
+ }
+
+ var sqpack = GetFlag("/sqpack");
+ var src = _Args[1];
+ var dest = _Args[2];
+
+ Console.WriteLine("Extracting File: " + src);
+
+ var rtx = ModTransaction.BeginReadonlyTransaction();
+ if (Path.GetExtension(src).ToLower() == Path.GetExtension(dest).ToLower())
+ {
+ var data = await rtx.ReadFile(src, false, sqpack);
+ File.WriteAllBytes(dest, data);
+ } else if (src.EndsWith(".tex") || src.EndsWith(".atex"))
+ {
+ var data = await rtx.ReadFile(src);
+ var tex = XivTex.FromUncompressedTex(data);
+ await tex.SaveAs(dest);
+ }
+ else if (src.EndsWith(".mdl"))
+ {
+ await Mdl.ExportMdlToFile(src, dest, 1, null, false, rtx);
+ }
+ else
+ {
+ var data = await rtx.ReadFile(src);
+ File.WriteAllBytes(dest, data);
+ }
+
+ Console.WriteLine("File saved to:" + dest);
+ return 0;
+ }
+ public static async Task WrapFile()
+ {
+ if (_Args.Length < 3)
+ {
+ Console.WriteLine("Insufficient argument count for function.");
+ return -1;
+ }
+
+ var src = _Args[1];
+ var dest = _Args[2];
+
+ // Just dub something in with same extention if we weren't given one.
+ // This will work for anything other than MDL.
+ var ffPath = "chara/file" + Path.GetExtension(dest);
+ if (_Args.Length > 3)
+ {
+ ffPath = _Args[3];
+ }
+ Console.WriteLine("Wrapping File: " + src);
+
+ var sqpack = GetFlag("/sqpack");
+ var parsed = new byte[0];
+ if (sqpack)
+ {
+ parsed = await SmartImport.CreateCompressedFile(src, ffPath);
+ } else
+ {
+ parsed = await SmartImport.CreateUncompressedFile(src, ffPath);
+ }
+
+ File.WriteAllBytes(dest, parsed);
+ Console.WriteLine("Wrapped File saved to: " + dest);
+ return 0;
+ }
+
+ public static async Task UnwrapFile()
+ {
+ if (_Args.Length < 3)
+ {
+ Console.WriteLine("Insufficient argument count for function.");
+ return -1;
+ }
+
+ var ffPath = "";
+ if(_Args.Length > 3)
+ {
+ ffPath = _Args[3];
+ }
+
+ var src = _Args[1];
+ var dest = _Args[2];
+
+ Console.WriteLine("Unwrapping file: " + src);
+ var data = File.ReadAllBytes(src);
+
+ using var br = new BinaryReader(new MemoryStream(data));
+ var type = Dat.GetSqPackType(br);
+
+ if(type > 1 && type < 4)
+ {
+ try
+ {
+ Console.WriteLine("Un-Sqpacking file...");
+ data = await Dat.ReadSqPackFile(data);
+ }
+ catch
+ {
+ // If this failed to parse, it may not be SqPacked.
+ Console.WriteLine("Un-Sqpack failed, continuing with file as-is...");
+ }
+ }
+
+ var rtx = ModTransaction.BeginReadonlyTransaction();
+ if (Path.GetExtension(src).ToLower() == Path.GetExtension(dest).ToLower())
+ {
+ File.WriteAllBytes(dest, data);
+ }
+ else if (src.EndsWith(".tex") || src.EndsWith(".atex"))
+ {
+ var tex = XivTex.FromUncompressedTex(data);
+ await tex.SaveAs(dest);
+ }
+ else if (src.EndsWith(".mdl"))
+ {
+ var mdl = Mdl.GetXivMdl(data);
+ var ttm = TTModel.FromRaw(mdl);
+ ttm.Source = ffPath;
+ await Mdl.ExportTTModelToFile(ttm, ffPath, 1, null, rtx);
+ }
+ else
+ {
+ File.WriteAllBytes(dest, data);
+ }
+
+ Console.WriteLine("Unwrapped File saved to: " + dest);
+ return 0;
+ }
+
+
+ public static async Task ShowHelp()
+ {
+ System.Console.WriteLine("==== ConsoleTools Help ====");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("== Commands ==");
+ System.Console.WriteLine("\t/? - Help => You're looking at it.");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("\t/upgrade [ModpackFilePath] [DestFilePath] - Updates a given Modpack for Dawntrail.");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("\t/resave [ModpackFilePath] [DestFilePath] - Re-Saves a given modpack file to a new path or type, after performing basic file processing.");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("\t/extract [FfxivInternalPath] [DestFilePath] - Extracts a given file from FFXIV. May be SQPacked with /sqpack");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("\t/wrap [SourceFilePath] [DestFilePath] [IntendedFfxivFilePath] - Creates an FFXIV format file from the given source file. May be SQPacked with /sqpack. FF Path only needed for MDLs.");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("\t/unwrap [SourceFilePath] [DestFilePath] [IntendedFfxivFilePath] - Unwraps a given on-disk SqPacked or Flat FFXIV file into the given format. FF Path only needed for MDLs Skeleton/Texture info.");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("== FORMATS ==");
+ System.Console.WriteLine("\tModpacks may be read or written in .ttmp2, .pmp, or unzipped PMP folder path formats.");
+ System.Console.WriteLine("\tImages may be saved as DDS, TEX, TGA, PNG, BMP.");
+ System.Console.WriteLine("\tModels may be either DB or FBX (or other formats if you have other external converters set up).");
+ System.Console.WriteLine("");
+ System.Console.WriteLine("== CURRENT CONFIG ==");
+
+ var config = ConsoleConfig.Get();
+ foreach (PropertyInfo prop in typeof(ConsoleConfig).GetProperties())
+ {
+ var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
+ System.Console.WriteLine(prop.Name + " : " + prop.GetValue(config, null).ToString());
+ }
+
+ return 0;
+ }
+ }
+}
+
diff --git a/ConsoleTools/Properties/launchSettings.json b/ConsoleTools/Properties/launchSettings.json
new file mode 100644
index 00000000..49d68e40
--- /dev/null
+++ b/ConsoleTools/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "ConsoleTools": {
+ "commandName": "Project",
+ "commandLineArgs": "/?"
+ }
+ }
+}
\ No newline at end of file
diff --git a/FFXIV_TexTools.sln b/FFXIV_TexTools.sln
index ef4e37b4..6645e8b7 100644
--- a/FFXIV_TexTools.sln
+++ b/FFXIV_TexTools.sln
@@ -9,17 +9,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xivModdingFramework", "lib\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ForceUpdateAssembly", "ForceUpdateAssembly\ForceUpdateAssembly.csproj", "{734ACC29-8F05-4193-B981-F5266150F11F}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTools", "ConsoleTools\ConsoleTools.csproj", "{60DC3C4C-9911-4536-90A7-171C8336ED0A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
- Endwalker Debug|Any CPU = Endwalker Debug|Any CPU
- Endwalker Debug|x64 = Endwalker Debug|x64
- Endwalker Debug|x86 = Endwalker Debug|x86
- Endwalker Release|Any CPU = Endwalker Release|Any CPU
- Endwalker Release|x64 = Endwalker Release|x64
- Endwalker Release|x86 = Endwalker Release|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
@@ -31,18 +27,6 @@ Global
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Debug|x64.Build.0 = Debug|Any CPU
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Debug|x86.ActiveCfg = Debug|Any CPU
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Debug|x86.Build.0 = Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|Any CPU.ActiveCfg = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|Any CPU.Build.0 = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|x64.ActiveCfg = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|x64.Build.0 = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|x86.ActiveCfg = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Debug|x86.Build.0 = Endwalker Debug|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|Any CPU.ActiveCfg = Endwalker Release|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|Any CPU.Build.0 = Endwalker Release|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|x64.ActiveCfg = Endwalker Release|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|x64.Build.0 = Endwalker Release|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|x86.ActiveCfg = Endwalker Release|Any CPU
- {4A5F7AB1-F296-450B-8F57-2FB82465964E}.Endwalker Release|x86.Build.0 = Endwalker Release|Any CPU
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Release|Any CPU.Build.0 = Release|Any CPU
{4A5F7AB1-F296-450B-8F57-2FB82465964E}.Release|x64.ActiveCfg = Release|Any CPU
@@ -55,18 +39,6 @@ Global
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Debug|x64.Build.0 = Debug|Any CPU
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Debug|x86.ActiveCfg = Debug|Any CPU
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Debug|x86.Build.0 = Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|Any CPU.ActiveCfg = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|Any CPU.Build.0 = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|x64.ActiveCfg = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|x64.Build.0 = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|x86.ActiveCfg = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Debug|x86.Build.0 = Endwalker Debug|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|Any CPU.ActiveCfg = Endwalker Release|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|Any CPU.Build.0 = Endwalker Release|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|x64.ActiveCfg = Endwalker Release|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|x64.Build.0 = Endwalker Release|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|x86.ActiveCfg = Endwalker Release|Any CPU
- {CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Endwalker Release|x86.Build.0 = Endwalker Release|Any CPU
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Release|Any CPU.Build.0 = Release|Any CPU
{CDC45CC3-60E7-4CB1-A267-BE1CB3BDE340}.Release|x64.ActiveCfg = Release|Any CPU
@@ -79,24 +51,24 @@ Global
{734ACC29-8F05-4193-B981-F5266150F11F}.Debug|x64.Build.0 = Debug|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Debug|x86.ActiveCfg = Debug|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Debug|x86.Build.0 = Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|Any CPU.ActiveCfg = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|Any CPU.Build.0 = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|x64.ActiveCfg = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|x64.Build.0 = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|x86.ActiveCfg = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Debug|x86.Build.0 = Endwalker Debug|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|Any CPU.ActiveCfg = Endwalker Release|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|Any CPU.Build.0 = Endwalker Release|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|x64.ActiveCfg = Endwalker Release|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|x64.Build.0 = Endwalker Release|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|x86.ActiveCfg = Endwalker Release|Any CPU
- {734ACC29-8F05-4193-B981-F5266150F11F}.Endwalker Release|x86.Build.0 = Endwalker Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|Any CPU.Build.0 = Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|x64.ActiveCfg = Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|x64.Build.0 = Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|x86.ActiveCfg = Release|Any CPU
{734ACC29-8F05-4193-B981-F5266150F11F}.Release|x86.Build.0 = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|x64.Build.0 = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Debug|x86.Build.0 = Debug|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|x64.ActiveCfg = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|x64.Build.0 = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|x86.ActiveCfg = Release|Any CPU
+ {60DC3C4C-9911-4536-90A7-171C8336ED0A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/FFXIV_TexTools/App.xaml.cs b/FFXIV_TexTools/App.xaml.cs
index fa755b6c..64ae2525 100644
--- a/FFXIV_TexTools/App.xaml.cs
+++ b/FFXIV_TexTools/App.xaml.cs
@@ -15,18 +15,6 @@
namespace FFXIV_TexTools
{
- public static class EntryPoint
- {
- [STAThread]
- public static int Main(string[] args)
- {
- var application = new App();
- var r = application.Run();
- return r;
- }
-
- }
-
///
/// Interaction logic for App.xaml
///
diff --git a/FFXIV_TexTools/Console/ConsoleManager.cs b/FFXIV_TexTools/Console/ConsoleManager.cs
deleted file mode 100644
index d879d54b..00000000
--- a/FFXIV_TexTools/Console/ConsoleManager.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using FFXIV_TexTools.Helpers;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using xivModdingFramework.Cache;
-using xivModdingFramework.General.Enums;
-
-namespace FFXIV_TexTools.Console
-{
-
- internal static class ConsoleManager
- {
-
- public static async Task HandleConsoleArgs(string[] args)
- {
- if(args == null || args.Length < 1 || args[0] != "/c")
- {
- return false;
- }
-
- var gameDir = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory);
- var lang = XivLanguages.GetXivLanguage(Properties.Settings.Default.Application_Language);
- await XivCache.SetGameInfo(gameDir, lang, false);
-
- var cmd = args[1];
-
- var code = -1;
- if(cmd == "/u")
- {
- code = await HandleUpgrade(args);
- }
-
- Application.Current.Shutdown(code);
- return true;
- }
-
- public static async Task HandleUpgrade(string[] args)
- {
- if(args.Length < 4)
- {
- return -1;
- }
-
- var src = args[2];
- var dest = args[3];
-
- try
- {
- await ModpackUpgrader.UpgradeModpack(src, dest);
- } catch (Exception ex)
- {
- Trace.WriteLine(ex);
- return -1;
- }
- return 0;
- }
-
- }
-}
diff --git a/FFXIV_TexTools/FFXIV_TexTools.csproj b/FFXIV_TexTools/FFXIV_TexTools.csproj
index 4df7164b..dab26419 100644
--- a/FFXIV_TexTools/FFXIV_TexTools.csproj
+++ b/FFXIV_TexTools/FFXIV_TexTools.csproj
@@ -17,53 +17,49 @@
false
true
0
- 2.0.0.0
false
true
FFXIV_TexTools
FFXIV_TexTools
Copyright © 2024
- 3.0.3.2
- 3.0.3.2
+
+ 3.0.3.3
+ 3.0.3.3
+ 3.0.3.3
+
9.0
- bin\$(Configuration)\
true
+ ..\\bin\\$(Configuration)\\
+ false
true
- mkdir $(TargetDir)lib
-move $(TargetDir)*.dll $(TargetDir)lib
- mkdir $(TargetDir)lib
-move $(TargetDir)*.dll $(TargetDir)lib
- mkdir $(TargetDir)lib
-move $(TargetDir)*.dll $(TargetDir)lib
- Debug;Release;Endwalker Debug;Endwalker Release
+
+ echo Moving DLL Files
+ mkdir $(TargetDir)lib
+ xcopy $(TargetDir)*.dll $(TargetDir)lib /y
+ del *.dll
+ del *.cso
+
+ Debug;Release
full
$(DefineConstants);DAWNTRAIL
-
- full
- $(DefineConstants);ENDWALKER
-
pdbonly
$(DefineConstants);DAWNTRAIL
-
- pdbonly
- $(DefineConstants);ENDWALKER
-
ffxiv2.ico
-
-
app.manifest
+ FFXIV_TexTools.App
-
- mkdir $(TargetDir)lib
-move $(TargetDir)*.dll $(TargetDir)lib
- FFXIV_TexTools.EntryPoint
-
+
+
+
+
+
+
diff --git a/FFXIV_TexTools/Helpers/FlexibleMessageBox.cs b/FFXIV_TexTools/Helpers/FlexibleMessageBox.cs
index 0fe4598e..032bf76c 100644
--- a/FFXIV_TexTools/Helpers/FlexibleMessageBox.cs
+++ b/FFXIV_TexTools/Helpers/FlexibleMessageBox.cs
@@ -831,7 +831,10 @@ public static DialogResult Show(IWin32Window owner, string text, string caption,
try
{
var mainWindow = MainWindow.GetMainWindow();
- owner = mainWindow.Win32Window;
+ if (mainWindow != null)
+ {
+ owner = mainWindow.Win32Window;
+ }
} catch
{
// No-Op.
diff --git a/FFXIV_TexTools/Helpers/ModpackUpgrader.cs b/FFXIV_TexTools/Helpers/ModpackUpgrader.cs
deleted file mode 100644
index e3eb5c75..00000000
--- a/FFXIV_TexTools/Helpers/ModpackUpgrader.cs
+++ /dev/null
@@ -1,202 +0,0 @@
-using FFXIV_TexTools.Properties;
-using FFXIV_TexTools.Views.Wizard;
-using FFXIV_TexTools.Views;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using xivModdingFramework.Helpers;
-using System.Windows.Forms;
-using System.IO;
-using System.Diagnostics;
-
-namespace FFXIV_TexTools.Helpers
-{
- ///
- /// Full modpack upgrade handler.
- /// This lives in the UI project as it relies on the WizardData handler for simplification of modpack type handling.
- ///
- public static class ModpackUpgrader
- {
- public static async Task UpgradeModpackPrompted(bool includePartials = true)
- {
- var mw = MainWindow.GetMainWindow();
- var ofd = new OpenFileDialog()
- {
- Filter = ViewHelpers.LoadModpackFilter,
- InitialDirectory = Path.GetFullPath(Settings.Default.ModPack_Directory),
- };
-
- if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
- {
- return;
- }
-
- var path = ofd.FileName;
-
-
- await mw.LockUi("Upgrading Modpack");
- try
- {
-
- var data = await UpgradeModpack(path, includePartials);
-
- var ext = Path.GetExtension(path);
-
- var name = Path.GetFileNameWithoutExtension(path);
- if (ext == ".json")
- {
- name = IOUtil.MakePathSafe(data.MetaPage.Name, false);
- }
-
- if(ext != ".ttmp2" && ext != ".pmp")
- {
- ext = ".pmp";
- }
-
-
- // Final Save location
- var dir = Path.GetDirectoryName(path);
- var fName = name + "_dt" + ext;
- var sfd = new SaveFileDialog()
- {
- FileName = fName,
- Filter = ViewHelpers.ModpackFileFilter,
- InitialDirectory = dir,
- };
- if (sfd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
- {
- return;
- }
- var newPath = sfd.FileName;
- await data.WriteModpack(newPath);
- }
- catch (Exception ex)
- {
- ViewHelpers.ShowError("Modpack Upgrade Error", "An error occurred while upgrading the modpack:\n\n" + ex.Message);
- }
- finally
- {
- await mw.UnlockUi();
- }
- }
-
- public static async Task UpgradeModpack(string path, bool includePartials = true)
- {
- if (Directory.Exists(path))
- {
- path = Path.GetFullPath(Path.Combine(path, "meta.json"));
- }
-
- var data = await WizardData.FromModpack(path);
- var textureUpgradeTargets = new Dictionary();
-
- var allTextures = new HashSet();
-
- // First Round Upgrade -
- // This does models and base MTRLS only.
- foreach (var p in data.DataPages)
- {
- foreach (var g in p.Groups)
- {
- if (g == null) continue;
- foreach (var o in g.Options)
- {
- if (o.StandardData != null)
- {
- try
- {
- var missing = await EndwalkerUpgrade.UpdateEndwalkerFiles(o.StandardData.Files);
- foreach (var kv in missing)
- {
- if (!textureUpgradeTargets.ContainsKey(kv.Key))
- {
- textureUpgradeTargets.Add(kv.Key, kv.Value);
- }
- }
-
- var textures = o.StandardData.Files.Select(x => x.Key).Where(x => x.EndsWith(".tex"));
- allTextures.UnionWith(textures);
- }
- catch (Exception ex)
- {
- var mes = "An error occurred while updating Group: " + g.Name + " - Option: " + o.Name + "\n\n" + ex.Message; ;
- throw new Exception(mes);
- }
- }
- }
- }
- }
-
-
- // Second Round Upgrade - This does textures based on the collated upgrade information from the previous pass
- foreach (var p in data.DataPages)
- {
- foreach (var g in p.Groups)
- {
- if (g == null) continue;
- foreach (var o in g.Options)
- {
- if (o.StandardData != null)
- {
- try
- {
- await EndwalkerUpgrade.UpgradeRemainingTextures(o.StandardData.Files, textureUpgradeTargets);
- }
- catch (Exception ex)
- {
- var mes = "An error occurred while updating Group: " + g.Name + " - Option: " + o.Name + "\n\n" + ex.Message; ;
- throw new Exception(mes);
- }
- }
- }
- }
- }
-
-
- if (includePartials)
- {
- // Find all un-referenced textures.
- var unusedTextures = new HashSet(
- allTextures.Where(t =>
- !textureUpgradeTargets.Any(x =>
- x.Value.Files.ContainsValue(t)
- )));
-
-
- // Third Round Upgrade - This inspects as-of-yet unupgraded textures for possible jank-upgrades,
- // Which is to say, upgrades where we can infer their usage and pairing, but the base mtrl was not included.
- foreach (var p in data.DataPages)
- {
- foreach (var g in p.Groups)
- {
- if (g == null) continue;
- foreach (var o in g.Options)
- {
- if (o.StandardData != null)
- {
- var contained = unusedTextures.Where(x => o.StandardData.Files.ContainsKey(x));
- await EndwalkerUpgrade.UpdateUnclaimedHairTextures(contained.ToList(), "Unused", null, null, o.StandardData.Files);
-
- foreach (var possibleMask in contained)
- {
- await EndwalkerUpgrade.UpdateEyeMask(possibleMask, "Unused", null, null, o.StandardData.Files);
- }
- }
- }
- }
- }
- }
-
- return data;
- }
-
- public static async Task UpgradeModpack(string path, string newPath, bool includePartials = true)
- {
- var data = await UpgradeModpack(path, includePartials);
-
- await data.WriteModpack(newPath);
- }
- }
-}
diff --git a/FFXIV_TexTools/Helpers/ModpackUpgraderWrapper.cs b/FFXIV_TexTools/Helpers/ModpackUpgraderWrapper.cs
new file mode 100644
index 00000000..a0c5d9ef
--- /dev/null
+++ b/FFXIV_TexTools/Helpers/ModpackUpgraderWrapper.cs
@@ -0,0 +1,79 @@
+using FFXIV_TexTools.Properties;
+using FFXIV_TexTools.Views;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using xivModdingFramework.Helpers;
+
+namespace FFXIV_TexTools.Helpers
+{
+ internal class ModpackUpgraderWrapper
+ {
+ public static async Task UpgradeModpackPrompted(bool includePartials = true)
+ {
+ var mw = MainWindow.GetMainWindow();
+ var ofd = new OpenFileDialog()
+ {
+ Filter = ViewHelpers.LoadModpackFilter,
+ InitialDirectory = Path.GetFullPath(Settings.Default.ModPack_Directory),
+ };
+
+ if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
+ {
+ return;
+ }
+
+ var path = ofd.FileName;
+
+
+ await mw.LockUi("Upgrading Modpack");
+ try
+ {
+
+ var data = await xivModdingFramework.Mods.ModpackUpgrader.UpgradeModpack(path, includePartials);
+
+ var ext = Path.GetExtension(path);
+
+ var name = Path.GetFileNameWithoutExtension(path);
+ if (ext == ".json")
+ {
+ name = IOUtil.MakePathSafe(data.MetaPage.Name, false);
+ }
+
+ if (ext != ".ttmp2" && ext != ".pmp")
+ {
+ ext = ".pmp";
+ }
+
+
+ // Final Save location
+ var dir = Path.GetDirectoryName(path);
+ var fName = name + "_dt" + ext;
+ var sfd = new SaveFileDialog()
+ {
+ FileName = fName,
+ Filter = ViewHelpers.ModpackFileFilter,
+ InitialDirectory = dir,
+ };
+ if (sfd.ShowDialog() != System.Windows.Forms.DialogResult.OK)
+ {
+ return;
+ }
+ var newPath = sfd.FileName;
+ await data.WriteModpack(newPath);
+ }
+ catch (Exception ex)
+ {
+ ViewHelpers.ShowError("Modpack Upgrade Error", "An error occurred while upgrading the modpack:\n\n" + ex.Message);
+ }
+ finally
+ {
+ await mw.UnlockUi();
+ }
+ }
+ }
+}
diff --git a/FFXIV_TexTools/MainWindow.xaml.cs b/FFXIV_TexTools/MainWindow.xaml.cs
index 059c3da7..cfc6703f 100644
--- a/FFXIV_TexTools/MainWindow.xaml.cs
+++ b/FFXIV_TexTools/MainWindow.xaml.cs
@@ -14,7 +14,6 @@
// along with this program. If not, see .
using AutoUpdaterDotNET;
-using FFXIV_TexTools.Console;
using FFXIV_TexTools.Helpers;
using FFXIV_TexTools.Properties;
using FFXIV_TexTools.Resources;
@@ -404,11 +403,6 @@ public MainWindow(string[] args)
private async Task HandleArgs(string[] args)
{
- if(await ConsoleManager.HandleConsoleArgs(args))
- {
- return;
- }
-
OnlyImport(args[0]);
}
@@ -1828,7 +1822,6 @@ await Task.Run(async () =>
var res = (FlexibleMessageBox.Show("Successfully downloaded fresh index backups.\nWould you like to delete all mods and apply these backups/[Start Over]?.".L(), "Backup Download Success".L(), MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1));
if (res == System.Windows.Forms.DialogResult.Yes)
{
- _lockProgressController.SetTitle("Removing All Mods");
Menu_StartOver_Click(null, null);
}
}
@@ -2102,7 +2095,7 @@ private async void UpdateModpack_Click(object sender, RoutedEventArgs e)
// Should we use this setting here? Or just always upgrade for modpacks?
//includePartials = Settings.Default.FixPreDawntrailPartialOnImport;
- await ModpackUpgrader.UpgradeModpackPrompted(includePartials);
+ await ModpackUpgraderWrapper.UpgradeModpackPrompted(includePartials);
}
catch
{
diff --git a/FFXIV_TexTools/Models/PenumbraUpgradeStatus.cs b/FFXIV_TexTools/Models/PenumbraUpgradeStatus.cs
index 9227c8be..c98f2920 100644
--- a/FFXIV_TexTools/Models/PenumbraUpgradeStatus.cs
+++ b/FFXIV_TexTools/Models/PenumbraUpgradeStatus.cs
@@ -9,6 +9,7 @@
using xivModdingFramework.Helpers;
using FFXIV_TexTools.Helpers;
using System.Diagnostics;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Models
{
diff --git a/FFXIV_TexTools/Properties/launchSettings.json b/FFXIV_TexTools/Properties/launchSettings.json
new file mode 100644
index 00000000..39b4fd7d
--- /dev/null
+++ b/FFXIV_TexTools/Properties/launchSettings.json
@@ -0,0 +1,7 @@
+{
+ "profiles": {
+ "FFXIV_TexTools": {
+ "commandName": "Project"
+ }
+ }
+}
\ No newline at end of file
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/EditImcGroupWindow.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/EditImcGroupWindow.xaml.cs
index 038edc07..79c504dc 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/EditImcGroupWindow.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/EditImcGroupWindow.xaml.cs
@@ -20,6 +20,7 @@
using xivModdingFramework.Cache;
using xivModdingFramework.Items.Interfaces;
using xivModdingFramework.Variants.FileTypes;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views.Wizard
{
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/EditableOptionControl.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/EditableOptionControl.xaml.cs
index d994dd4c..4fe13969 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/EditableOptionControl.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/EditableOptionControl.xaml.cs
@@ -14,6 +14,7 @@
using System.Windows.Navigation;
using System.Windows.Shapes;
using xivModdingFramework.Mods.DataContainers;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views
{
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/ExportWizardWindow.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/ExportWizardWindow.xaml.cs
index a49e985b..fe32fa79 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/ExportWizardWindow.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/ExportWizardWindow.xaml.cs
@@ -37,6 +37,7 @@
using xivModdingFramework.Mods.FileTypes.PMP;
using xivModdingFramework.Mods.Interfaces;
using Image = SixLabors.ImageSharp.Image;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views
{
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/ImcOptionRow.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/ImcOptionRow.xaml.cs
index 88222283..1079b596 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/ImcOptionRow.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/ImcOptionRow.xaml.cs
@@ -16,6 +16,7 @@
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using UserControl = System.Windows.Controls.UserControl;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views.Wizard
{
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/ImportWizardWindow.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/ImportWizardWindow.xaml.cs
index 3ce0386d..93247cb4 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/ImportWizardWindow.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/ImportWizardWindow.xaml.cs
@@ -38,6 +38,8 @@
using xivModdingFramework.Mods.DataContainers;
using xivModdingFramework.Mods.FileTypes;
using xivModdingFramework.Mods.FileTypes.PMP;
+using xivModdingFramework.Mods;
+
namespace FFXIV_TexTools.Views.Wizard
{
///
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditorWindow.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditorWindow.xaml.cs
index 520ce1fb..3a222578 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditorWindow.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditorWindow.xaml.cs
@@ -8,6 +8,7 @@
using System.Windows.Controls;
using System.Windows.Input;
using xivModdingFramework.Mods.FileTypes;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views.Wizard
{
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/WizardData.cs b/FFXIV_TexTools/Views/ModPack/Wizard/WizardData.cs
deleted file mode 100644
index 9c3e79bc..00000000
--- a/FFXIV_TexTools/Views/ModPack/Wizard/WizardData.cs
+++ /dev/null
@@ -1,1527 +0,0 @@
-using SixLabors.ImageSharp.Formats.Png;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using xivModdingFramework.Cache;
-using xivModdingFramework.General;
-using xivModdingFramework.General.DataContainers;
-using xivModdingFramework.Helpers;
-using xivModdingFramework.Mods;
-using xivModdingFramework.Mods.DataContainers;
-using xivModdingFramework.Mods.FileTypes;
-using xivModdingFramework.Mods.FileTypes.PMP;
-using xivModdingFramework.SqPack.FileTypes;
-using xivModdingFramework.Variants.DataContainers;
-using xivModdingFramework.Variants.FileTypes;
-using static xivModdingFramework.Mods.FileTypes.TTMP;
-using Image = SixLabors.ImageSharp.Image;
-
-// ================================ //
-
-/// This file contains all of the Data Classes for the Advanced Modpack Wizard, both import and export.
-/// This, thus, also contains all of the logic necessary for generating these classes from the
-/// TTMP or PMP Json formats, and converting them back into those formats/into TTMP/PMP files.
-///
-/// This borders a little on being too heavy to maintain as a TexTools class, but inevitably the
-/// UI Model in this case are very close to the final product by definition, and difficult to break
-/// apart without adding layers of inefficiency and redundancy that would make maintaining them even worse.
-
-// ================================ //
-
-namespace FFXIV_TexTools.Views.Wizard
-{
- public enum EOptionType
- {
- Single,
- Multi
- };
-
- public enum EGroupType
- {
- Standard,
- Imc
- };
-
- internal static class WizardHelpers
- {
- public static string WriteImage(string currentPath, string tempFolder, string newName) {
-
- if (string.IsNullOrWhiteSpace(currentPath))
- {
- return "";
- }
-
- if (!File.Exists(currentPath))
- {
- return "";
- }
-
- var path = Path.Combine("images", newName + ".png");
-
- var img = SixLabors.ImageSharp.Image.Load(currentPath);
- var fName = Path.Combine(tempFolder, path);
- var dir = Path.GetDirectoryName(fName);
- Directory.CreateDirectory(dir);
-
- using var fs = File.OpenWrite(fName);
- var enc = new PngEncoder();
- enc.BitDepth = PngBitDepth.Bit16;
- img.Save(fs, enc);
-
- return path;
- }
-
- }
-
- public class WizardStandardOptionData : WizardOptionData
- {
- public Dictionary Files = new Dictionary();
-
- public List Manipulations = new List();
-
- public int Priority;
-
- protected override bool CheckHasData()
- {
- return Files.Count > 0 || Manipulations.Count > 0;
- }
-
- private static List ManipulationTypeSortOrder = new List()
- {
- "Imc",
- "Eqp",
- "Eqdp",
- "Est",
- "Gmp",
- "Rsp",
- "GlobalEqp"
- };
-
- public void SortManipulations()
- {
- Manipulations.Sort((a,b) =>
- {
- if(a == null || b == null)
- {
- return -1;
- }
- var aType = ManipulationTypeSortOrder.IndexOf(a.Type);
- var bType = ManipulationTypeSortOrder.IndexOf(b.Type);
-
- aType = aType < 0 ? int.MaxValue : aType;
- bType = bType < 0 ? int.MaxValue : bType;
-
- if(aType != bType)
- {
- return aType - bType;
- }
-
- if(a.Type == "Rsp")
- {
- var aRsp = a.GetManipulation() as PMPRspManipulationJson;
- var bRsp = b.GetManipulation() as PMPRspManipulationJson;
-
- var aVal = (int)aRsp.GetRaceGenderHash();
- var bVal = (int)bRsp.GetRaceGenderHash();
-
- aVal = aVal * 100 + (int)aRsp.Attribute;
- bVal = bVal * 100 + (int)aRsp.Attribute;
- return aVal - bVal;
- }
-
- var aIm = a.GetManipulation() as IPMPItemMetadata;
- var bIm = b.GetManipulation() as IPMPItemMetadata;
-
- if(aIm == null)
- {
- // No defined order for non Item, Non-Rsp types.
- return 0;
- }
-
- // Sort by root information.
- var diff = GetSortOrder(aIm) - GetSortOrder(bIm);
- if(diff != 0 || a.Type != "Imc")
- {
- return diff;
- }
-
- var aImc = a.GetManipulation() as PMPImcManipulationJson;
- var bImc = b.GetManipulation() as PMPImcManipulationJson;
-
- if(aImc == null)
- {
- return 0;
- }
-
- return (int)aImc.Variant - (int)bImc.Variant;
- });
- }
-
- private static int GetSortOrder (IPMPItemMetadata manipulation)
- {
- //Generic sort-order resolver for root using manipulations.
- var root = manipulation.GetRoot();
-
- // 6x shift
- var val = (int)root.Info.PrimaryType * 1000000;
-
- // 2x shift
- val += root.Info.PrimaryId * 100;
-
- // 0x shift
- if (root.Info.Slot == null || !Imc.SlotOffsetDictionary.ContainsKey(root.Info.Slot))
- {
- val += 0;
- }
- else
- {
- val += (Imc.SlotOffsetDictionary.Keys.ToList().IndexOf(root.Info.Slot) + 1);
- }
-
- return val;
- }
- }
-
- public class WizardImcOptionData : WizardOptionData
- {
- public bool IsDisableOption;
- public ushort AttributeMask;
-
- protected override bool CheckHasData()
- {
- if(AttributeMask > 0 || IsDisableOption)
- {
- return true;
- }
- return false;
- }
- }
-
- public class WizardOptionData
- {
-
- public bool HasData
- {
- get
- {
- return CheckHasData();
- }
- }
-
- protected virtual bool CheckHasData()
- {
- return false;
- }
-
- }
-
- ///
- /// Class representing a single, clickable [Option],
- /// Aka a Radio Button or Checkbox the user can select, that internally resolves
- /// to a single list of files to be imported.
- ///
- public class WizardOptionEntry : INotifyPropertyChanged
- {
- public string Name { get; set; }
- public string Description { get; set; }
- public string Image { get; set; }
-
- public string NoDataIndicator
- {
- get
- {
- if (HasData)
- {
- return "";
- }
-
- if(!_Group.Options.Any(x => x.HasData))
- {
- // Group needs valid data.
- return " (Empty)";
- }
-
- if(OptionType == EOptionType.Single)
- {
- if(_Group.Options.FirstOrDefault(x => !x.HasData) == this)
- {
- // First empty is preserved in single select.
- return "";
- }
- }
-
- return " (Empty)";
- }
- }
-
- public bool HasData
- {
- get {
-
- if(_Group.ModOption != null)
- {
- // Read mode.
- return true;
- }
-
- if (StandardData != null)
- {
- return StandardData.HasData;
- } else if(ImcData != null)
- {
- return ImcData.HasData;
- }
- return false;
- }
- }
-
-
- private bool _Selected;
- public bool Selected
- {
- get
- {
- return _Selected;
- }
- set
- {
- if (_Selected == value) return;
- _Selected = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Selected)));
-
- var index = _Group.Options.IndexOf(this);
- if (index < 0) return;
-
- if(GroupType == EGroupType.Imc && Selected == true)
- {
- if (ImcData.IsDisableOption)
- {
- foreach(var opt in _Group.Options)
- {
- if(!opt.ImcData.IsDisableOption)
- {
- opt.Selected = false;
- }
- }
- }
- else
- {
- foreach (var opt in _Group.Options)
- {
- if (opt.ImcData.IsDisableOption)
- {
- opt.Selected = false;
- }
- }
- }
- }
- }
- }
-
- private WizardGroupEntry _Group;
-
- // Group name is used by the UI template binding for establishing radio button groupings.
- public string GroupName
- {
- get
- {
- return _Group.Name;
- }
- }
-
- // Option type is used by the UI template binding to determine template type.
- public EOptionType OptionType
- {
- get
- {
- return _Group.OptionType;
- }
- }
- public EGroupType GroupType
- {
- get
- {
- return _Group.GroupType;
- }
- }
-
- private WizardOptionData _Data = new WizardStandardOptionData();
-
- public WizardImcOptionData ImcData
- {
- get
- {
- if(GroupType == EGroupType.Imc)
- {
- return _Data as WizardImcOptionData;
- } else
- {
- return null;
- }
- }
- set
- {
- if(GroupType == EGroupType.Imc)
- {
- _Data = value;
- }
- }
- }
-
- public WizardStandardOptionData StandardData
- {
- get
- {
- if (GroupType == EGroupType.Standard)
- {
- return _Data as WizardStandardOptionData;
- }
- else
- {
- return null;
- }
- }
- set
- {
- if (GroupType == EGroupType.Standard)
- {
- _Data = value;
- }
- }
- }
-
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- public WizardOptionEntry(WizardGroupEntry owningGroup)
- {
- _Group = owningGroup;
- }
-
- public async Task ToModOption()
- {
- Image img = null;
- if (!string.IsNullOrWhiteSpace(Image))
- {
- img = SixLabors.ImageSharp.Image.Load(Image);
- }
-
- var mo = new ModOption()
- {
- Description = Description,
- Name = Name,
- GroupName = _Group.Name,
- ImageFileName = Image,
- IsChecked = Selected,
- SelectionType = OptionType.ToString(),
- Image = img,
- };
-
- if(StandardData == null)
- {
- throw new NotImplementedException("TTMP Export does not support one or more of the selected Option types.");
- }
-
- foreach(var fkv in StandardData.Files)
- {
- var path = fkv.Key;
- var forceType2 = path.EndsWith(".atex");
- if (!File.Exists(fkv.Value.RealPath))
- {
- // Sometimes poorly behaved PMPs or Penumbra folders may have been used as a source,
- // where they are missing files that they claim to have.
- continue;
- }
- var data = await TransactionDataHandler.GetCompressedFile(fkv.Value, forceType2);
-
- var root = await XivCache.GetFirstRoot(path);
- var itemCategory = "Unknown";
- var itemName = "Unknown";
- if(root != null)
- {
- var item = root.GetFirstItem();
- if(item != null)
- {
- itemCategory = item.SecondaryCategory;
- itemName = item.Name;
- }
- }
-
- var mData = new ModData()
- {
- Name = itemName,
- Category = itemCategory,
- FullPath = path,
- ModDataBytes = data,
- };
- mo.Mods.Add(path, mData);
- }
-
- if (StandardData.Manipulations != null && StandardData.Manipulations.Count > 0) {
- // Readonly TX for retrieving base values.
- var tx = ModTransaction.BeginReadonlyTransaction();
- var manips = await PMP.ManipulationsToMetadata(this.StandardData.Manipulations, tx);
-
- foreach(var meta in manips.Metadatas)
- {
- // Need to convert these and add them to the file array.
- var item = meta.Root.GetFirstItem();
- var path = meta.Root.Info.GetRootFile();
- var mData = new ModData()
- {
- Name = item.Name,
- Category = item.SecondaryCategory,
- FullPath = path,
- ModDataBytes = await SmartImport.CreateCompressedFile(await ItemMetadata.Serialize(meta), true),
- };
- mo.Mods.Add(path, mData);
- }
-
- foreach(var rgsp in manips.Rgsps)
- {
- // Need to convert these and add them to the file array.
- var data = await SmartImport.CreateCompressedFile(rgsp.GetBytes(), true);
- var path = CMP.GetRgspPath(rgsp);
- var item = CMP.GetDummyItem(rgsp);
-
- var mData = new ModData()
- {
- Name = item.Name,
- Category = item.SecondaryCategory,
- FullPath = path,
- ModDataBytes = data,
- };
- mo.Mods.Add(path, mData);
- }
- }
-
-
- return mo;
- }
-
- public async Task ToPmpOption(string tempFolder, IEnumerable identifiers, string imageName)
- {
- PMPOptionJson op;
- if(GroupType == EGroupType.Imc)
- {
- if (ImcData.IsDisableOption)
- {
- var io = new PmpDisableImcOptionJson();
- op = io;
- io.IsDisableSubMod = true;
- } else
- {
- var io = new PmpImcOptionJson();
- op = io;
- io.AttributeMask = ImcData.AttributeMask;
- }
- op.Name = Name;
- op.Description = Description;
- } else
- {
- // This unpacks our deduplicated files as needed.
- op = await PMP.CreatePmpStandardOption(tempFolder, Name, Description, identifiers, StandardData.Manipulations, null, StandardData.Priority);
- }
-
- op.Image = WizardHelpers.WriteImage(Image, tempFolder, imageName);
-
-
- return op;
- }
- }
-
-
- public class WizardImcGroupData
- {
- public XivDependencyRoot Root = new XivDependencyRoot(new XivDependencyRootInfo()
- {
- PrimaryType = xivModdingFramework.Items.Enums.XivItemType.equipment,
- PrimaryId = 1,
- Slot = "top"
- });
- public ushort Variant;
- public XivImc BaseEntry = new XivImc();
- public bool AllVariants;
- }
-
- ///
- /// Class represnting a Group of options.
- /// Aka a collection of radio buttons or checkboxes.
- ///
- public class WizardGroupEntry
- {
- public string Name;
- public string Description;
- public string Image;
-
- // Int or Bitflag depending on OptionType.
- public int Selection
- {
- get
- {
- if(this.OptionType == EOptionType.Single)
- {
- var op = Options.FirstOrDefault(x => x.Selected);
- if(op == null)
- {
- return 0;
- }
- return Options.IndexOf(op);
- }
- else
- {
- var total = 0;
- for(int i = 0; i < Options.Count; i++)
- {
- if (Options[i].Selected)
- {
- uint mask = 1;
- uint shifted = mask << i;
- total |= (int)shifted;
- }
- }
- return total;
- }
- }
- }
-
- public EOptionType OptionType;
-
- public EGroupType GroupType
- {
- get
- {
- if(ImcData != null)
- {
- return EGroupType.Imc;
- }
- return EGroupType.Standard;
- }
- }
-
- public bool HasData
- {
- get
- {
- return Options.Any(x => x.HasData);
- }
- }
-
-
- public List Options = new List();
-
- ///
- /// Option Data for Penumbra style Imc-Mask Option Groups.
- ///
- public WizardImcGroupData ImcData = null;
-
- public int Priority;
-
- ///
- /// Handler to the base modpack option.
- /// Typically either a ModGroupJson or PMPGroupJson
- ///
- public object ModOption;
-
- public static async Task FromWizardGroup(ModGroupJson tGroup, string unzipPath, bool needsTexFix)
- {
- var group = new WizardGroupEntry();
- group.Options = new List();
- group.ModOption = tGroup;
-
- group.Name = tGroup.GroupName;
- group.OptionType = tGroup.SelectionType == "Single" ? EOptionType.Single : EOptionType.Multi;
-
- var mpdPath = Path.Combine(unzipPath, "TTMPD.mpd");
-
- foreach(var o in tGroup.OptionList)
- {
- var wizOp = new WizardOptionEntry(group);
- wizOp.Name = o.Name;
- wizOp.Description = o.Description;
- if(!String.IsNullOrWhiteSpace(o.ImagePath))
- {
- wizOp.Image = Path.Combine(unzipPath, o.ImagePath);
- }
- wizOp.Selected = o.IsChecked;
-
- var data = new WizardStandardOptionData();
-
- foreach(var mj in o.ModsJsons)
- {
- var finfo = new FileStorageInformation()
- {
- StorageType = EFileStorageType.CompressedBlob,
- FileSize = mj.ModSize,
- RealOffset = mj.ModOffset,
- RealPath = mpdPath
- };
-
- // Data may not be unzipped here if we're in import mode.
- if (File.Exists(finfo.RealPath))
- {
- if (mj.FullPath.EndsWith(".meta") || mj.FullPath.EndsWith(".rgsp"))
- {
- var raw = await TransactionDataHandler.GetUncompressedFile(finfo);
- if (mj.FullPath.EndsWith(".meta"))
- {
- var meta = await ItemMetadata.Deserialize(raw);
- data.Manipulations.AddRange(PMPExtensions.MetadataToManipulations(meta));
- }
- else
- {
- var rgsp = new RacialGenderScalingParameter(raw);
- data.Manipulations.AddRange(PMPExtensions.RgspToManipulations(rgsp));
- }
- }
- else
- {
- if (needsTexFix && mj.FullPath.EndsWith(".tex"))
- {
- try
- {
- finfo = await TTMP.FixOldTexData(finfo);
- }
- catch(Exception ex)
- {
- Trace.WriteLine(ex);
- // File majorly broken, skip it.
- continue;
- }
- } else if(needsTexFix && mj.FullPath.EndsWith(".mdl"))
- {
- try
- {
- // Have to fix old busted models.
- finfo = await EndwalkerUpgrade.FixOldModel(finfo);
- }
- catch (Exception ex)
- {
- Trace.WriteLine(ex);
- // Hmm... What should we do about this?
- // Skip the file?
- continue;
- }
- }
-
- data.Files.Add(mj.FullPath, finfo);
- }
- }
- }
-
- wizOp.StandardData = data;
-
-
-
- group.Options.Add(wizOp);
- }
-
- if(group.Options.Count == 0)
- {
- // Empty group.
- return null;
- }
-
- if(group.OptionType == EOptionType.Single && !group.Options.Any(x => x.Selected))
- {
- group.Options[0].Selected = true;
- }
-
- return group;
- }
-
- public static async Task FromPMPGroup(PMPGroupJson pGroup, string unzipPath)
- {
- var group = new WizardGroupEntry();
- group.Options = new List();
- group.ModOption = pGroup;
-
- group.OptionType = pGroup.Type == "Single" ? EOptionType.Single : EOptionType.Multi;
- group.Name = pGroup.Name;
- group.Priority = pGroup.Priority;
-
- if (!string.IsNullOrWhiteSpace(pGroup.Image))
- {
- group.Image = Path.Combine(unzipPath, pGroup.Image);
- } else
- {
- group.Image = "";
- }
-
- group.Description = pGroup.Description;
-
- var imcGroup = pGroup as PMPImcGroupJson;
- if (imcGroup != null)
- {
- group.ImcData = new WizardImcGroupData()
- {
- Variant = imcGroup.Identifier.Variant,
- Root = imcGroup.Identifier.GetRoot(),
- BaseEntry = imcGroup.DefaultEntry.ToXivImc(),
- AllVariants = imcGroup.AllVariants,
- };
- }
-
- var idx = 0;
- foreach(var o in pGroup.Options)
- {
- var wizOp = new WizardOptionEntry(group);
- wizOp.Name = o.Name;
- wizOp.Description = o.Description;
- wizOp.Image = null;
-
- if (group.OptionType == EOptionType.Single)
- {
- wizOp.Selected = pGroup.DefaultSettings == idx;
- }
- else
- {
- var bit = 1 << idx;
- wizOp.Selected = (pGroup.DefaultSettings & bit) != 0;
- }
- group.Options.Add(wizOp);
-
- if(group.GroupType == EGroupType.Standard)
- {
- var data = await PMP.UnpackPmpOption(o, null, unzipPath, false);
- wizOp.StandardData.Files = data.Files;
- wizOp.StandardData.Manipulations = data.OtherManipulations;
- var sop = o as PmpStandardOptionJson;
- if (sop != null) {
- wizOp.StandardData.Priority = sop.Priority;
- }
-
- } else if(group.GroupType == EGroupType.Imc)
- {
- var imcData = new WizardImcOptionData();
- var imcOp = o as PmpImcOptionJson;
- if (imcOp != null)
- {
- imcData.IsDisableOption = false;
- imcData.AttributeMask = imcOp.AttributeMask;
- }
- var defOp = o as PmpDisableImcOptionJson;
- if(defOp != null)
- {
- imcData.IsDisableOption = defOp.IsDisableSubMod;
- imcData.AttributeMask = 0;
- }
- wizOp.ImcData = imcData;
- }
-
- if (!string.IsNullOrWhiteSpace(o.Image))
- {
- wizOp.Image = Path.Combine(unzipPath, o.Image);
- }
- else
- {
- wizOp.Image = "";
- }
-
- idx++;
- }
-
- if (group.Options.Count == 0)
- {
- // Empty group.
- return null;
- }
-
- if (group.OptionType == EOptionType.Single && !group.Options.Any(x => x.Selected))
- {
- group.Options[0].Selected = true;
- }
-
-
- return group;
- }
-
- public async Task ToModGroup()
- {
- if(this.ImcData != null)
- {
- throw new InvalidDataException("TTMP Does not support IMC Groups.");
- }
-
- var mg = new ModGroup()
- {
- GroupName = Name,
- OptionList = new List(),
- SelectionType = OptionType.ToString(),
- };
-
- foreach(var option in Options)
- {
- var tOpt = await option.ToModOption();
- mg.OptionList.Add(tOpt);
- }
-
- return mg;
- }
-
- public async Task ToPmpGroup(string tempFolder, string groupPrefix, Dictionary> identifiers, int page, bool oneOption = false)
- {
- var pg = new PMPGroupJson();
-
- if (this.ImcData != null)
- {
- var imcG = new PMPImcGroupJson();
- pg = imcG;
- pg.Type = "Imc";
-
- imcG.Identifier = PmpIdentifierJson.FromRoot(ImcData.Root.Info, ImcData.Variant);
- imcG.DefaultEntry = PMPImcManipulationJson.PMPImcEntry.FromXivImc(ImcData.BaseEntry);
- imcG.AllVariants = ImcData.AllVariants;
- }
- else
- {
- pg.Type = OptionType.ToString();
- }
-
- pg.Name = Name.Trim();
- pg.Description = Description;
- pg.Options = new List();
- pg.Priority = Priority;
- pg.DefaultSettings = Selection;
- pg.SelectedSettings = Selection;
- pg.Page = page;
-
- pg.Image = WizardHelpers.WriteImage(Image, tempFolder, IOUtil.MakePathSafe(Name));
-
- foreach (var option in Options)
- {
- option.Name = option.Name.Trim();
- var optionPrefix = WizardData.MakeOptionPrefix(groupPrefix, this, option);
- var imgName = optionPrefix;
- if(imgName.Length > 0)
- {
- // Remove trailing slash
- imgName = imgName.Substring(0, imgName.Length - 1);
- }
-
- if (oneOption)
- {
- imgName = "default_image";
- }
- identifiers.TryGetValue(optionPrefix, out var files);
- var opt = await option.ToPmpOption(tempFolder, files, imgName);
- pg.Options.Add(opt);
- }
-
- return pg;
- }
- }
-
- ///
- /// Class representing a Page of Groups.
- ///
- public class WizardPageEntry
- {
- public string Name;
- public List Groups = new List();
-
- public bool HasData
- {
- get
- {
- return Groups.Any(x => x.HasData);
- }
- }
-
- public static async Task FromWizardModpackPage(ModPackPageJson jp, string unzipPath, bool needsTexFix)
- {
- var page = new WizardPageEntry();
- page.Name = "Page " + (jp.PageIndex + 1);
-
- page.Groups = new List();
- foreach(var p in jp.ModGroups)
- {
- var g = await WizardGroupEntry.FromWizardGroup(p, unzipPath, needsTexFix);
- if (g == null) continue;
- page.Groups.Add(g);
- }
- return page;
- }
-
- public async Task ToModPackPage(int index)
- {
- var mpp = new ModPackData.ModPackPage() {
- PageIndex = index,
- ModGroups = new List(),
- };
-
- foreach(var group in Groups)
- {
- var mpg = await group.ToModGroup();
- mpp.ModGroups.Add(mpg);
- }
-
- return mpp;
- }
- }
-
- ///
- /// Class representing the description/cover page of a Modpack.
- ///
- public class WizardMetaEntry
- {
- public string Name = "";
- public string Author = "";
- public string Description = "";
- public string Url = "";
- public string Version = "1.0";
- public string Image = "";
-
- public static WizardMetaEntry FromPMP(PMPJson pmp, string unzipPath)
- {
- var meta = pmp.Meta;
- var page = new WizardMetaEntry();
- page.Url = meta.Website;
- page.Version = meta.Version;
- page.Author = meta.Author;
- page.Description = meta.Description;
- page.Name = meta.Name;
-
-
- if (!string.IsNullOrWhiteSpace(meta.Image))
- {
- page.Image = Path.Combine(unzipPath, meta.Image);
- }
- else
- {
- page.Image = "";
- var hImage = pmp.GetHeaderImage();
- if (!string.IsNullOrWhiteSpace(hImage))
- {
- meta.Image = Path.Combine(unzipPath, hImage);
- }
- }
- return page;
- }
-
- public static WizardMetaEntry FromTtmp(ModPackJson wiz, string unzipPath)
- {
- var page = new WizardMetaEntry();
- page.Url = wiz.Url;
- page.Name = wiz.Name;
- page.Version = wiz.Version;
- page.Author = wiz.Author;
- page.Description = wiz.Description;
-
- var img = wiz.GetHeaderImagePath();
- if (!string.IsNullOrWhiteSpace(img))
- {
- page.Image = Path.Combine(unzipPath, img);
- }
-
-
- return page;
- }
- }
-
-
- ///
- /// The full set of data necessary to render and display a wizard modpack install.
- ///
- public class WizardData
- {
- public WizardMetaEntry MetaPage = new WizardMetaEntry();
- public List DataPages = new List();
- public EModpackType ModpackType;
- public ModPack ModPack;
-
- public bool HasData
- {
- get
- {
- return DataPages.Any(x => x.HasData);
- }
- }
-
- ///
- /// Original source this Wizard Data was generated from.
- /// Null if the user created a fresh modpack in the UI.
- ///
- public object RawSource;
-
- public static async Task FromPmp(PMPJson pmp, string unzipPath)
- {
- var data = new WizardData();
- data.MetaPage = WizardMetaEntry.FromPMP(pmp, unzipPath);
- data.DataPages = new List();
- data.ModpackType = EModpackType.Pmp;
-
- var mp = new ModPack(null);
- mp.Author = data.MetaPage.Author;
- mp.Version = data.MetaPage.Version;
- mp.Name = data.MetaPage.Name;
- mp.Url = data.MetaPage.Url;
- data.ModPack = mp;
- data.RawSource = pmp;
-
- if (pmp.Groups.Count > 0 && pmp.Groups.Any(x => x.Options.Count > 0))
- {
- // Create sufficient pages.
- var pageMax = pmp.Groups.Max(x => x.Page);
- for (int i = 0; i <= pageMax; i++)
- {
- var page = new WizardPageEntry();
- page.Name = "Page " + (i+1).ToString();
- page.Groups = new List();
- data.DataPages.Add(page);
- }
-
- // Assign groups to pages.
- foreach (var g in pmp.Groups)
- {
- var page = data.DataPages[g.Page];
- page.Groups.Add(await WizardGroupEntry.FromPMPGroup(g, unzipPath));
- }
- } else
- {
-
- // Just drum up a basic group containing the default option.
- var fakeGroup = new PMPGroupJson();
- fakeGroup.Name = "Default";
- fakeGroup.Options = new List() { pmp.DefaultMod };
- fakeGroup.SelectedSettings = 1;
- fakeGroup.Type = "Single";
-
- if (string.IsNullOrWhiteSpace(pmp.DefaultMod.Name))
- {
- pmp.DefaultMod.Name = "Default";
- }
-
- var page = new WizardPageEntry();
- page.Name = "Page 1";
- page.Groups = new List();
- page.Groups.Add(await WizardGroupEntry.FromPMPGroup(fakeGroup, unzipPath));
- data.DataPages.Add(page);
- }
- return data;
- }
-
- public static async Task FromWizardTtmp(ModPackJson mpl, string unzipPath)
- {
- var data = new WizardData();
- data.ModpackType = EModpackType.TtmpWizard;
- data.MetaPage = WizardMetaEntry.FromTtmp(mpl, unzipPath);
-
- var mp = new ModPack(null);
- mp.Author = data.MetaPage.Author;
- mp.Version = data.MetaPage.Version;
- mp.Name = data.MetaPage.Name;
- mp.Url = data.MetaPage.Url;
- data.ModPack = mp;
- data.RawSource = mpl;
-
- var needsTexFix = TTMP.DoesModpackNeedTexFix(mpl);
-
-
- data.DataPages = new List();
- foreach (var p in mpl.ModPackPages)
- {
- data.DataPages.Add(await WizardPageEntry.FromWizardModpackPage(p, unzipPath, needsTexFix));
- }
- return data;
- }
- public static async Task FromSimpleTtmp(ModPackJson mpl, string unzipPath)
- {
- var data = new WizardData();
- data.ModpackType = EModpackType.TtmpWizard;
- data.MetaPage = WizardMetaEntry.FromTtmp(mpl, unzipPath);
-
- var mp = new ModPack(null);
- mp.Author = data.MetaPage.Author;
- mp.Version = data.MetaPage.Version;
- mp.Name = data.MetaPage.Name;
- mp.Url = data.MetaPage.Url;
- data.ModPack = mp;
- data.RawSource = mpl;
-
- var needsTexFix = TTMP.DoesModpackNeedTexFix(mpl);
-
- // Create a fake page/group.
- data.DataPages = new List();
- var page = new WizardPageEntry()
- {
- Groups = new List(),
- Name = "Page 1"
- };
- data.DataPages.Add(page);
-
- var mgj = new ModGroupJson()
- {
- GroupName = "Default Group",
- SelectionType = "Single",
- OptionList = new List()
- {
- new ModOptionJson()
- {
- Name = "Default Option",
- IsChecked = true,
- SelectionType = "Single",
- GroupName = "Default Group",
- ModsJsons = mpl.SimpleModsList,
- },
- },
- };
-
- var g = await WizardGroupEntry.FromWizardGroup(mgj, unzipPath, needsTexFix);
- page.Groups.Add(g);
- return data;
- }
-
-
- public void ClearEmpties()
- {
- var pages = DataPages.ToList();
- foreach(var p in pages)
- {
- if (!p.HasData)
- {
- DataPages.Remove(p);
- continue;
- }
-
- var groups = p.Groups.ToList();
- foreach(var g in groups)
- {
- if (g == null || !g.HasData)
- {
- p.Groups.Remove(g);
- continue;
- }
-
- var options = g.Options.ToList();
- var firstEmpty = false;
- foreach (var o in options)
- {
- if (!o.HasData)
- {
- if (!firstEmpty && g.OptionType == EOptionType.Single)
- {
- // Allow one empty option for single selects.
- firstEmpty = true;
- continue;
- }
- g.Options.Remove(o);
- continue;
- }
- }
- }
- }
-
- }
-
- public async Task WriteModpack(string targetPath)
- {
- if (targetPath.ToLower().EndsWith(".pmp"))
- {
- await WritePmp(targetPath);
- }
- else if(targetPath.ToLower().EndsWith(".ttmp2"))
- {
- await WriteWizardPack(targetPath);
- }
- else if (Directory.Exists(targetPath) || !Path.GetFileName(targetPath).Contains("."))
- {
- Directory.CreateDirectory(targetPath);
- await WritePmp(targetPath, false);
- }
- else
- {
- throw new ArgumentException("Invalid Modpack Path: " + targetPath);
- }
- }
- public async Task WriteWizardPack(string targetPath)
- {
- ClearEmpties();
-
- Version.TryParse(MetaPage.Version, out var ver);
-
- ver ??= new Version("1.0");
- var modPackData = new ModPackData()
- {
- Name = MetaPage.Name,
- Author = MetaPage.Author,
- Url = MetaPage.Url,
- Version = ver,
- Description = MetaPage.Description,
- ModPackPages = new List(),
- };
-
- int i = 0;
- foreach(var page in DataPages)
- {
- if (!page.HasData)
- {
- continue;
- }
- modPackData.ModPackPages.Add(await page.ToModPackPage(i));
- i++;
- }
-
- await TTMP.CreateWizardModPack(modPackData, targetPath, null, true);
- }
-
- private string MakePagePrefix(WizardPageEntry page)
- {
- var pagePrefix = "";
- if (DataPages.Count > 1)
- {
- var pIdx = DataPages.IndexOf(page);
- pagePrefix = "p" + pIdx + "/";
- }
- else if (page.Groups.Count == 1)
- {
- return "";
- }
- return pagePrefix;
- }
- private string MakeGroupPrefix(WizardPageEntry page, WizardGroupEntry group)
- {
- var pagePrefix = MakePagePrefix(page);
- if(page.Groups.Count == 1)
- {
- return pagePrefix;
- }
- var optionPrefix = pagePrefix + IOUtil.MakePathSafe(group.Name) + "/";
- return optionPrefix;
- }
- private string MakeOptionPrefix(WizardPageEntry page, WizardGroupEntry group, WizardOptionEntry option)
- {
- return MakeOptionPrefix(MakeGroupPrefix(page, group), group, option);
- }
- internal static string MakeOptionPrefix(string groupPrefix, WizardGroupEntry group, WizardOptionEntry option)
- {
- if (group.Options.Count > 1)
- {
- return groupPrefix + IOUtil.MakePathSafe(option.Name) + "/";
- }
- else
- {
- return groupPrefix;
- }
- }
-
- public async Task WritePmp(string targetPath, bool zip = true)
- {
- ClearEmpties();
-
- var pmp = new PMPJson()
- {
- DefaultMod = new PMPOptionJson(),
- Groups = new List(),
- Meta = new PMPMetaJson(),
- };
-
- var tempFolder = IOUtil.GetFrameworkTempSubfolder("PMP");
- Directory.CreateDirectory(tempFolder);
- try
- {
- Version.TryParse(MetaPage.Version, out var ver);
- ver ??= new Version("1.0");
-
- pmp.Meta.Name = MetaPage.Name;
- pmp.Meta.Author = MetaPage.Author;
- pmp.Meta.Website = MetaPage.Url;
- pmp.Meta.Description = MetaPage.Description;
- pmp.Meta.Version = ver.ToString();
- pmp.Meta.Tags = new List();
- pmp.Meta.FileVersion = PMP._WriteFileVersion;
- pmp.Meta.Image = WizardHelpers.WriteImage(MetaPage.Image, tempFolder, "_MetaImage");
-
- var optionCount = DataPages.Sum(p => p.Groups.Sum(x => x.Options.Count));
-
- // We need to compose a list of all the file storage information we're going to use.
- // Grouped by option folder.
- var allFiles = new Dictionary>();
- var pIdx = 1;
- foreach(var p in DataPages)
- {
- foreach (var g in p.Groups)
- {
- g.Name = g.Name.Trim();
- foreach (var o in g.Options)
- {
- if(o.GroupType != EGroupType.Standard)
- {
- continue;
- }
-
- var files = o.StandardData.Files;
-
- if(string.IsNullOrWhiteSpace(o.Name) || string.IsNullOrWhiteSpace(g.Name))
- {
- throw new InvalidDataException("PMP Files must have valid group and option names.");
- }
-
-
- var optionPrefix = MakeOptionPrefix(p, g, o);
-
- if (allFiles.ContainsKey(optionPrefix))
- {
- foreach(var f in files)
- {
- allFiles[optionPrefix].Add(f.Key, f.Value);
- }
- }
- else
- {
- allFiles.Add(optionPrefix, files);
- }
- }
- }
- pIdx++;
- }
-
- // These are de-duplicated internal write paths for the final PMP folder structure, coupled with
- // their file identifier and internal path information
- var identifiers = await FileIdentifier.IdentifierListFromDictionaries(allFiles);
-
- if(optionCount == 1)
- {
- pmp.DefaultMod = (await DataPages.First(x => x.Groups.Count > 0).Groups.First(x => x.Options.Count > 0).ToPmpGroup(tempFolder, "", identifiers, 0, true)).Options[0];
- } else
- {
- // This both constructs the JSON structure and writes our files to their
- // real location in the folder tree in the temp folder.
- var page = 0;
- foreach (var p in DataPages)
- {
- foreach (var g in p.Groups)
- {
- var gPrefix = MakeGroupPrefix(p, g);
- var pg = await g.ToPmpGroup(tempFolder, gPrefix, identifiers, page);
- pmp.Groups.Add(pg);
- }
- page++;
- }
- }
-
-
- // This performs the final json serialization/writing and zipping.
- if (zip)
- {
- await PMP.WritePmp(pmp, tempFolder, targetPath);
- } else
- {
- await PMP.WritePmp(pmp, tempFolder);
- Directory.CreateDirectory(targetPath);
- IOUtil.CopyFolder(tempFolder, targetPath);
- }
- }
- finally
- {
- IOUtil.DeleteTempDirectory(tempFolder);
- }
- }
-
- ///
- /// Updates the base Penumbra groups with the new user-selected values.
- ///
- public void FinalizePmpSelections()
- {
- // Need to go through and assign the Selected values back to the PMP.
- foreach(var p in DataPages)
- {
- foreach(var g in p.Groups)
- {
- var pg = (g.ModOption as PMPGroupJson);
- pg.SelectedSettings = g.Selection;
- }
- }
- }
-
-
- ///
- /// Returns the list of selected mod files that the TTMP importers expect, based on user selection(s).
- ///
- ///
- public List FinalizeTttmpSelections()
- {
- List modFiles = new List();
- // Need to go through and compile the final ModJson list.
- foreach (var p in DataPages)
- {
- foreach (var g in p.Groups)
- {
- var ttGroup = g.ModOption as ModGroupJson;
- if (ttGroup == null)
- {
- continue;
- }
-
- var selected = 0;
- for (int i = 0; i < g.Options.Count; i++)
- {
- var opt = g.Options[i];
- if (opt.Selected)
- {
- if (opt.GroupType == EGroupType.Standard)
- {
- if (opt.StandardData.Manipulations != null && opt.StandardData.Manipulations.Count > 0)
- {
- // We shouldn't actually be able to get to this path, but safety is good.
- throw new NotImplementedException("Importing TTMPs with Meta Manipulations is not supported. How did you get here though?");
- }
-
- var ttOpt = ttGroup.OptionList[i];
- modFiles.AddRange(ttOpt.ModsJsons);
- } else
- {
- // We shouldn't actually be able to get to this path, but safety is good.
- throw new NotImplementedException("Importing TTMPs with IMC Groups is not supported. How did you get here though?");
- }
- }
- }
- }
- }
-
- // Assign mod pack linkage that the framework expects.
- foreach(var mj in modFiles)
- {
- mj.ModPackEntry = ModPack;
- }
-
- return modFiles;
- }
-
-
- ///
- /// The simplest method for generating fully constructed wizard data.
- /// Unzips the entire modpack in the process.
- ///
- ///
- ///
- public static async Task FromModpack(string modpack)
- {
- return await Task.Run(async () =>
- {
- var modpackType = TTMP.GetModpackType(modpack);
-
- if (modpackType == TTMP.EModpackType.Pmp)
- {
- var pmp = await PMP.LoadPMP(modpack, false, true);
- return await WizardData.FromPmp(pmp.pmp, pmp.path);
- }
- else if (modpackType == TTMP.EModpackType.TtmpWizard)
- {
- var ttmp = await TTMP.UnzipTtmp(modpack);
- return await WizardData.FromWizardTtmp(ttmp.Mpl, ttmp.UnzipFolder);
- }
- else if (modpackType == TTMP.EModpackType.TtmpSimple || modpackType == TTMP.EModpackType.TtmpOriginal || modpackType == TTMP.EModpackType.TtmpBackup)
- {
- var ttmp = await TTMP.UnzipTtmp(modpack);
- return await WizardData.FromSimpleTtmp(ttmp.Mpl, ttmp.UnzipFolder);
- }
- return null;
- });
- }
- }
-}
diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/WizardPageControl.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/WizardPageControl.xaml.cs
index 2f0b03ac..6b81e646 100644
--- a/FFXIV_TexTools/Views/ModPack/Wizard/WizardPageControl.xaml.cs
+++ b/FFXIV_TexTools/Views/ModPack/Wizard/WizardPageControl.xaml.cs
@@ -29,6 +29,7 @@
using System.Windows.Media.Imaging;
using xivModdingFramework.Mods.DataContainers;
using Image = SixLabors.ImageSharp.Image;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views.Wizard
{
diff --git a/FFXIV_TexTools/Views/OnboardingWindow.xaml.cs b/FFXIV_TexTools/Views/OnboardingWindow.xaml.cs
index 2eb9ea11..0c7beec0 100644
--- a/FFXIV_TexTools/Views/OnboardingWindow.xaml.cs
+++ b/FFXIV_TexTools/Views/OnboardingWindow.xaml.cs
@@ -166,7 +166,7 @@ private void Done_Click(object sender, RoutedEventArgs e)
Settings.Default.Save();
DialogResult = true;
- System.Windows.Forms.Application.Restart();
+ MainWindow.GetMainWindow().Restart();
System.Windows.Application.Current.Shutdown();
}
@@ -189,6 +189,71 @@ public static void OnboardAndInitialize()
CheckRerunAdmin();
ValidateModlist();
}
+ public static void InitializeSettings()
+ {
+ SetDirectories();
+ XivCache.FrameworkSettings.DefaultTextureFormat = Settings.Default.CompressEndwalkerUpgradeTextures ? xivModdingFramework.Textures.Enums.XivTexFormat.BC7 : xivModdingFramework.Textures.Enums.XivTexFormat.A8R8G8B8;
+
+ if (Enum.TryParse(Settings.Default.ModelingTool, true, out var mt))
+ {
+ XivCache.FrameworkSettings.ModelingTool = mt;
+ }
+ XivCache.FrameworkSettings.DefaultTextureFormat = Settings.Default.CompressEndwalkerUpgradeTextures ? xivModdingFramework.Textures.Enums.XivTexFormat.BC7 : xivModdingFramework.Textures.Enums.XivTexFormat.A8R8G8B8;
+
+ UpdateConsoleConfig();
+ Properties.Settings.Default.SettingsSaving += (object sender, System.ComponentModel.CancelEventArgs e) => {
+ UpdateConsoleConfig();
+ };
+
+ }
+
+ private static void UpdateConsoleConfig()
+ {
+ ConsoleConfig.Update(x =>
+ {
+ x.XivPath = Settings.Default.FFXIV_Directory;
+ x.Language = Settings.Default.Application_Language;
+ });
+ }
+
+
+
+ ///
+ /// Validates the various directories TexTools relies on.
+ ///
+ private static void SetDirectories()
+ {
+ // Create/assign directories if they don't exist already.
+ SetSaveDirectory();
+
+ SetBackupsDirectory();
+
+ SetModPackDirectory();
+ }
+
+ private static void SetSaveDirectory()
+ {
+ if (!Directory.Exists(Properties.Settings.Default.Save_Directory))
+ {
+ Directory.CreateDirectory(Properties.Settings.Default.Save_Directory);
+ }
+ }
+
+ private static void SetBackupsDirectory()
+ {
+ if (!Directory.Exists(Properties.Settings.Default.Backup_Directory))
+ {
+ Directory.CreateDirectory(Properties.Settings.Default.Backup_Directory);
+ }
+ }
+
+ private static void SetModPackDirectory()
+ {
+ if (!Directory.Exists(Properties.Settings.Default.ModPack_Directory))
+ {
+ Directory.CreateDirectory(Properties.Settings.Default.ModPack_Directory);
+ }
+ }
public static void ValidateModlist()
{
@@ -241,7 +306,7 @@ public static bool CheckRerunAdminSimple()
}
return true;
}
- public static void CheckRerunAdmin()
+ public static void CheckRerunAdmin(bool throwErrors = false)
{
var cwd = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
var converterFolder = Path.GetFullPath(Path.Combine(cwd, "converters"));
@@ -255,6 +320,11 @@ public static void CheckRerunAdmin()
if (!allSuccess && !IsRunningAsAdministrator())
{
+ if (throwErrors)
+ {
+ throw new ApplicationException("Application must be run as administrator for proper file access due to current folder configurations.");
+ }
+
// Setting up start info of the new process of the same application
ProcessStartInfo processStartInfo = new ProcessStartInfo(Assembly.GetEntryAssembly().CodeBase);
@@ -362,19 +432,6 @@ public static string GetDefaultInstallDirectory()
return installDirectory;
}
- public static void InitializeSettings()
- {
- SetDirectories();
- XivCache.FrameworkSettings.DefaultTextureFormat = Settings.Default.CompressEndwalkerUpgradeTextures ? xivModdingFramework.Textures.Enums.XivTexFormat.BC7 : xivModdingFramework.Textures.Enums.XivTexFormat.A8R8G8B8;
-
- if (Enum.TryParse(Settings.Default.ModelingTool, true, out var mt))
- {
- XivCache.FrameworkSettings.ModelingTool = mt;
- }
- XivCache.FrameworkSettings.DefaultTextureFormat = Settings.Default.CompressEndwalkerUpgradeTextures ? xivModdingFramework.Textures.Enums.XivTexFormat.BC7 : xivModdingFramework.Textures.Enums.XivTexFormat.A8R8G8B8;
-
- }
-
public static bool IsGameDirectoryValid(string dir)
{
if (string.IsNullOrEmpty(dir))
@@ -396,45 +453,6 @@ public static bool IsGameDirectoryValid(string dir)
}
-
-
- ///
- /// Validates the various directories TexTools relies on.
- ///
- private static void SetDirectories()
- {
- // Create/assign directories if they don't exist already.
- SetSaveDirectory();
-
- SetBackupsDirectory();
-
- SetModPackDirectory();
- }
-
- private static void SetSaveDirectory()
- {
- if (!Directory.Exists(Properties.Settings.Default.Save_Directory))
- {
- Directory.CreateDirectory(Properties.Settings.Default.Save_Directory);
- }
- }
-
- private static void SetBackupsDirectory()
- {
- if (!Directory.Exists(Properties.Settings.Default.Backup_Directory))
- {
- Directory.CreateDirectory(Properties.Settings.Default.Backup_Directory);
- }
- }
-
- private static void SetModPackDirectory()
- {
- if (!Directory.Exists(Properties.Settings.Default.ModPack_Directory))
- {
- Directory.CreateDirectory(Properties.Settings.Default.ModPack_Directory);
- }
- }
-
///
/// Resolves a valid TexTools desired FFXIV folder from a given user folder, if at all possible.
///
diff --git a/FFXIV_TexTools/Views/Textures/EyeDiffuseCreator.xaml.cs b/FFXIV_TexTools/Views/Textures/EyeDiffuseCreator.xaml.cs
index ffd602c9..60ad8f6a 100644
--- a/FFXIV_TexTools/Views/Textures/EyeDiffuseCreator.xaml.cs
+++ b/FFXIV_TexTools/Views/Textures/EyeDiffuseCreator.xaml.cs
@@ -25,6 +25,7 @@
using SixLabors.ImageSharp;
using Point = SixLabors.ImageSharp.Point;
using xivModdingFramework.Helpers;
+using xivModdingFramework.Mods;
namespace FFXIV_TexTools.Views.Textures
{
diff --git a/ForceUpdateAssembly/ForceUpdateAssembly.csproj b/ForceUpdateAssembly/ForceUpdateAssembly.csproj
index beb71fe3..5539c433 100644
--- a/ForceUpdateAssembly/ForceUpdateAssembly.csproj
+++ b/ForceUpdateAssembly/ForceUpdateAssembly.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
@@ -9,7 +9,9 @@
A stub library with version 0.0.0.0 for use when forcing updates.
ForceUpdateAssembly
Branch Change Update
- Debug;Release;Endwalker Debug;Endwalker Release
+ Debug;Release
+ ..\\bin\\$(Configuration)\\lib\\
+ false
diff --git a/lib/xivModdingFramework b/lib/xivModdingFramework
index 20825119..9b3d171c 160000
--- a/lib/xivModdingFramework
+++ b/lib/xivModdingFramework
@@ -1 +1 @@
-Subproject commit 2082511953559dd9e6b0dd0a50fec6146305b1a5
+Subproject commit 9b3d171cf9af54403126255fc903eca174dd4bd1