Rework game path selection a bit.

This commit is contained in:
Ottermandias 2024-01-05 19:02:50 +01:00
parent 55f38865e3
commit c33545acdf
2 changed files with 93 additions and 49 deletions

View file

@ -15,7 +15,7 @@ public partial class ModEditWindow
public readonly MdlFile Mdl; public readonly MdlFile Mdl;
private readonly List<string>[] _attributes; private readonly List<string>[] _attributes;
public List<Utf8GamePath>? GamePaths { get; private set ;} public List<Utf8GamePath>? GamePaths { get; private set; }
public int GamePathIndex; public int GamePathIndex;
public bool PendingIo { get; private set; } = false; public bool PendingIo { get; private set; } = false;
@ -24,10 +24,12 @@ public partial class ModEditWindow
[GeneratedRegex(@"chara/(?:equipment|accessory)/(?'Set'[a-z]\d{4})/model/(?'Race'c\d{4})\k'Set'_[^/]+\.mdl", RegexOptions.Compiled)] [GeneratedRegex(@"chara/(?:equipment|accessory)/(?'Set'[a-z]\d{4})/model/(?'Race'c\d{4})\k'Set'_[^/]+\.mdl", RegexOptions.Compiled)]
private static partial Regex CharaEquipmentRegex(); private static partial Regex CharaEquipmentRegex();
[GeneratedRegex(@"chara/human/(?'Race'c\d{4})/obj/(?'Type'[^/]+)/(?'Set'[^/]\d{4})/model/(?'Race'c\d{4})\k'Set'_[^/]+\.mdl", RegexOptions.Compiled)] [GeneratedRegex(@"chara/human/(?'Race'c\d{4})/obj/(?'Type'[^/]+)/(?'Set'[^/]\d{4})/model/(?'Race'c\d{4})\k'Set'_[^/]+\.mdl",
RegexOptions.Compiled)]
private static partial Regex CharaHumanRegex(); private static partial Regex CharaHumanRegex();
[GeneratedRegex(@"chara/(?'SubCategory'demihuman|monster|weapon)/(?'Set'w\d{4})/obj/body/(?'Body'b\d{4})/model/\k'Set'\k'Body'.mdl", RegexOptions.Compiled)] [GeneratedRegex(@"chara/(?'SubCategory'demihuman|monster|weapon)/(?'Set'w\d{4})/obj/body/(?'Body'b\d{4})/model/\k'Set'\k'Body'.mdl",
RegexOptions.Compiled)]
private static partial Regex CharaBodyRegex(); private static partial Regex CharaBodyRegex();
public MdlTab(ModEditWindow edit, byte[] bytes, string path, Mod? mod) public MdlTab(ModEditWindow edit, byte[] bytes, string path, Mod? mod)
@ -54,21 +56,29 @@ public partial class ModEditWindow
/// <param name="mod"> Mod within which the .mdl is resolved. </param> /// <param name="mod"> Mod within which the .mdl is resolved. </param>
private void FindGamePaths(string path, Mod mod) private void FindGamePaths(string path, Mod mod)
{ {
if (!Path.IsPathRooted(path) && Utf8GamePath.FromString(path, out var p))
{
GamePaths = [p];
return;
}
PendingIo = true; PendingIo = true;
var task = Task.Run(() => { var task = Task.Run(() =>
{
// TODO: Is it worth trying to order results based on option priorities for cases where more than one match is found? // TODO: Is it worth trying to order results based on option priorities for cases where more than one match is found?
// NOTE: We're using case insensitive comparisons, as option group paths in mods are stored in lower case, but the mod editor uses paths directly from the file system, which may be mixed case. // NOTE: We're using case-insensitive comparisons, as option group paths in mods are stored in lower case, but the mod editor uses paths directly from the file system, which may be mixed case.
return mod.AllSubMods return mod.AllSubMods
.SelectMany(submod => submod.Files.Concat(submod.FileSwaps)) .SelectMany(m => m.Files.Concat(m.FileSwaps))
.Where(kv => kv.Value.FullName.Equals(path, StringComparison.OrdinalIgnoreCase)) .Where(kv => kv.Value.FullName.Equals(path, StringComparison.OrdinalIgnoreCase))
.Select(kv => kv.Key) .Select(kv => kv.Key)
.ToList(); .ToList();
}); });
task.ContinueWith(task => { task.ContinueWith(t =>
IoException = task.Exception?.ToString(); {
IoException = t.Exception?.ToString();
PendingIo = false; PendingIo = false;
GamePaths = task.Result; GamePaths = t.Result;
}); });
} }
@ -77,17 +87,21 @@ public partial class ModEditWindow
public void Export(string outputPath, Utf8GamePath mdlPath) public void Export(string outputPath, Utf8GamePath mdlPath)
{ {
SklbFile? sklb = null; SklbFile? sklb = null;
try { try
{
var sklbPath = GetSklbPath(mdlPath.ToString()); var sklbPath = GetSklbPath(mdlPath.ToString());
sklb = sklbPath != null ? ReadSklb(sklbPath) : null; sklb = sklbPath != null ? ReadSklb(sklbPath) : null;
} catch (Exception exception) { }
catch (Exception exception)
{
IoException = exception?.ToString(); IoException = exception?.ToString();
return; return;
} }
PendingIo = true; PendingIo = true;
_edit._models.ExportToGltf(Mdl, sklb, outputPath) _edit._models.ExportToGltf(Mdl, sklb, outputPath)
.ContinueWith(task => { .ContinueWith(task =>
{
IoException = task.Exception?.ToString(); IoException = task.Exception?.ToString();
PendingIo = false; PendingIo = false;
}); });
@ -146,7 +160,8 @@ public partial class ModEditWindow
FullPath path => File.ReadAllBytes(path.ToPath()), FullPath path => File.ReadAllBytes(path.ToPath()),
}; };
if (bytes == null) if (bytes == null)
throw new Exception($"Resolved skeleton path {sklbPath} could not be found. If modded, is it enabled in the current collection?"); throw new Exception(
$"Resolved skeleton path {sklbPath} could not be found. If modded, is it enabled in the current collection?");
return new SklbFile(bytes); return new SklbFile(bytes);
} }

View file

@ -20,6 +20,8 @@ public partial class ModEditWindow
private string _modelNewMaterial = string.Empty; private string _modelNewMaterial = string.Empty;
private readonly List<TagButtons> _subMeshAttributeTagWidgets = []; private readonly List<TagButtons> _subMeshAttributeTagWidgets = [];
private string _customPath = string.Empty;
private Utf8GamePath _customGamePath = Utf8GamePath.Empty;
private bool DrawModelPanel(MdlTab tab, bool disabled) private bool DrawModelPanel(MdlTab tab, bool disabled)
{ {
@ -51,10 +53,6 @@ public partial class ModEditWindow
private void DrawExport(MdlTab tab, bool disabled) private void DrawExport(MdlTab tab, bool disabled)
{ {
// IO on a disabled panel doesn't really make sense.
if (disabled)
return;
if (!ImGui.CollapsingHeader("Export")) if (!ImGui.CollapsingHeader("Export"))
return; return;
@ -70,16 +68,14 @@ public partial class ModEditWindow
DrawGamePathCombo(tab); DrawGamePathCombo(tab);
if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.", tab.PendingIo)) var gamePath = tab.GamePathIndex >= 0 && tab.GamePathIndex < tab.GamePaths.Count
? tab.GamePaths[tab.GamePathIndex]
: _customGamePath;
if (ImGuiUtil.DrawDisabledButton("Export to glTF", Vector2.Zero, "Exports this mdl file to glTF, for use in 3D authoring applications.",
tab.PendingIo || gamePath.IsEmpty))
_fileDialog.OpenSavePicker("Save model as glTF.", ".gltf", Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()),
".gltf", (valid, path) =>
{ {
var gamePath = tab.GamePaths[tab.GamePathIndex];
_fileDialog.OpenSavePicker(
"Save model as glTF.",
".gltf",
Path.GetFileNameWithoutExtension(gamePath.Filename().ToString()),
".gltf",
(valid, path) => {
if (!valid) if (!valid)
return; return;
@ -88,20 +84,48 @@ public partial class ModEditWindow
_mod!.ModPath.FullName, _mod!.ModPath.FullName,
false false
); );
}
if (tab.IoException != null) if (tab.IoException != null)
ImGuiUtil.TextWrapped(tab.IoException); ImGuiUtil.TextWrapped(tab.IoException);
return;
} }
private void DrawGamePathCombo(MdlTab tab) private void DrawGamePathCombo(MdlTab tab)
{ {
using var combo = ImRaii.Combo("Game Path", tab.GamePaths![tab.GamePathIndex].ToString()); if (tab.GamePaths!.Count == 0)
if (!combo) {
return; ImGui.TextUnformatted("No associated game path detected. Valid game paths are currently necessary for exporting.");
if (ImGui.InputTextWithHint("##customInput", "Enter custom game path...", ref _customPath, 256))
if (!Utf8GamePath.FromString(_customPath, out _customGamePath, false))
_customGamePath = Utf8GamePath.Empty;
return;
}
DrawComboButton(tab);
}
private static void DrawComboButton(MdlTab tab)
{
const string label = "Game Path";
var preview = tab.GamePaths![tab.GamePathIndex].ToString();
var labelWidth = ImGui.CalcTextSize(label).X + ImGui.GetStyle().ItemInnerSpacing.X;
var buttonWidth = ImGui.GetContentRegionAvail().X - labelWidth;
if (tab.GamePaths!.Count == 1)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.FrameBg))
.Push(ImGuiCol.ButtonHovered, ImGui.GetColorU32(ImGuiCol.FrameBgHovered))
.Push(ImGuiCol.ButtonActive, ImGui.GetColorU32(ImGuiCol.FrameBgActive));
using var group = ImRaii.Group();
ImGui.Button(preview, new Vector2(buttonWidth, 0));
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.TextUnformatted("Game Path");
}
else
{
ImGui.SetNextItemWidth(buttonWidth);
using var combo = ImRaii.Combo("Game Path", preview);
if (combo.Success)
foreach (var (path, index) in tab.GamePaths.WithIndex()) foreach (var (path, index) in tab.GamePaths.WithIndex())
{ {
if (!ImGui.Selectable(path.ToString(), index == tab.GamePathIndex)) if (!ImGui.Selectable(path.ToString(), index == tab.GamePathIndex))
@ -111,6 +135,11 @@ public partial class ModEditWindow
} }
} }
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
ImGui.SetClipboardText(preview);
ImGuiUtil.HoverTooltip("Right-Click to copy to clipboard.", ImGuiHoveredFlags.AllowWhenDisabled);
}
private bool DrawModelMaterialDetails(MdlTab tab, bool disabled) private bool DrawModelMaterialDetails(MdlTab tab, bool disabled)
{ {
if (!ImGui.CollapsingHeader("Materials")) if (!ImGui.CollapsingHeader("Materials"))