mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Flesh out skeleton path resolution a bit
This commit is contained in:
parent
d85cbd8051
commit
655e2fd2ca
1 changed files with 45 additions and 20 deletions
|
|
@ -21,10 +21,15 @@ public partial class ModEditWindow
|
||||||
public bool PendingIo { get; private set; } = false;
|
public bool PendingIo { get; private set; } = false;
|
||||||
public string? IoException { get; private set; } = null;
|
public string? IoException { get; private set; } = null;
|
||||||
|
|
||||||
// TODO: this can probably be genericised across all of chara
|
[GeneratedRegex(@"chara/(?:equipment|accessory)/(?'Set'[a-z]\d{4})/model/(?'Race'c\d{4})\k'Set'_[^/]+\.mdl", RegexOptions.Compiled)]
|
||||||
[GeneratedRegex(@"chara/equipment/e(?'Set'\d{4})/model/c(?'Race'\d{4})e\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)]
|
||||||
|
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)]
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
_edit = edit;
|
_edit = edit;
|
||||||
|
|
@ -71,16 +76,14 @@ public partial class ModEditWindow
|
||||||
/// <param name="outputPath"> Disk path to save the resulting file to. </param>
|
/// <param name="outputPath"> Disk path to save the resulting file to. </param>
|
||||||
public void Export(string outputPath, Utf8GamePath mdlPath)
|
public void Export(string outputPath, Utf8GamePath mdlPath)
|
||||||
{
|
{
|
||||||
// NOTES ON EST
|
SklbFile? sklb = null;
|
||||||
// for collection wide lookup;
|
try {
|
||||||
// Collections.Cache.EstCache::GetEstEntry
|
var sklbPath = GetSklbPath(mdlPath.ToString());
|
||||||
// Collections.Cache.MetaCache::GetEstEntry
|
sklb = sklbPath != null ? ReadSklb(sklbPath) : null;
|
||||||
// Collections.ModCollection.MetaCache?
|
} catch (Exception exception) {
|
||||||
// for default lookup, probably;
|
IoException = exception?.ToString();
|
||||||
// EstFile.GetDefault(...)
|
return;
|
||||||
|
}
|
||||||
var sklbPath = GetSklbPath(mdlPath.ToString());
|
|
||||||
var sklb = sklbPath != null ? ReadSklb(sklbPath) : null;
|
|
||||||
|
|
||||||
PendingIo = true;
|
PendingIo = true;
|
||||||
_edit._models.ExportToGltf(Mdl, sklb, outputPath)
|
_edit._models.ExportToGltf(Mdl, sklb, outputPath)
|
||||||
|
|
@ -94,15 +97,37 @@ public partial class ModEditWindow
|
||||||
/// <param name="mdlPath"> .mdl file to look up the skeleton for. </param>
|
/// <param name="mdlPath"> .mdl file to look up the skeleton for. </param>
|
||||||
private string? GetSklbPath(string mdlPath)
|
private string? GetSklbPath(string mdlPath)
|
||||||
{
|
{
|
||||||
// TODO: This needs to be drastically expanded, it's dodgy af rn
|
// Equipment is skinned to the base body skeleton of the race they target.
|
||||||
|
|
||||||
var match = CharaEquipmentRegex().Match(mdlPath);
|
var match = CharaEquipmentRegex().Match(mdlPath);
|
||||||
if (!match.Success)
|
if (match.Success)
|
||||||
return null;
|
{
|
||||||
|
var race = match.Groups["Race"].Value;
|
||||||
|
return $"chara/human/{race}/skeleton/base/b0001/skl_{race}b0001.sklb";
|
||||||
|
}
|
||||||
|
|
||||||
var race = match.Groups["Race"].Value;
|
// Some parts of human have their own skeletons.
|
||||||
|
match = CharaHumanRegex().Match(mdlPath);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var type = match.Groups["Type"].Value;
|
||||||
|
var race = match.Groups["Race"].Value;
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
"body" or "tail" => $"chara/human/{race}/skeleton/base/b0001/skl_{race}b0001.sklb",
|
||||||
|
_ => throw new Exception($"Currently unsupported human model type \"{type}\"."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return $"chara/human/c{race}/skeleton/base/b0001/skl_c{race}b0001.sklb";
|
// A few subcategories - such as weapons, demihumans, and monsters - have dedicated per-"body" skeletons.
|
||||||
|
match = CharaBodyRegex().Match(mdlPath);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var subCategory = match.Groups["SubCategory"].Value;
|
||||||
|
var set = match.Groups["Set"].Value;
|
||||||
|
return $"chara/{subCategory}/{set}/skeleton/base/b0001/skl_{set}b0001.sklb";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Read a .sklb from the active collection or game. </summary>
|
/// <summary> Read a .sklb from the active collection or game. </summary>
|
||||||
|
|
@ -111,7 +136,7 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
// TODO: if cross-collection lookups are turned off, this conversion can be skipped
|
// TODO: if cross-collection lookups are turned off, this conversion can be skipped
|
||||||
if (!Utf8GamePath.FromString(sklbPath, out var utf8SklbPath, true))
|
if (!Utf8GamePath.FromString(sklbPath, out var utf8SklbPath, true))
|
||||||
throw new Exception("TODO: handle - should it throw, or try to fail gracefully?");
|
throw new Exception($"Resolved skeleton path {sklbPath} could not be converted to a game path.");
|
||||||
|
|
||||||
var resolvedPath = _edit._activeCollections.Current.ResolvePath(utf8SklbPath);
|
var resolvedPath = _edit._activeCollections.Current.ResolvePath(utf8SklbPath);
|
||||||
// TODO: is it worth trying to use streams for these instead? i'll need to do this for mtrl/tex too, so might be a good idea. that said, the mtrl reader doesn't accept streams, so...
|
// TODO: is it worth trying to use streams for these instead? i'll need to do this for mtrl/tex too, so might be a good idea. that said, the mtrl reader doesn't accept streams, so...
|
||||||
|
|
@ -121,7 +146,7 @@ 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("TODO: handle - this effectively means that the resolved path doesn't exist. graceful?");
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue