From 9ca0956aae70b2d19143736472a95b76e3b653e2 Mon Sep 17 00:00:00 2001 From: neru Date: Thu, 18 Jun 2026 18:15:51 -0300 Subject: [PATCH] feat: add DumpOfferings --- src/dumper/dumper.cs | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/dumper/dumper.cs b/src/dumper/dumper.cs index bac1273..ee84c6a 100644 --- a/src/dumper/dumper.cs +++ b/src/dumper/dumper.cs @@ -1,6 +1,7 @@ using CUE4Parse.Compression; using CUE4Parse.Encryption.Aes; using CUE4Parse.FileProvider; +using CUE4Parse.FileProvider.Objects; using CUE4Parse.MappingsProvider.Usmap; using CUE4Parse.UE4.Assets; using CUE4Parse.UE4.Assets.Exports.Engine; @@ -28,6 +29,14 @@ struct ItemInfo public string iconFilePath; } +struct OfferingInfo +{ + public string id; + public string name; + public string iconFilePath; + public EPlayerRole role; +} + class Dumper { private DefaultFileProvider? _provider; @@ -38,6 +47,7 @@ class Dumper private readonly Dictionary _characterMap = new(); private readonly Dictionary _itemMap = new(); + private readonly Dictionary _offeringMap = new(); public Dumper(string outDir) { @@ -238,6 +248,59 @@ class Dumper return; } + public void DumpOfferings() + { + if (_dataPak == null || _provider == null) + throw new InvalidOperationException("Attempted to call dump function without dumper initialization/state"); + + _log.Info("Dumping offerings"); + + List offeringDBPaths = _dataPak.Files.Keys.Where(x => x.Contains("/OfferingDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList(); + + foreach (string path in offeringDBPaths) + { + string cleanPath = path.Contains('.') ? path[..path.LastIndexOf('.')] : path; + + if (_provider.TryLoadPackageObject(cleanPath, out UDataTable? dataTable)) + { + foreach (KeyValuePair row in dataTable.RowMap) + { + List props = row.Value.Properties; + + string offeringId = row.Key.Text; + + if (!TryGetProp(props, "Role", out EPlayerRole role) + || !TryGetProp(props, "UIData", out FStructFallback uiDataFb) + || !TryGetProp(props, "Inventory", out bool isInventory) + || !TryGetProp(props, "IsFakeItem", out bool isFakeItem) + ) + throw new KeyNotFoundException("Role, UIData, Inventory or IsFakeItem was not found"); + + UIDataStruct uiData = uiDataFb.MapToStruct(); + + if (!isInventory || isFakeItem) + { + _log.Verbose("Ignoring invalid offering ({0})", offeringDBPaths); + continue; + } + + if (uiData.IconAssetList.Length == 0) + throw new InvalidDataException("Offerings's UIData had no icons"); + + _offeringMap[offeringId] = new OfferingInfo + { + id = offeringId, + name = uiData.DisplayName.ToString(), + iconFilePath = uiData.IconAssetList[0].ToString(), + role = role + }; + } + } + } + + WriteJson("offerings", _offeringMap.Values); + _log.Info("Dumped {0} offerings", _offeringMap.Count); + } /* * internal helper functions */