feat: rewrite dumper from scratch
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly string MappingURL = "https://git.neru.rip/neru/UnlockedByDaylight/raw/branch/main/res/mappings/latest-xbox.usmap";
|
||||
|
||||
public static readonly string PackageName = "BehaviourInteractive.DeadbyDaylightWindows";
|
||||
|
||||
public static readonly string AESKey = "0x22B1639B548124925CF7B9CBAA09F9AC295FCF0324586D6B37EE1D42670B39B3";
|
||||
|
||||
public static readonly string[] BlacklistedCustomizationItems = ["NK_Torso01_Crew01Kraken"];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
public enum EItemAvailability : byte
|
||||
{
|
||||
Available,
|
||||
Disabled,
|
||||
Retired
|
||||
}
|
||||
|
||||
public enum ECustomizationCategory : byte
|
||||
{
|
||||
None,
|
||||
SurvivorHead,
|
||||
SurvivorTorso,
|
||||
SurvivorLegs,
|
||||
KillerHead,
|
||||
KillerBody,
|
||||
KillerWeapon,
|
||||
Outfits,
|
||||
Charm,
|
||||
Badge,
|
||||
Banner,
|
||||
PortraitBackground
|
||||
}
|
||||
|
||||
public enum EPlayerRole : byte
|
||||
{
|
||||
VE_None,
|
||||
VE_Slasher,
|
||||
VE_Camper,
|
||||
VE_Observer
|
||||
}
|
||||
|
||||
public struct AvailabilityStruct
|
||||
{
|
||||
public EItemAvailability itemAvailability;
|
||||
public string DLCId;
|
||||
public string[] AdditionalDlcIds;
|
||||
public Int32 CloudInventoryId;
|
||||
public string CommunityId;
|
||||
public bool _isLicensed;
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.FileProvider;
|
||||
using CUE4Parse.MappingsProvider;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Actor;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Assets.Objects.Properties;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse.UE4.VirtualFileSystem;
|
||||
using CUE4Parse.UE4.Wwise.Plugins.MasteringSuite;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using System.Reflection;
|
||||
|
||||
class Dumper
|
||||
{
|
||||
private DefaultFileProvider? _provider = null;
|
||||
private IAesVfsReader? _dataPak = null;
|
||||
|
||||
public async Task<bool> InitAsync()
|
||||
{
|
||||
/*
|
||||
* ensure mapping
|
||||
*/
|
||||
string mappingPath = Path.Combine(Path.GetTempPath(), "DeadByDaylight.usmap");
|
||||
bool hasDownloadedMapping = await DownloadMappingFileAsync(Constants.MappingURL, mappingPath);
|
||||
if (!hasDownloadedMapping)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* compression setup
|
||||
*/
|
||||
ZlibHelper.Initialize();
|
||||
|
||||
var oodlePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OodleHelper.OodleFileName);
|
||||
OodleHelper.Initialize(oodlePath);
|
||||
|
||||
/*
|
||||
* game localization
|
||||
*/
|
||||
var gamePath = getGamePath();
|
||||
if (gamePath == null) return false;
|
||||
|
||||
var pakDir = gamePath + "\\DeadByDaylight\\Content\\Paks";
|
||||
if (!Directory.Exists(pakDir))
|
||||
{
|
||||
Console.WriteLine("PAK dir does not exist. (Invalid install?)");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* provider setup
|
||||
*/
|
||||
var version = new VersionContainer(EGame.GAME_DeadByDaylight, ETexturePlatform.DesktopMobile);
|
||||
_provider = new DefaultFileProvider(pakDir, SearchOption.TopDirectoryOnly, version);
|
||||
_provider.MappingsContainer = new FileUsmapTypeMappingsProvider(mappingPath);
|
||||
|
||||
_provider.Initialize();
|
||||
_provider.SubmitKey(new FGuid(), new FAesKey(Constants.AESKey));
|
||||
_provider.Mount();
|
||||
|
||||
/*
|
||||
* load db pak
|
||||
*/
|
||||
bool hasDataPak = _provider.TryGetArchive("pakchunk4-WinGDK.utoc", out _dataPak);
|
||||
if (!hasDataPak)
|
||||
{
|
||||
Console.WriteLine("Failed to load pakchunk4-WinGDK.utoc");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string? DumpCustomizationItems()
|
||||
{
|
||||
if (_dataPak == null || _provider == null) return null;
|
||||
|
||||
var searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/CustomizationItemDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var characterItems = new List<string>();
|
||||
var outfits = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? categoryProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Category");
|
||||
if (categoryProp == null || categoryProp.Tag == null) continue;
|
||||
|
||||
FPropertyTag? availabilityProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Availability");
|
||||
if (availabilityProp == null || availabilityProp.Tag == null) continue;
|
||||
|
||||
FStructFallback? availabilityFallback = availabilityProp.Tag.GetValue<FStructFallback>();
|
||||
if (availabilityFallback == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string itemName = row.Key.Text;
|
||||
AvailabilityStruct availability = availabilityFallback.MapToStruct<AvailabilityStruct>();
|
||||
ECustomizationCategory category = categoryProp.Tag.GetValue<ECustomizationCategory>();
|
||||
|
||||
/*
|
||||
* sketchy item filtering
|
||||
*/
|
||||
if (availability.DLCId == "development") continue;
|
||||
if (category < ECustomizationCategory.SurvivorHead || category > ECustomizationCategory.KillerWeapon) continue;
|
||||
if (Constants.BlacklistedCustomizationItems.Contains(itemName)) continue;
|
||||
|
||||
/*
|
||||
* storing
|
||||
*/
|
||||
characterItems.Add(itemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/OutfitDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? availabilityProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Availability");
|
||||
if (availabilityProp == null || availabilityProp.Tag == null) continue;
|
||||
|
||||
FStructFallback? availabilityFallback = availabilityProp.Tag.GetValue<FStructFallback>();
|
||||
if (availabilityFallback == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string outfitName = row.Key.Text;
|
||||
AvailabilityStruct availability = availabilityFallback.MapToStruct<AvailabilityStruct>();
|
||||
|
||||
if (availability.DLCId == "development")
|
||||
continue;
|
||||
|
||||
outfits.Add(outfitName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var customizationsSerialized = new
|
||||
{
|
||||
Items = characterItems.OrderBy(x => x).ToList(),
|
||||
Outfits = outfits.OrderBy(x => x).ToList()
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(customizationsSerialized, Formatting.Indented);
|
||||
}
|
||||
|
||||
public string? DumpItems()
|
||||
{
|
||||
if (_dataPak == null || _provider == null) return null;
|
||||
|
||||
var searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/ItemDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var camperItems = new List<string>();
|
||||
//var slasherPowers = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? roleProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProp == null || roleProp.Tag == null) continue;
|
||||
|
||||
//FPropertyTag? typeProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Type");
|
||||
//if (typeProp == null || typeProp.Tag == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string itemName = row.Key.Text;
|
||||
EPlayerRole role = roleProp.Tag.GetValue<EPlayerRole>();
|
||||
|
||||
if (role == EPlayerRole.VE_Camper)
|
||||
camperItems.Add(itemName);
|
||||
//else
|
||||
// slasherPowers.Add(itemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var itemsSerialized = new
|
||||
{
|
||||
Campers = camperItems.OrderBy(x => x).ToList()
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(itemsSerialized, Formatting.Indented);
|
||||
}
|
||||
|
||||
public string? DumpAddons()
|
||||
{
|
||||
if (_dataPak == null || _provider == null) return null;
|
||||
|
||||
var searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/ItemAddonDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var camperAddons = new List<string>();
|
||||
var slasherAddons = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? roleProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProp == null || roleProp.Tag == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string itemName = row.Key.Text;
|
||||
EPlayerRole role = roleProp.Tag.GetValue<EPlayerRole>();
|
||||
|
||||
if (role == EPlayerRole.VE_Camper)
|
||||
camperAddons.Add(itemName);
|
||||
else
|
||||
slasherAddons.Add(itemName);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addonsSerialized = new
|
||||
{
|
||||
Slashers = slasherAddons.OrderBy(x => x).ToList(),
|
||||
Campers = camperAddons.OrderBy(x => x).ToList()
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(addonsSerialized, Formatting.Indented);
|
||||
}
|
||||
|
||||
public string? DumpOfferings()
|
||||
{
|
||||
if (_dataPak == null || _provider == null) return null;
|
||||
|
||||
var searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/OfferingDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var globalOfferings = new List<string>();
|
||||
var camperOfferings = new List<string>();
|
||||
var slasherOfferings = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? roleProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProp == null || roleProp.Tag == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string itemName = row.Key.Text;
|
||||
EPlayerRole role = roleProp.Tag.GetValue<EPlayerRole>();
|
||||
|
||||
if (role == EPlayerRole.VE_Camper)
|
||||
camperOfferings.Add(itemName);
|
||||
else if (role == EPlayerRole.VE_Slasher)
|
||||
slasherOfferings.Add(itemName);
|
||||
else
|
||||
globalOfferings.Add(itemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
var offeringsSerialized = new
|
||||
{
|
||||
Slashers = slasherOfferings.OrderBy(x => x).ToList(),
|
||||
Campers = camperOfferings.OrderBy(x => x).ToList(),
|
||||
All = globalOfferings.OrderBy(x => x).ToList()
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(offeringsSerialized, Formatting.Indented);
|
||||
}
|
||||
|
||||
public string? DumpPerks()
|
||||
{
|
||||
if (_dataPak == null || _provider == null) return null;
|
||||
|
||||
var searchPaths = _dataPak.Files.Keys.Where(x => x.Contains($"/PerkDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var camperPerks = new List<string>();
|
||||
var slasherPerks = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (_provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
/*
|
||||
* props
|
||||
*/
|
||||
var props = row.Value.Properties;
|
||||
|
||||
FPropertyTag? roleProp = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProp == null || roleProp.Tag == null) continue;
|
||||
|
||||
/*
|
||||
* real data
|
||||
*/
|
||||
string itemName = row.Key.Text;
|
||||
EPlayerRole role = roleProp.Tag.GetValue<EPlayerRole>();
|
||||
|
||||
if (role == EPlayerRole.VE_Camper)
|
||||
camperPerks.Add(itemName);
|
||||
else
|
||||
slasherPerks.Add(itemName);
|
||||
}
|
||||
}
|
||||
}
|
||||
var perksSerialized = new
|
||||
{
|
||||
Slashers = slasherPerks.OrderBy(x => x).ToList(),
|
||||
Campers = camperPerks.OrderBy(x => x).ToList()
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(perksSerialized, Formatting.Indented);
|
||||
}
|
||||
private string? getGamePath()
|
||||
{
|
||||
const string packagesPath = @"Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages";
|
||||
|
||||
using var key = Registry.CurrentUser.OpenSubKey(packagesPath);
|
||||
|
||||
if (key == null) return null;
|
||||
string? matchingKeyName = key.GetSubKeyNames()
|
||||
.FirstOrDefault(name => name.StartsWith(Constants.PackageName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (matchingKeyName != null)
|
||||
{
|
||||
using (RegistryKey? subKey = key.OpenSubKey(matchingKeyName))
|
||||
return subKey?.GetValue("PackageRootFolder")?.ToString();
|
||||
}
|
||||
|
||||
Console.WriteLine("Failed to find game install dir");
|
||||
return null;
|
||||
}
|
||||
private async Task<bool> DownloadMappingFileAsync(string url, string outPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var bytes = await httpClient.GetByteArrayAsync(url);
|
||||
Console.WriteLine("Downloading mapping file...");
|
||||
await File.WriteAllBytesAsync(outPath, bytes);
|
||||
Console.WriteLine($"Mapping file downloaded to: {outPath}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error occured while downloading mapping: {ex.Message}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+68
-224
@@ -1,228 +1,72 @@
|
||||
using CUE4Parse.Compression;
|
||||
using CUE4Parse.Encryption.Aes;
|
||||
using CUE4Parse.FileProvider;
|
||||
using CUE4Parse.MappingsProvider;
|
||||
using CUE4Parse.UE4.Assets.Exports.Actor;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects.Properties;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using Newtonsoft.Json;
|
||||
using CUE4Parse.UE4.Assets.Exports.CustomizableObject;
|
||||
|
||||
class DumpByDaylight
|
||||
int displayError(string err)
|
||||
{
|
||||
private const string _pakDir = "D:\\XboxGames\\Dead By Daylight\\Content\\DeadByDaylight\\Content\\Paks";
|
||||
private const string _aesKey = "0x22B1639B548124925CF7B9CBAA09F9AC295FCF0324586D6B37EE1D42670B39B3";
|
||||
private const string _mappingURL = "https://git.neru.rip/neru/UnlockedByDaylight/raw/branch/main/res/mappings/latest-xbox.usmap";
|
||||
|
||||
public static async Task<string?> DownloadMappingFileAsync(string url, string savePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
Console.WriteLine("downloading mapping file...");
|
||||
var bytes = await httpClient.GetByteArrayAsync(url);
|
||||
await File.WriteAllBytesAsync(savePath, bytes);
|
||||
Console.WriteLine($"mapping file downloaded to: {savePath}");
|
||||
return savePath;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"failed to download mapping: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
// mapping
|
||||
string mappingPath = Path.Combine(Path.GetTempPath(), "DeadByDaylight.usmap");
|
||||
string? downloadedMapping = await DownloadMappingFileAsync(_mappingURL, mappingPath);
|
||||
|
||||
if (string.IsNullOrEmpty(downloadedMapping))
|
||||
{
|
||||
Console.WriteLine("failed to download mapping file, press any key to exit");
|
||||
Console.WriteLine($"Dumper error: {err}");
|
||||
Console.WriteLine("Press any key to exit");
|
||||
Console.ReadKey();
|
||||
return;
|
||||
}
|
||||
|
||||
ZlibHelper.Initialize();
|
||||
|
||||
var oodlePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OodleHelper.OodleFileName);
|
||||
OodleHelper.Initialize(oodlePath);
|
||||
|
||||
|
||||
// parsing setup
|
||||
var version = new VersionContainer(EGame.GAME_DeadByDaylight, ETexturePlatform.DesktopMobile);
|
||||
var provider = new DefaultFileProvider(_pakDir, SearchOption.TopDirectoryOnly, version);
|
||||
provider.MappingsContainer = new FileUsmapTypeMappingsProvider(downloadedMapping);
|
||||
|
||||
provider.Initialize();
|
||||
provider.SubmitKey(new FGuid(), new FAesKey(_aesKey));
|
||||
provider.Mount();
|
||||
|
||||
Console.WriteLine("\nProvider Initialized. Extracting Databases...");
|
||||
|
||||
var dataPak = provider.GetArchive("pakchunk4-WinGDK.utoc");
|
||||
|
||||
/*
|
||||
* itemdb dump
|
||||
*/
|
||||
var searchPaths = dataPak.Files.Keys.Where(x => x.Contains($"/ItemDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
var camperItems = new List<string>();
|
||||
|
||||
var slasherPowers = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
Console.WriteLine($"getting items / powers from {Path.GetFullPath(path)}");
|
||||
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
var roleProperty = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
var typeProperty = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Type");
|
||||
if (typeProperty?.Tag is EnumProperty typeProp && roleProperty?.Tag is EnumProperty roleProp)
|
||||
{
|
||||
string typeName = typeProp.Value.ToString() ?? "null";
|
||||
string roleName = roleProp.Value.ToString() ?? "null";
|
||||
|
||||
if (roleName == "EPlayerRole::VE_Slasher")
|
||||
slasherPowers.Add(row.Key.Text);
|
||||
else
|
||||
camperItems.Add(row.Key.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var itemsSerialized = new
|
||||
{
|
||||
Campers = camperItems.OrderBy(x => x).ToList(),
|
||||
Slashers = slasherPowers.OrderBy(x => x).ToList()
|
||||
};
|
||||
File.WriteAllText("items.json", JsonConvert.SerializeObject(itemsSerialized, Formatting.Indented));
|
||||
|
||||
/*
|
||||
* addon dump
|
||||
*/
|
||||
searchPaths = dataPak.Files.Keys.Where(x => x.Contains($"/ItemAddonDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
var camperAddons = new List<string>();
|
||||
var slasherAddons = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
Console.WriteLine($"getting item / power addons from {Path.GetFullPath(path)}");
|
||||
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
bool isSlasherAddon = false;
|
||||
var roleProperty = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProperty?.Tag is EnumProperty roleEnumProp)
|
||||
{
|
||||
string roleName = roleEnumProp.Value.ToString() ?? "null";
|
||||
if (roleName == "EPlayerRole::VE_Slasher")
|
||||
isSlasherAddon = true;
|
||||
}
|
||||
|
||||
if (isSlasherAddon)
|
||||
slasherAddons.Add(row.Key.Text);
|
||||
else
|
||||
camperAddons.Add(row.Key.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addonsSerialized = new
|
||||
{
|
||||
Slashers = slasherAddons.OrderBy(x => x).ToList(),
|
||||
Campers = camperAddons.OrderBy(x => x).ToList()
|
||||
};
|
||||
File.WriteAllText("addons.json", JsonConvert.SerializeObject(addonsSerialized, Formatting.Indented));
|
||||
|
||||
/*
|
||||
* offerings
|
||||
*/
|
||||
searchPaths = dataPak.Files.Keys.Where(x => x.Contains($"/OfferingDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var camperOfferings = new List<string>();
|
||||
var slasherOfferings = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
Console.WriteLine($"getting offerings from {Path.GetFullPath(path)}");
|
||||
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
var roleProperty = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProperty?.Tag is EnumProperty roleEnumProp)
|
||||
{
|
||||
string roleName = roleEnumProp.Value.ToString() ?? "null";
|
||||
if (roleName == "EPlayerRole::VE_Slasher")
|
||||
slasherOfferings.Add(row.Key.Text);
|
||||
else
|
||||
camperOfferings.Add(row.Key.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var offeringsSerialized = new
|
||||
{
|
||||
Slashers = slasherOfferings.OrderBy(x => x).ToList(),
|
||||
Campers = camperOfferings.OrderBy(x => x).ToList()
|
||||
};
|
||||
File.WriteAllText("offerings.json", JsonConvert.SerializeObject(offeringsSerialized, Formatting.Indented));
|
||||
|
||||
/*
|
||||
* perks
|
||||
*/
|
||||
searchPaths = dataPak.Files.Keys.Where(x => x.Contains($"/PerkDB.uasset", StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var slasherPerks = new List<string>();
|
||||
var camperPerks = new List<string>();
|
||||
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
var cleanPath = path.Contains('.') ? path.Substring(0, path.LastIndexOf('.')) : path;
|
||||
if (provider.TryLoadPackageObject<UDataTable>(cleanPath, out var dataTable))
|
||||
{
|
||||
Console.WriteLine($"getting perks from {Path.GetFullPath(path)}");
|
||||
|
||||
foreach (var row in dataTable.RowMap)
|
||||
{
|
||||
var roleProperty = row.Value.Properties.FirstOrDefault(p => p.Name.Text == "Role");
|
||||
if (roleProperty?.Tag is EnumProperty roleEnumProp)
|
||||
{
|
||||
string roleName = roleEnumProp.Value.ToString() ?? "null";
|
||||
if (roleName == "EPlayerRole::VE_Slasher")
|
||||
slasherPerks.Add(row.Key.Text);
|
||||
else
|
||||
camperPerks.Add(row.Key.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var perksSerialized = new
|
||||
{
|
||||
Slashers = slasherPerks.OrderBy(x => x).ToList(),
|
||||
Campers = camperPerks.OrderBy(x => x).ToList()
|
||||
};
|
||||
File.WriteAllText("perks.json", JsonConvert.SerializeObject(perksSerialized, Formatting.Indented));
|
||||
|
||||
Console.WriteLine("\nAll dumper operations finished. Press any key to Close.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Console.WriteLine("Dumper start");
|
||||
var dumper = new Dumper();
|
||||
|
||||
bool hasInitialized = await dumper.InitAsync();
|
||||
if (!hasInitialized) return displayError("Dumper.InitAsync failed");
|
||||
|
||||
Console.WriteLine("Dumper initialized");
|
||||
|
||||
string? customizationItemsJSON = dumper.DumpCustomizationItems();
|
||||
if (customizationItemsJSON != null)
|
||||
{
|
||||
File.WriteAllText("customizations.json", customizationItemsJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to dump customizations");
|
||||
}
|
||||
|
||||
string? itemsJSON = dumper.DumpItems();
|
||||
if (itemsJSON != null)
|
||||
{
|
||||
File.WriteAllText("items.json", itemsJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to dump items");
|
||||
}
|
||||
|
||||
string? addonsJSON = dumper.DumpAddons();
|
||||
if (addonsJSON != null)
|
||||
{
|
||||
File.WriteAllText("addons.json", addonsJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to dump addons");
|
||||
}
|
||||
|
||||
|
||||
string? offeringsJSON = dumper.DumpOfferings();
|
||||
if (offeringsJSON != null)
|
||||
{
|
||||
File.WriteAllText("offerings.json", offeringsJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to dump offerings");
|
||||
}
|
||||
|
||||
string? perksJSON = dumper.DumpPerks();
|
||||
if (perksJSON != null)
|
||||
{
|
||||
File.WriteAllText("perks.json", perksJSON);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to dump perks");
|
||||
}
|
||||
|
||||
Console.WriteLine("Dumper finished");
|
||||
|
||||
return 0;
|
||||
@@ -0,0 +1,39 @@
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using System.Reflection;
|
||||
|
||||
public static class StructMapper
|
||||
{
|
||||
public static T MapToStruct<T>(this FStructFallback fallback) where T : struct
|
||||
{
|
||||
object result = new T();
|
||||
var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
try
|
||||
{
|
||||
var method = typeof(CUE4Parse.UE4.Assets.Exports.AbstractPropertyHolder)
|
||||
.GetMethods()
|
||||
.FirstOrDefault(m => m.Name == "GetOrDefault" && m.GetGenericArguments().Length == 1);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
var genericMethod = method.MakeGenericMethod(field.FieldType);
|
||||
|
||||
var value = genericMethod.Invoke(fallback, new object[] { field.Name, null!, StringComparison.Ordinal });
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
field.SetValue(result, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Mapping failed for {field.Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return (T)result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user