mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 12:44:19 +01:00
Use DataShare in ObjectIdentifier
This commit is contained in:
parent
52b2b66cd7
commit
ef3ffb5f10
17 changed files with 4138 additions and 573 deletions
|
|
@ -1,16 +1,66 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Logging;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.GameData.Util;
|
||||
|
||||
namespace Penumbra.GameData;
|
||||
|
||||
internal class GamePathParser : IGamePathParser
|
||||
{
|
||||
public GameObjectInfo GetFileInfo(string path)
|
||||
{
|
||||
path = path.ToLowerInvariant().Replace('\\', '/');
|
||||
|
||||
var (fileType, objectType, match) = ParseGamePath(path);
|
||||
if (match == null || !match.Success)
|
||||
return new GameObjectInfo
|
||||
{
|
||||
FileType = fileType,
|
||||
ObjectType = objectType,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var groups = match.Groups;
|
||||
switch (objectType)
|
||||
{
|
||||
case ObjectType.Accessory: return HandleEquipment(fileType, groups);
|
||||
case ObjectType.Equipment: return HandleEquipment(fileType, groups);
|
||||
case ObjectType.Weapon: return HandleWeapon(fileType, groups);
|
||||
case ObjectType.Map: return HandleMap(fileType, groups);
|
||||
case ObjectType.Monster: return HandleMonster(fileType, groups);
|
||||
case ObjectType.DemiHuman: return HandleDemiHuman(fileType, groups);
|
||||
case ObjectType.Character: return HandleCustomization(fileType, groups);
|
||||
case ObjectType.Icon: return HandleIcon(fileType, groups);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Error($"Could not parse {path}:\n{e}");
|
||||
}
|
||||
|
||||
return new GameObjectInfo
|
||||
{
|
||||
FileType = fileType,
|
||||
ObjectType = objectType,
|
||||
};
|
||||
}
|
||||
|
||||
public string VfxToKey(string path)
|
||||
{
|
||||
var match = _vfxRegexTmb.Match(path);
|
||||
if (match.Success)
|
||||
return match.Groups["key"].Value.ToLowerInvariant();
|
||||
|
||||
match = _vfxRegexPap.Match(path);
|
||||
return match.Success ? match.Groups["key"].Value.ToLowerInvariant() : string.Empty;
|
||||
}
|
||||
|
||||
private const string CharacterFolder = "chara";
|
||||
private const string EquipmentFolder = "equipment";
|
||||
private const string PlayerFolder = "human";
|
||||
|
|
@ -30,62 +80,74 @@ internal class GamePathParser : IGamePathParser
|
|||
private const string WorldFolder1 = "bgcommon";
|
||||
private const string WorldFolder2 = "bg";
|
||||
|
||||
// @formatter:off
|
||||
private readonly Dictionary<FileType, Dictionary<ObjectType, Regex[]>> _regexes = new()
|
||||
{ { FileType.Font, new Dictionary< ObjectType, Regex[] >(){ { ObjectType.Font, new Regex[]{ new(@"common/font/(?'fontname'.*)_(?'id'\d\d)(_lobby)?\.fdt") } } } }
|
||||
, { FileType.Texture, new Dictionary< ObjectType, Regex[] >()
|
||||
{ { ObjectType.Icon, new Regex[]{ new(@"ui/icon/(?'group'\d*)(/(?'lang'[a-z]{2}))?(/(?'hq'hq))?/(?'id'\d*)(?'hr'_hr1)?\.tex") } }
|
||||
, { ObjectType.Map, new Regex[]{ new(@"ui/map/(?'id'[a-z0-9]{4})/(?'variant'\d{2})/\k'id'\k'variant'(?'suffix'[a-z])?(_[a-z])?\.tex") } }
|
||||
, { ObjectType.Weapon, new Regex[]{ new(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/texture/v(?'variant'\d{2})_w\k'id'b\k'weapon'(_[a-z])?_[a-z]\.tex") } }
|
||||
, { ObjectType.Monster, new Regex[]{ new(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/texture/v(?'variant'\d{2})_m\k'monster'b\k'id'(_[a-z])?_[a-z]\.tex") } }
|
||||
, { ObjectType.Equipment, new Regex[]{ new(@"chara/equipment/e(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex") } }
|
||||
, { ObjectType.DemiHuman, new Regex[]{ new(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/texture/v(?'variant'\d{2})_d\k'id'e\k'equip'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex") } }
|
||||
, { ObjectType.Accessory, new Regex[]{ new(@"chara/accessory/a(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})_[a-z]\.tex") } }
|
||||
, { ObjectType.Character, new Regex[]{ new(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture/(?'minus'(--)?)(v(?'variant'\d{2})_)?c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?(_[a-z])?_[a-z]\.tex")
|
||||
, new(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture")
|
||||
, new(@"chara/common/texture/skin(?'skin'.*)\.tex")
|
||||
, new(@"chara/common/texture/(?'catchlight'catchlight)(.*)\.tex")
|
||||
, new(@"chara/common/texture/decal_(?'location'[a-z]+)/[-_]?decal_(?'id'\d+).tex") } } } }
|
||||
, { FileType.Model, new Dictionary< ObjectType, Regex[] >()
|
||||
{ { ObjectType.Weapon, new Regex[]{ new(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/model/w\k'id'b\k'weapon'\.mdl") } }
|
||||
, { ObjectType.Monster, new Regex[]{ new(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/model/m\k'monster'b\k'id'\.mdl") } }
|
||||
, { ObjectType.Equipment, new Regex[]{ new(@"chara/equipment/e(?'id'\d{4})/model/c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})\.mdl") } }
|
||||
, { ObjectType.DemiHuman, new Regex[]{ new(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/model/d\k'id'e\k'equip'_(?'slot'[a-z]{3})\.mdl") } }
|
||||
, { ObjectType.Accessory, new Regex[]{ new(@"chara/accessory/a(?'id'\d{4})/model/c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})\.mdl") } }
|
||||
, { ObjectType.Character, new Regex[]{ new(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/model/c\k'race'\k'typeabr'\k'id'_(?'slot'[a-z]{3})\.mdl") } } } }
|
||||
, { FileType.Material, new Dictionary< ObjectType, Regex[] >()
|
||||
{ { ObjectType.Weapon, new Regex[]{ new(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/material/v(?'variant'\d{4})/mt_w\k'id'b\k'weapon'_[a-z]\.mtrl") } }
|
||||
, { ObjectType.Monster, new Regex[]{ new(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/material/v(?'variant'\d{4})/mt_m\k'monster'b\k'id'_[a-z]\.mtrl") } }
|
||||
, { ObjectType.Equipment, new Regex[]{ new(@"chara/equipment/e(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})_[a-z]\.mtrl") } }
|
||||
, { ObjectType.DemiHuman, new Regex[]{ new(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/material/v(?'variant'\d{4})/mt_d\k'id'e\k'equip'_(?'slot'[a-z]{3})_[a-z]\.mtrl") } }
|
||||
, { ObjectType.Accessory, new Regex[]{ new(@"chara/accessory/a(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})_[a-z]\.mtrl") } }
|
||||
, { ObjectType.Character, new Regex[]{ new( @"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/material(/v(?'variant'\d{4}))?/mt_c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?_[a-z]\.mtrl" ) } } } }
|
||||
, { FileType.Imc, new Dictionary< ObjectType, Regex[] >()
|
||||
{ { ObjectType.Weapon, new Regex[]{ new(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/b\k'weapon'\.imc") } }
|
||||
, { ObjectType.Monster, new Regex[]{ new(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/b\k'id'\.imc") } }
|
||||
, { ObjectType.Equipment, new Regex[]{ new(@"chara/equipment/e(?'id'\d{4})/e\k'id'\.imc") } }
|
||||
, { ObjectType.DemiHuman, new Regex[]{ new(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/e\k'equip'\.imc") } }
|
||||
, { ObjectType.Accessory, new Regex[]{ new(@"chara/accessory/a(?'id'\d{4})/a\k'id'\.imc") } } } },
|
||||
};
|
||||
// @formatter:off
|
||||
// language=regex
|
||||
private readonly IReadOnlyDictionary<FileType, IReadOnlyDictionary<ObjectType, IReadOnlyList<Regex>>> _regexes = new Dictionary<FileType, IReadOnlyDictionary<ObjectType, IReadOnlyList<Regex>>>()
|
||||
{
|
||||
[FileType.Font] = new Dictionary<ObjectType, IReadOnlyList<Regex>>
|
||||
{
|
||||
[ObjectType.Font] = CreateRegexes( @"common/font/(?'fontname'.*)_(?'id'\d\d)(_lobby)?\.fdt"),
|
||||
},
|
||||
[FileType.Texture] = new Dictionary<ObjectType, IReadOnlyList<Regex>>
|
||||
{
|
||||
[ObjectType.Icon] = CreateRegexes( @"ui/icon/(?'group'\d*)(/(?'lang'[a-z]{2}))?(/(?'hq'hq))?/(?'id'\d*)(?'hr'_hr1)?\.tex"),
|
||||
[ObjectType.Map] = CreateRegexes( @"ui/map/(?'id'[a-z0-9]{4})/(?'variant'\d{2})/\k'id'\k'variant'(?'suffix'[a-z])?(_[a-z])?\.tex"),
|
||||
[ObjectType.Weapon] = CreateRegexes( @"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/texture/v(?'variant'\d{2})_w\k'id'b\k'weapon'(_[a-z])?_[a-z]\.tex"),
|
||||
[ObjectType.Monster] = CreateRegexes( @"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/texture/v(?'variant'\d{2})_m\k'monster'b\k'id'(_[a-z])?_[a-z]\.tex"),
|
||||
[ObjectType.Equipment] = CreateRegexes( @"chara/equipment/e(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex"),
|
||||
[ObjectType.DemiHuman] = CreateRegexes( @"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/texture/v(?'variant'\d{2})_d\k'id'e\k'equip'_(?'slot'[a-z]{3})(_[a-z])?_[a-z]\.tex"),
|
||||
[ObjectType.Accessory] = CreateRegexes( @"chara/accessory/a(?'id'\d{4})/texture/v(?'variant'\d{2})_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})_[a-z]\.tex"),
|
||||
[ObjectType.Character] = CreateRegexes( @"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture/(?'minus'(--)?)(v(?'variant'\d{2})_)?c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?(_[a-z])?_[a-z]\.tex"
|
||||
, @"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/texture"
|
||||
, @"chara/common/texture/skin(?'skin'.*)\.tex"
|
||||
, @"chara/common/texture/(?'catchlight'catchlight)(.*)\.tex"
|
||||
, @"chara/common/texture/decal_(?'location'[a-z]+)/[-_]?decal_(?'id'\d+).tex"),
|
||||
},
|
||||
[FileType.Model] = new Dictionary<ObjectType, IReadOnlyList<Regex>>
|
||||
{
|
||||
[ObjectType.Weapon] = CreateRegexes(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/model/w\k'id'b\k'weapon'\.mdl"),
|
||||
[ObjectType.Monster] = CreateRegexes(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/model/m\k'monster'b\k'id'\.mdl"),
|
||||
[ObjectType.Equipment] = CreateRegexes(@"chara/equipment/e(?'id'\d{4})/model/c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})\.mdl"),
|
||||
[ObjectType.DemiHuman] = CreateRegexes(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/model/d\k'id'e\k'equip'_(?'slot'[a-z]{3})\.mdl"),
|
||||
[ObjectType.Accessory] = CreateRegexes(@"chara/accessory/a(?'id'\d{4})/model/c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})\.mdl"),
|
||||
[ObjectType.Character] = CreateRegexes(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/model/c\k'race'\k'typeabr'\k'id'_(?'slot'[a-z]{3})\.mdl"),
|
||||
},
|
||||
[FileType.Material] = new Dictionary<ObjectType, IReadOnlyList<Regex>>
|
||||
{
|
||||
[ObjectType.Weapon] = CreateRegexes(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/material/v(?'variant'\d{4})/mt_w\k'id'b\k'weapon'_[a-z]\.mtrl"),
|
||||
[ObjectType.Monster] = CreateRegexes(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/material/v(?'variant'\d{4})/mt_m\k'monster'b\k'id'_[a-z]\.mtrl"),
|
||||
[ObjectType.Equipment] = CreateRegexes(@"chara/equipment/e(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})e\k'id'_(?'slot'[a-z]{3})_[a-z]\.mtrl"),
|
||||
[ObjectType.DemiHuman] = CreateRegexes(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/material/v(?'variant'\d{4})/mt_d\k'id'e\k'equip'_(?'slot'[a-z]{3})_[a-z]\.mtrl"),
|
||||
[ObjectType.Accessory] = CreateRegexes(@"chara/accessory/a(?'id'\d{4})/material/v(?'variant'\d{4})/mt_c(?'race'\d{4})a\k'id'_(?'slot'[a-z]{3})_[a-z]\.mtrl"),
|
||||
[ObjectType.Character] = CreateRegexes(@"chara/human/c(?'race'\d{4})/obj/(?'type'[a-z]+)/(?'typeabr'[a-z])(?'id'\d{4})/material(/v(?'variant'\d{4}))?/mt_c\k'race'\k'typeabr'\k'id'(_(?'slot'[a-z]{3}))?_[a-z]\.mtrl"),
|
||||
},
|
||||
[FileType.Imc] = new Dictionary<ObjectType, IReadOnlyList<Regex>>
|
||||
{
|
||||
[ObjectType.Weapon] = CreateRegexes(@"chara/weapon/w(?'id'\d{4})/obj/body/b(?'weapon'\d{4})/b\k'weapon'\.imc"),
|
||||
[ObjectType.Monster] = CreateRegexes(@"chara/monster/m(?'monster'\d{4})/obj/body/b(?'id'\d{4})/b\k'id'\.imc"),
|
||||
[ObjectType.Equipment] = CreateRegexes(@"chara/equipment/e(?'id'\d{4})/e\k'id'\.imc"),
|
||||
[ObjectType.DemiHuman] = CreateRegexes(@"chara/demihuman/d(?'id'\d{4})/obj/equipment/e(?'equip'\d{4})/e\k'equip'\.imc"),
|
||||
[ObjectType.Accessory] = CreateRegexes(@"chara/accessory/a(?'id'\d{4})/a\k'id'\.imc"),
|
||||
},
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
public ObjectType PathToObjectType( GamePath path )
|
||||
|
||||
private static IReadOnlyList<Regex> CreateRegexes(params string[] regexes)
|
||||
=> regexes.Select(s => new Regex(s, RegexOptions.Compiled)).ToArray();
|
||||
|
||||
public ObjectType PathToObjectType(string path)
|
||||
{
|
||||
if( path.Empty )
|
||||
{
|
||||
if (path.Length == 0)
|
||||
return ObjectType.Unknown;
|
||||
}
|
||||
|
||||
string p = path;
|
||||
var folders = p.Split( '/' );
|
||||
if( folders.Length < 2 )
|
||||
{
|
||||
var folders = path.Split('/');
|
||||
if (folders.Length < 2)
|
||||
return ObjectType.Unknown;
|
||||
}
|
||||
|
||||
return folders[ 0 ] switch
|
||||
return folders[0] switch
|
||||
{
|
||||
CharacterFolder => folders[ 1 ] switch
|
||||
CharacterFolder => folders[1] switch
|
||||
{
|
||||
EquipmentFolder => ObjectType.Equipment,
|
||||
AccessoryFolder => ObjectType.Accessory,
|
||||
|
|
@ -96,7 +158,7 @@ internal class GamePathParser : IGamePathParser
|
|||
CommonFolder => ObjectType.Character,
|
||||
_ => ObjectType.Unknown,
|
||||
},
|
||||
UiFolder => folders[ 1 ] switch
|
||||
UiFolder => folders[1] switch
|
||||
{
|
||||
IconFolder => ObjectType.Icon,
|
||||
LoadingFolder => ObjectType.LoadingScreen,
|
||||
|
|
@ -104,13 +166,13 @@ internal class GamePathParser : IGamePathParser
|
|||
InterfaceFolder => ObjectType.Interface,
|
||||
_ => ObjectType.Unknown,
|
||||
},
|
||||
CommonFolder => folders[ 1 ] switch
|
||||
CommonFolder => folders[1] switch
|
||||
{
|
||||
FontFolder => ObjectType.Font,
|
||||
_ => ObjectType.Unknown,
|
||||
},
|
||||
HousingFolder => ObjectType.Housing,
|
||||
WorldFolder1 => folders[ 1 ] switch
|
||||
WorldFolder1 => folders[1] switch
|
||||
{
|
||||
HousingFolder => ObjectType.Housing,
|
||||
_ => ObjectType.World,
|
||||
|
|
@ -121,152 +183,120 @@ internal class GamePathParser : IGamePathParser
|
|||
};
|
||||
}
|
||||
|
||||
private (FileType, ObjectType, Match?) ParseGamePath( GamePath path )
|
||||
private (FileType, ObjectType, Match?) ParseGamePath(string path)
|
||||
{
|
||||
if( !Names.ExtensionToFileType.TryGetValue( Extension( path ), out var fileType ) )
|
||||
{
|
||||
if (!Names.ExtensionToFileType.TryGetValue(Path.GetExtension(path), out var fileType))
|
||||
fileType = FileType.Unknown;
|
||||
}
|
||||
|
||||
var objectType = PathToObjectType( path );
|
||||
var objectType = PathToObjectType(path);
|
||||
|
||||
if( !_regexes.TryGetValue( fileType, out var objectDict ) )
|
||||
if (!_regexes.TryGetValue(fileType, out var objectDict))
|
||||
return (fileType, objectType, null);
|
||||
|
||||
if (!objectDict.TryGetValue(objectType, out var regexes))
|
||||
return (fileType, objectType, null);
|
||||
|
||||
foreach (var regex in regexes)
|
||||
{
|
||||
return ( fileType, objectType, null );
|
||||
var match = regex.Match(path);
|
||||
if (match.Success)
|
||||
return (fileType, objectType, match);
|
||||
}
|
||||
|
||||
if( !objectDict.TryGetValue( objectType, out var regexes ) )
|
||||
{
|
||||
return ( fileType, objectType, null );
|
||||
}
|
||||
|
||||
foreach( var regex in regexes )
|
||||
{
|
||||
var match = regex.Match( path );
|
||||
if( match.Success )
|
||||
{
|
||||
return ( fileType, objectType, match );
|
||||
}
|
||||
}
|
||||
|
||||
return ( fileType, objectType, null );
|
||||
return (fileType, objectType, null);
|
||||
}
|
||||
|
||||
private static string Extension( string filename )
|
||||
private static GameObjectInfo HandleEquipment(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var extIdx = filename.LastIndexOf( '.' );
|
||||
return extIdx < 0 ? "" : filename.Substring( extIdx );
|
||||
var setId = ushort.Parse(groups["id"].Value);
|
||||
if (fileType == FileType.Imc)
|
||||
return GameObjectInfo.Equipment(fileType, setId);
|
||||
|
||||
var gr = Names.GenderRaceFromCode(groups["race"].Value);
|
||||
var slot = Names.SuffixToEquipSlot[groups["slot"].Value];
|
||||
if (fileType == FileType.Model)
|
||||
return GameObjectInfo.Equipment(fileType, setId, gr, slot);
|
||||
|
||||
var variant = byte.Parse(groups["variant"].Value);
|
||||
return GameObjectInfo.Equipment(fileType, setId, gr, slot, variant);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleEquipment( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleWeapon(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var setId = ushort.Parse( groups[ "id" ].Value );
|
||||
if( fileType == FileType.Imc )
|
||||
{
|
||||
return GameObjectInfo.Equipment( fileType, setId );
|
||||
}
|
||||
var weaponId = ushort.Parse(groups["weapon"].Value);
|
||||
var setId = ushort.Parse(groups["id"].Value);
|
||||
if (fileType == FileType.Imc || fileType == FileType.Model)
|
||||
return GameObjectInfo.Weapon(fileType, setId, weaponId);
|
||||
|
||||
var gr = Names.GenderRaceFromCode( groups[ "race" ].Value );
|
||||
var slot = Names.SuffixToEquipSlot[ groups[ "slot" ].Value ];
|
||||
if( fileType == FileType.Model )
|
||||
{
|
||||
return GameObjectInfo.Equipment( fileType, setId, gr, slot );
|
||||
}
|
||||
|
||||
var variant = byte.Parse( groups[ "variant" ].Value );
|
||||
return GameObjectInfo.Equipment( fileType, setId, gr, slot, variant );
|
||||
var variant = byte.Parse(groups["variant"].Value);
|
||||
return GameObjectInfo.Weapon(fileType, setId, weaponId, variant);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleWeapon( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleMonster(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var weaponId = ushort.Parse( groups[ "weapon" ].Value );
|
||||
var setId = ushort.Parse( groups[ "id" ].Value );
|
||||
if( fileType == FileType.Imc || fileType == FileType.Model )
|
||||
{
|
||||
return GameObjectInfo.Weapon( fileType, setId, weaponId );
|
||||
}
|
||||
var monsterId = ushort.Parse(groups["monster"].Value);
|
||||
var bodyId = ushort.Parse(groups["id"].Value);
|
||||
if (fileType == FileType.Imc || fileType == FileType.Model)
|
||||
return GameObjectInfo.Monster(fileType, monsterId, bodyId);
|
||||
|
||||
var variant = byte.Parse( groups[ "variant" ].Value );
|
||||
return GameObjectInfo.Weapon( fileType, setId, weaponId, variant );
|
||||
var variant = byte.Parse(groups["variant"].Value);
|
||||
return GameObjectInfo.Monster(fileType, monsterId, bodyId, variant);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleMonster( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleDemiHuman(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var monsterId = ushort.Parse( groups[ "monster" ].Value );
|
||||
var bodyId = ushort.Parse( groups[ "id" ].Value );
|
||||
if( fileType == FileType.Imc || fileType == FileType.Model )
|
||||
{
|
||||
return GameObjectInfo.Monster( fileType, monsterId, bodyId );
|
||||
}
|
||||
var demiHumanId = ushort.Parse(groups["id"].Value);
|
||||
var equipId = ushort.Parse(groups["equip"].Value);
|
||||
if (fileType == FileType.Imc)
|
||||
return GameObjectInfo.DemiHuman(fileType, demiHumanId, equipId);
|
||||
|
||||
var variant = byte.Parse( groups[ "variant" ].Value );
|
||||
return GameObjectInfo.Monster( fileType, monsterId, bodyId, variant );
|
||||
var slot = Names.SuffixToEquipSlot[groups["slot"].Value];
|
||||
if (fileType == FileType.Model)
|
||||
return GameObjectInfo.DemiHuman(fileType, demiHumanId, equipId, slot);
|
||||
|
||||
var variant = byte.Parse(groups["variant"].Value);
|
||||
return GameObjectInfo.DemiHuman(fileType, demiHumanId, equipId, slot, variant);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleDemiHuman( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleCustomization(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var demiHumanId = ushort.Parse( groups[ "id" ].Value );
|
||||
var equipId = ushort.Parse( groups[ "equip" ].Value );
|
||||
if( fileType == FileType.Imc )
|
||||
if (groups["catchlight"].Success)
|
||||
return GameObjectInfo.Customization(fileType, CustomizationType.Iris);
|
||||
|
||||
if (groups["skin"].Success)
|
||||
return GameObjectInfo.Customization(fileType, CustomizationType.Skin);
|
||||
|
||||
var id = ushort.Parse(groups["id"].Value);
|
||||
if (groups["location"].Success)
|
||||
{
|
||||
return GameObjectInfo.DemiHuman( fileType, demiHumanId, equipId );
|
||||
var tmpType = groups["location"].Value == "face" ? CustomizationType.DecalFace
|
||||
: groups["location"].Value == "equip" ? CustomizationType.DecalEquip : CustomizationType.Unknown;
|
||||
return GameObjectInfo.Customization(fileType, tmpType, id);
|
||||
}
|
||||
|
||||
var slot = Names.SuffixToEquipSlot[ groups[ "slot" ].Value ];
|
||||
if( fileType == FileType.Model )
|
||||
{
|
||||
return GameObjectInfo.DemiHuman( fileType, demiHumanId, equipId, slot );
|
||||
}
|
||||
|
||||
var variant = byte.Parse( groups[ "variant" ].Value );
|
||||
return GameObjectInfo.DemiHuman( fileType, demiHumanId, equipId, slot, variant );
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleCustomization( FileType fileType, GroupCollection groups )
|
||||
{
|
||||
if( groups[ "catchlight" ].Success )
|
||||
{
|
||||
return GameObjectInfo.Customization( fileType, CustomizationType.Iris );
|
||||
}
|
||||
|
||||
if( groups[ "skin" ].Success )
|
||||
{
|
||||
return GameObjectInfo.Customization( fileType, CustomizationType.Skin );
|
||||
}
|
||||
|
||||
var id = ushort.Parse( groups[ "id" ].Value );
|
||||
if( groups[ "location" ].Success )
|
||||
{
|
||||
var tmpType = groups[ "location" ].Value == "face" ? CustomizationType.DecalFace
|
||||
: groups[ "location" ].Value == "equip" ? CustomizationType.DecalEquip : CustomizationType.Unknown;
|
||||
return GameObjectInfo.Customization( fileType, tmpType, id );
|
||||
}
|
||||
|
||||
var gr = Names.GenderRaceFromCode( groups[ "race" ].Value );
|
||||
var bodySlot = Names.StringToBodySlot[ groups[ "type" ].Value ];
|
||||
var type = groups[ "slot" ].Success
|
||||
? Names.SuffixToCustomizationType[ groups[ "slot" ].Value ]
|
||||
var gr = Names.GenderRaceFromCode(groups["race"].Value);
|
||||
var bodySlot = Names.StringToBodySlot[groups["type"].Value];
|
||||
var type = groups["slot"].Success
|
||||
? Names.SuffixToCustomizationType[groups["slot"].Value]
|
||||
: CustomizationType.Skin;
|
||||
if( fileType == FileType.Material )
|
||||
if (fileType == FileType.Material)
|
||||
{
|
||||
var variant = groups[ "variant" ].Success ? byte.Parse( groups[ "variant" ].Value ) : ( byte )0;
|
||||
return GameObjectInfo.Customization( fileType, type, id, gr, bodySlot, variant );
|
||||
var variant = groups["variant"].Success ? byte.Parse(groups["variant"].Value) : (byte)0;
|
||||
return GameObjectInfo.Customization(fileType, type, id, gr, bodySlot, variant);
|
||||
}
|
||||
|
||||
return GameObjectInfo.Customization( fileType, type, id, gr, bodySlot );
|
||||
return GameObjectInfo.Customization(fileType, type, id, gr, bodySlot);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleIcon( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleIcon(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var hq = groups[ "hq" ].Success;
|
||||
var hr = groups[ "hr" ].Success;
|
||||
var id = uint.Parse( groups[ "id" ].Value );
|
||||
if( !groups[ "lang" ].Success )
|
||||
{
|
||||
return GameObjectInfo.Icon( fileType, id, hq, hr );
|
||||
}
|
||||
var hq = groups["hq"].Success;
|
||||
var hr = groups["hr"].Success;
|
||||
var id = uint.Parse(groups["id"].Value);
|
||||
if (!groups["lang"].Success)
|
||||
return GameObjectInfo.Icon(fileType, id, hq, hr);
|
||||
|
||||
var language = groups[ "lang" ].Value switch
|
||||
var language = groups["lang"].Value switch
|
||||
{
|
||||
"en" => Dalamud.ClientLanguage.English,
|
||||
"ja" => Dalamud.ClientLanguage.Japanese,
|
||||
|
|
@ -274,65 +304,23 @@ internal class GamePathParser : IGamePathParser
|
|||
"fr" => Dalamud.ClientLanguage.French,
|
||||
_ => Dalamud.ClientLanguage.English,
|
||||
};
|
||||
return GameObjectInfo.Icon( fileType, id, hq, hr, language );
|
||||
return GameObjectInfo.Icon(fileType, id, hq, hr, language);
|
||||
}
|
||||
|
||||
private static GameObjectInfo HandleMap( FileType fileType, GroupCollection groups )
|
||||
private static GameObjectInfo HandleMap(FileType fileType, GroupCollection groups)
|
||||
{
|
||||
var map = Encoding.ASCII.GetBytes( groups[ "id" ].Value );
|
||||
var variant = byte.Parse( groups[ "variant" ].Value );
|
||||
if( groups[ "suffix" ].Success )
|
||||
var map = Encoding.ASCII.GetBytes(groups["id"].Value);
|
||||
var variant = byte.Parse(groups["variant"].Value);
|
||||
if (groups["suffix"].Success)
|
||||
{
|
||||
var suffix = Encoding.ASCII.GetBytes( groups[ "suffix" ].Value )[ 0 ];
|
||||
return GameObjectInfo.Map( fileType, map[ 0 ], map[ 1 ], map[ 2 ], map[ 3 ], variant, suffix );
|
||||
var suffix = Encoding.ASCII.GetBytes(groups["suffix"].Value)[0];
|
||||
return GameObjectInfo.Map(fileType, map[0], map[1], map[2], map[3], variant, suffix);
|
||||
}
|
||||
|
||||
return GameObjectInfo.Map( fileType, map[ 0 ], map[ 1 ], map[ 2 ], map[ 3 ], variant );
|
||||
return GameObjectInfo.Map(fileType, map[0], map[1], map[2], map[3], variant);
|
||||
}
|
||||
|
||||
public GameObjectInfo GetFileInfo( GamePath path )
|
||||
{
|
||||
var (fileType, objectType, match) = ParseGamePath( path );
|
||||
if( match == null || !match.Success )
|
||||
{
|
||||
return new GameObjectInfo { FileType = fileType, ObjectType = objectType };
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var groups = match.Groups;
|
||||
switch( objectType )
|
||||
{
|
||||
case ObjectType.Accessory: return HandleEquipment( fileType, groups );
|
||||
case ObjectType.Equipment: return HandleEquipment( fileType, groups );
|
||||
case ObjectType.Weapon: return HandleWeapon( fileType, groups );
|
||||
case ObjectType.Map: return HandleMap( fileType, groups );
|
||||
case ObjectType.Monster: return HandleMonster( fileType, groups );
|
||||
case ObjectType.DemiHuman: return HandleDemiHuman( fileType, groups );
|
||||
case ObjectType.Character: return HandleCustomization( fileType, groups );
|
||||
case ObjectType.Icon: return HandleIcon( fileType, groups );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not parse {path}:\n{e}" );
|
||||
}
|
||||
|
||||
return new GameObjectInfo { FileType = fileType, ObjectType = objectType };
|
||||
}
|
||||
|
||||
private readonly Regex _vfxRegexTmb = new(@"chara/action/(?'key'[^\s]+?)\.tmb");
|
||||
private readonly Regex _vfxRegexPap = new(@"chara/human/c0101/animation/a0001/[^\s]+?/(?'key'[^\s]+?)\.pap");
|
||||
|
||||
public string VfxToKey( GamePath path )
|
||||
{
|
||||
var match = _vfxRegexTmb.Match( path );
|
||||
if( match.Success )
|
||||
{
|
||||
return match.Groups[ "key" ].Value.ToLowerInvariant();
|
||||
}
|
||||
|
||||
match = _vfxRegexPap.Match( path );
|
||||
return match.Success ? match.Groups[ "key" ].Value.ToLowerInvariant() : string.Empty;
|
||||
}
|
||||
}
|
||||
private readonly Regex _vfxRegexTmb = new(@"chara[\/]action[\/](?'key'[^\s]+?)\.tmb", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private readonly Regex _vfxRegexPap = new(@"chara[\/]human[\/]c0101[\/]animation[\/]a0001[\/][^\s]+?[\/](?'key'[^\s]+?)\.pap", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue