feat: add customizations dump (data and icons)

This commit is contained in:
2026-06-18 18:59:32 -03:00
parent 1dba3568dc
commit 5989bd3bce
2 changed files with 121 additions and 2 deletions
+118
View File
@@ -44,6 +44,15 @@ struct DLCInfo
public Dictionary<string, string> dlcIds; public Dictionary<string, string> dlcIds;
} }
struct CustomizationItemInfo
{
public string id;
public string name;
public string iconFilePath;
public int associatedCharacter;
public ECustomizationCategory category;
}
class Dumper class Dumper
{ {
private DefaultFileProvider? _provider; private DefaultFileProvider? _provider;
@@ -55,6 +64,7 @@ class Dumper
private readonly Dictionary<int, CharacterInfo> _characterMap = new(); private readonly Dictionary<int, CharacterInfo> _characterMap = new();
private readonly Dictionary<string, ItemInfo> _itemMap = new(); private readonly Dictionary<string, ItemInfo> _itemMap = new();
private readonly Dictionary<string, OfferingInfo> _offeringMap = new(); private readonly Dictionary<string, OfferingInfo> _offeringMap = new();
private readonly Dictionary<string, CustomizationItemInfo> _customizationItemMap = new();
public Dumper(string outDir) public Dumper(string outDir)
{ {
@@ -245,9 +255,117 @@ class Dumper
WriteJson("dlcs", dlcList.ToArray()); WriteJson("dlcs", dlcList.ToArray());
} }
public void DumpCustomizations()
{
ProcessDataTables("/CustomizationItemDB.uasset", "customization items", (rowKey, props) =>
{
if (!TryGetProp(props, "Category", out ECustomizationCategory category)
|| !TryGetProp(props, "Availability", out FStructFallback availabilityFb)
|| !TryGetProp(props, "UIData", out FStructFallback uiDataFb)
|| !TryGetProp(props, "AssociatedCharacter", out int associatedCharacter))
throw new KeyNotFoundException($"Required properties missing for CustomizationItem: {rowKey}");
AvailabilityStruct availability = availabilityFb.MapToStruct<AvailabilityStruct>();
UIDataStruct uiData = uiDataFb.MapToStruct<UIDataStruct>();
if (availability.DLCId == "development") return; // skip development dlc items just in case
if (uiData.IconAssetList.Length == 0)
throw new InvalidDataException("CustomizationItem's UIData had no icons");
string iconPathStr = uiData.IconAssetList[0].ToString();
_customizationItemMap[rowKey] = new CustomizationItemInfo
{
id = rowKey,
name = uiData.DisplayName.ToString(),
associatedCharacter = associatedCharacter,
category = category,
iconFilePath = iconPathStr,
};
});
_log.Info("Dumped {0} customization items", _customizationItemMap.Count);
WriteJson("customization_items", _customizationItemMap.Values);
}
public void DumpCharacterIcons() => ExportIcons(_characterMap.Values.Select(x => x.iconFilePath), "character", "/character-icons/"); public void DumpCharacterIcons() => ExportIcons(_characterMap.Values.Select(x => x.iconFilePath), "character", "/character-icons/");
public void DumpItemIcons() => ExportIcons(_itemMap.Values.Select(x => x.iconFilePath), "item", "/item-icons/"); public void DumpItemIcons() => ExportIcons(_itemMap.Values.Select(x => x.iconFilePath), "item", "/item-icons/");
public void DumpOfferingIcons() => ExportIcons(_offeringMap.Values.Select(x => x.iconFilePath), "offering", "/offering-icons/"); public void DumpOfferingIcons() => ExportIcons(_offeringMap.Values.Select(x => x.iconFilePath), "offering", "/offering-icons/");
public void DumpCustomizationIcons()
{
_log.Info("Dumping customization item icons");
foreach (var item in _customizationItemMap.Values)
{
string outPath = "customization/";
if (item.category == ECustomizationCategory.Charm ||
item.category == ECustomizationCategory.Badge ||
item.category == ECustomizationCategory.Banner ||
item.category == ECustomizationCategory.PortraitBackground)
{
switch (item.category)
{
case ECustomizationCategory.Charm:
outPath += "charms/";
break;
case ECustomizationCategory.Badge:
outPath += "badges/";
break;
case ECustomizationCategory.Banner:
outPath += "banners/";
break;
case ECustomizationCategory.PortraitBackground:
outPath += "portrait-backgrounds/";
break;
}
}
else if (item.associatedCharacter > -1)
{
string charFolderName = item.associatedCharacter.ToString();
if (_characterMap.TryGetValue(item.associatedCharacter, out CharacterInfo charInfo) && !string.IsNullOrWhiteSpace(charInfo.name))
charFolderName = string.Join("_", charInfo.name.Split(Path.GetInvalidFileNameChars()));
outPath += $"characters/{charFolderName}/";
switch (item.category)
{
case ECustomizationCategory.SurvivorHead:
case ECustomizationCategory.KillerHead:
outPath += "heads/";
break;
case ECustomizationCategory.SurvivorTorso:
outPath += "torsos/";
break;
case ECustomizationCategory.SurvivorLegs:
outPath += "legs/";
break;
case ECustomizationCategory.KillerBody:
outPath += "bodys/";
break;
case ECustomizationCategory.KillerWeapon:
outPath += "weapons/";
break;
case ECustomizationCategory.Outfits:
outPath += "outfits/";
break;
}
}
try
{
ExportIcon(item.iconFilePath, outPath);
}
catch (Exception ex)
{
_log.Error($"Failed to export icon for {item.id}: {ex.Message}");
}
}
_log.Info("Dumped all customization item icons");
}
/* /*
* bulk functions for dumping * bulk functions for dumping
+1
View File
@@ -15,6 +15,7 @@ var dumpTasks = new (string Name, Action Execute)[]
("character", () => { dumper.DumpCharacters(); dumper.DumpCharacterIcons(); }), ("character", () => { dumper.DumpCharacters(); dumper.DumpCharacterIcons(); }),
("item", () => { dumper.DumpItems(); dumper.DumpItemIcons(); }), ("item", () => { dumper.DumpItems(); dumper.DumpItemIcons(); }),
("offering", () => { dumper.DumpOfferings(); dumper.DumpOfferingIcons(); }), ("offering", () => { dumper.DumpOfferings(); dumper.DumpOfferingIcons(); }),
("customizations", () => { dumper.DumpCustomizations(); dumper.DumpCustomizationIcons(); }),
("dlc", () => { dumper.DumpDLCs(); }) ("dlc", () => { dumper.DumpDLCs(); })
}; };