mirror of
https://github.com/4sval/FModel.git
synced 2026-03-21 17:24:26 -05:00
Uhm ok so I guess that's the big commit
- Added utoc/ucas (io store) loading support - Added new unversioned asset parsing support ONLY for asset we have mappings yet - Integrated that new parsing as good as possible into fmodel's old structure (which certainly wasn't prepared for such a change lol) - ONLY for new package format disabled caching of files in debug mode (was annoying) - Type and enum mappings while by default be loaded from a github repo BUT in debug mode it will first attempt to load them from local files (relative to the exe, also for debugging conveniance) but fallback to the ones from the repo - F12 hotkey for reloading current type mappings (Mainly made for debug but also works in release)
This commit is contained in:
parent
3835717dfc
commit
1b2f6c636f
|
|
@ -2,8 +2,8 @@
|
|||
using System.Windows;
|
||||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using FModel.Creator.Bundles;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using System.Collections.Generic;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
@ -53,7 +53,7 @@ namespace FModel.Creator.Bases
|
|||
{
|
||||
foreach (SoftObjectProperty questPath in careerQuestBitShifts.Value)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(questPath.Value.AssetPathName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(questPath.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,16 +7,14 @@ using FModel.Creator.Icons;
|
|||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Properties;
|
||||
using FModel.Utils;
|
||||
|
||||
using Fortnite_API.Objects;
|
||||
using Fortnite_API.Objects.V1;
|
||||
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
|
|
@ -68,7 +66,7 @@ namespace FModel.Creator.Bases
|
|||
LargeSmallImage.GetPreviewImage(this, previewImage);
|
||||
else if (export.GetExport<ObjectProperty>("access_item") is { } accessItem)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(accessItem.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(accessItem.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ namespace FModel.Creator.Bases
|
|||
if (export.GetExport<ObjectProperty>("access_item") is ObjectProperty accessItem)
|
||||
{
|
||||
SItem = accessItem.Value.Resource.ObjectName.String;
|
||||
PakPackage p = Utils.GetPropertyPakPackage(accessItem.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(accessItem.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
using System.Windows;
|
||||
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Utils;
|
||||
|
||||
using Fortnite_API.Objects;
|
||||
using Fortnite_API.Objects.V1;
|
||||
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
using FModel.Creator.Bundles;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Documents;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
using System.Collections.Generic;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using FModel.Utils;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using System;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.IO;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
|
|
@ -37,7 +37,7 @@ namespace FModel.Creator.Bundles
|
|||
|
||||
if (obj.TryGetValue("RewardsTable", out var v4) && v4 is ObjectProperty rewardsTable)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(rewardsTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(rewardsTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var u = p.GetExport<UDataTable>();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
|
|
@ -37,7 +37,7 @@ namespace FModel.Creator.Bundles
|
|||
string[] parts = assetName.Split(':');
|
||||
if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage($"/Game/Items/BannerIcons/{parts[1]}.{parts[1]}");
|
||||
Package p = Utils.GetPropertyPakPackage($"/Game/Items/BannerIcons/{parts[1]}.{parts[1]}");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
if (p.GetExport<UObject>() is UObject banner)
|
||||
|
|
@ -54,7 +54,7 @@ namespace FModel.Creator.Bundles
|
|||
public Reward(IntProperty quantity, FSoftObjectPath itemFullPath)
|
||||
{
|
||||
RewardQuantity = quantity.Value;
|
||||
PakPackage p = Utils.GetPropertyPakPackage(itemFullPath.AssetPathName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(itemFullPath.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
|
|
@ -71,7 +71,7 @@ namespace FModel.Creator.Bundles
|
|||
{
|
||||
RewardQuantity = quantity.Value;
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(itemDefinition.Value.AssetPathName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(itemDefinition.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
|
|
@ -135,7 +135,7 @@ namespace FModel.Creator.Bundles
|
|||
default:
|
||||
{
|
||||
string path = Utils.GetFullPath($"/FortniteGame/Content/Athena/.*?/{trigger}.*").Replace("FortniteGame/Content", "Game");
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
Package p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ using FModel.Creator.Rarities;
|
|||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.ViewModels.ImageBox;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using SkiaSharp;
|
||||
using System.IO;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.Creator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
|
|
@ -33,8 +33,8 @@ namespace FModel.Creator.Icons
|
|||
if (string.IsNullOrEmpty(path))
|
||||
path = "/Game/Catalog/DisplayAssets/DA_Featured_" + assetName.Substring(0, assetName.LastIndexOf("."));
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
Package p = Utils.GetPropertyPakPackage(path);
|
||||
if (p != null && p.HasExport())
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
|
|
@ -16,11 +16,11 @@ namespace FModel.Creator.Icons
|
|||
public static void GetPreviewImage(BaseIcon icon, ObjectProperty o, string assetName) => GetPreviewImage(icon, o, assetName, true);
|
||||
public static void GetPreviewImage(BaseIcon icon, ObjectProperty o, string assetName, bool hightRes)
|
||||
{
|
||||
string path = o.Value.Resource.OuterIndex.Resource.ObjectName.String;
|
||||
if (path.Equals("/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_STWCosmetic_Tier"))
|
||||
string path = o.Value.Resource?.OuterIndex.Resource?.ObjectName.String;
|
||||
if (path?.Equals("/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_STWCosmetic_Tier") == true)
|
||||
path += "_" + assetName.Substring(assetName.LastIndexOf(".") - 1, 1);
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
Package p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
if (GetPreviewImage(icon, p.GetIndexedExport<UObject>(0), hightRes))
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
|
|
@ -16,7 +16,7 @@ namespace FModel.Creator.Icons
|
|||
{
|
||||
if (uffs.Count > 0)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Items/ItemCategories"); //PrimaryCategories - SecondaryCategories - TertiaryCategories
|
||||
Package p = Utils.GetPropertyPakPackage("/Game/Items/ItemCategories"); //PrimaryCategories - SecondaryCategories - TertiaryCategories
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var o = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Linq;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Rarities
|
||||
{
|
||||
|
|
@ -12,8 +12,8 @@ namespace FModel.Creator.Rarities
|
|||
{
|
||||
public static void GetInGameRarity(BaseIcon icon, EnumProperty e)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Balance/RarityData");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
Package p = Utils.GetPropertyPakPackage("/Game/Balance/RarityData");
|
||||
if (p != null && p.HasExport())
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
if (d != null)
|
||||
|
|
@ -68,13 +68,13 @@ namespace FModel.Creator.Rarities
|
|||
|
||||
public static void GetInGameRarity(BaseGCosmetic icon, EnumProperty e)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/UI/UIKit/DT_RarityColors");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
Package p = Utils.GetPropertyPakPackage("/Game/UI/UIKit/DT_RarityColors");
|
||||
if (p != null || p.HasExport())
|
||||
{
|
||||
var d = p.GetExport<UDataTable>();
|
||||
if (d != null)
|
||||
{
|
||||
if (e != null && d.TryGetValue(e?.Value.String["EXRarity::".Length..], out object r) && r is UObject rarity &&
|
||||
if (e != null && d.TryGetValue(e.Value.String["EXRarity::".Length..], out object r) && r is UObject rarity &&
|
||||
rarity.GetExport<ArrayProperty>("Colors") is ArrayProperty colors &&
|
||||
colors.Value[0] is StructProperty s1 && s1.Value is FLinearColor color1 &&
|
||||
colors.Value[1] is StructProperty s2 && s2.Value is FLinearColor color2 &&
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Rarities
|
||||
|
|
@ -11,7 +11,7 @@ namespace FModel.Creator.Rarities
|
|||
{
|
||||
public static void GetRarity(BaseIcon icon, ObjectProperty o)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(o.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(o.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Stats
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@ namespace FModel.Creator.Stats
|
|||
{
|
||||
if (!ammoData.Value.AssetPathName.String.StartsWith("/Game/Athena/Items/Consumables/"))
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(ammoData.Value.AssetPathName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(ammoData.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
|
|
@ -43,7 +43,7 @@ namespace FModel.Creator.Stats
|
|||
o1.TryGetValue("DataTable", out var c1) && c1 is ObjectProperty dataTable &&
|
||||
o1.TryGetValue("RowName", out var c2) && c2 is NameProperty rowName)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(dataTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(dataTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UDataTable>();
|
||||
|
|
@ -79,7 +79,7 @@ namespace FModel.Creator.Stats
|
|||
|
||||
public static void GetHeroStats(BaseIcon icon, ObjectProperty heroGameplayDefinition)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(heroGameplayDefinition.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(heroGameplayDefinition.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
|
|
@ -108,7 +108,7 @@ namespace FModel.Creator.Stats
|
|||
{
|
||||
if (parent.TryGetValue("GrantedAbilityKit", out var v) && v is SoftObjectProperty grantedAbilityKit)
|
||||
{
|
||||
PakPackage k = Utils.GetPropertyPakPackage(grantedAbilityKit.Value.AssetPathName.String);
|
||||
Package k = Utils.GetPropertyPakPackage(grantedAbilityKit.Value.AssetPathName.String);
|
||||
if (k.HasExport() && !k.Equals(default))
|
||||
{
|
||||
var kit = k.GetExport<UObject>();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Icons;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Texts
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ namespace FModel.Creator.Texts
|
|||
|
||||
private static string GetCosmeticSet(string setName)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Athena/Items/Cosmetics/Metadata/CosmeticSets");
|
||||
Package p = Utils.GetPropertyPakPackage("/Game/Athena/Items/Cosmetics/Metadata/CosmeticSets");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UDataTable>();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ namespace FModel.Creator.Texts
|
|||
return b.SourceString.Replace("<Emphasized>", string.Empty).Replace("</>", string.Empty);
|
||||
else if (text.Text is FTextHistory.StringTableEntry s)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(s.TableId.String);
|
||||
Package p = Utils.GetPropertyPakPackage(s.TableId.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UStringTable>();
|
||||
|
|
@ -69,7 +69,7 @@ namespace FModel.Creator.Texts
|
|||
o2.TryGetValue("CurveTable", out var c2) && c2 is ObjectProperty curveTable &&
|
||||
o2.TryGetValue("RowName", out var c3) && c3 is NameProperty rowName) // new way
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(curveTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(curveTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UCurveTable>();
|
||||
|
|
@ -98,7 +98,7 @@ namespace FModel.Creator.Texts
|
|||
o2.TryGetValue("CurveTable", out var c2) && c2 is ObjectProperty curveTable &&
|
||||
o2.TryGetValue("RowName", out var c3) && c3 is NameProperty rowName) // new way
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(curveTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
Package p = Utils.GetPropertyPakPackage(curveTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UCurveTable>();
|
||||
|
|
|
|||
|
|
@ -82,8 +82,13 @@ namespace FModel.Creator.Texts
|
|||
if (Globals.Game.ActualGame == EGame.Fortnite)
|
||||
{
|
||||
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
if (t != null && t.Length == 3)
|
||||
{
|
||||
if (t[0].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[0].AsStream());
|
||||
if (BundleDefaultTypeface != null && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else BundleDefaultTypeface = DefaultTypeface;
|
||||
|
||||
string namePath = _FORTNITE_BASE_PATH + (
|
||||
|
|
@ -97,8 +102,13 @@ namespace FModel.Creator.Texts
|
|||
if (!namePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(namePath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
if (t != null && t.Length == 3)
|
||||
{
|
||||
if (t[0].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[0].AsStream());
|
||||
if (DisplayNameTypeface != null && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
}
|
||||
else DisplayNameTypeface = DefaultTypeface;
|
||||
|
||||
|
|
@ -110,8 +120,13 @@ namespace FModel.Creator.Texts
|
|||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? string.Empty :
|
||||
_BURBANK_SMALL_BOLD);
|
||||
t = Utils.GetPropertyArraySegmentByte(bottomPath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BottomDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
if (t != null && t.Length == 3)
|
||||
{
|
||||
if (t[0].Array != null)
|
||||
BottomDefaultTypeface = SKTypeface.FromStream(t[0].AsStream());
|
||||
if (BottomDefaultTypeface != null && t[2].Array != null)
|
||||
BottomDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
|
||||
string descriptionPath = _FORTNITE_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _NOTO_SANS_KR_REGULAR :
|
||||
|
|
@ -121,8 +136,13 @@ namespace FModel.Creator.Texts
|
|||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_REGULAR :
|
||||
_NOTO_SANS_REGULAR);
|
||||
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
if (t != null && t.Length == 3)
|
||||
{
|
||||
if (t[0].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[0].AsStream());
|
||||
if (DescriptionTypeface != null && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else DescriptionTypeface = DefaultTypeface;
|
||||
|
||||
string bundleNamePath = _FORTNITE_BASE_PATH + (
|
||||
|
|
@ -136,8 +156,13 @@ namespace FModel.Creator.Texts
|
|||
if (!bundleNamePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(bundleNamePath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
if (t != null && t.Length == 3)
|
||||
{
|
||||
if (t[0].Array != null)
|
||||
BundleDisplayNameTypeface = SKTypeface.FromStream(t[0].AsStream());
|
||||
if (BundleDisplayNameTypeface != null && t[2].Array != null)
|
||||
BundleDisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
}
|
||||
else BundleDisplayNameTypeface = BundleDefaultTypeface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator
|
||||
{
|
||||
static class Utils
|
||||
{
|
||||
public static string GetFullPath(FPackageId id)
|
||||
{
|
||||
foreach (var ioStore in Globals.CachedIoStores.Values)
|
||||
{
|
||||
if (ioStore.IsInitialized)
|
||||
{
|
||||
var entry = ioStore.Files.FirstOrDefault(it => it.Value.ChunkId.ChunkId == id.Id).Value;
|
||||
if (entry != null)
|
||||
return ioStore.MountPoint + entry.Name;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetFullPath(string partialPath)
|
||||
{
|
||||
|
|
@ -18,20 +34,35 @@ namespace FModel.Creator
|
|||
{
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
foreach (var ioStoreReader in Globals.CachedIoStores.Values)
|
||||
{
|
||||
if (ioStoreReader.TryGetPartialKey(partialPath, out var fullPath))
|
||||
{
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static PakPackage GetPropertyPakPackage(string value)
|
||||
public static Package GetPropertyPakPackage(string value)
|
||||
{
|
||||
string path = Strings.FixPath(value);
|
||||
foreach (var fileReader in Globals.CachedPakFiles.Values)
|
||||
if (fileReader.TryGetCaseInsensiteveValue(path, out var entry))
|
||||
{
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length);
|
||||
return Assets.GetPakPackage(entry, mount);
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf('.')).Length);
|
||||
return Assets.GetPackage(entry, mount);
|
||||
}
|
||||
return default;
|
||||
foreach (var ioStoreReader in Globals.CachedIoStores.Values)
|
||||
if (ioStoreReader.TryGetCaseInsensiteveValue(path, out var entry))
|
||||
{
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf('.')).Length);
|
||||
return Assets.GetPackage(entry, mount);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte>[] GetPropertyArraySegmentByte(string value)
|
||||
|
|
@ -41,7 +72,14 @@ namespace FModel.Creator
|
|||
if (fileReader.TryGetCaseInsensiteveValue(path, out var entry))
|
||||
{
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length);
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf('.')).Length);
|
||||
return Assets.GetArraySegmentByte(entry, mount);
|
||||
}
|
||||
foreach (var ioStoreReader in Globals.CachedIoStores.Values)
|
||||
if (ioStoreReader.TryGetCaseInsensiteveValue(path, out var entry))
|
||||
{
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf('.')).Length);
|
||||
return Assets.GetArraySegmentByte(entry, mount);
|
||||
}
|
||||
return default;
|
||||
|
|
@ -74,7 +112,7 @@ namespace FModel.Creator
|
|||
s = "/Game/UI/Textures/assets/cosmetics/skins/headshot/Skin_Headshot_TimeWeaver_UIT";
|
||||
}
|
||||
|
||||
PakPackage p = GetPropertyPakPackage(s);
|
||||
var p = GetPropertyPakPackage(s);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var i = p.GetExport<UTexture2D>();
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@
|
|||
<PackageReference Include="AvalonEdit" Version="6.0.1" />
|
||||
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
|
||||
<PackageReference Include="DotNetZip" Version="1.13.8" />
|
||||
<PackageReference Include="DotNetZip" Version="1.14.0" />
|
||||
<PackageReference Include="EpicManifestParser" Version="1.2.0" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.0.1" />
|
||||
<PackageReference Include="Fortnite-API-Wrapper" Version="2.1.0" />
|
||||
|
|
@ -149,6 +149,7 @@
|
|||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="1.1.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.2" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
|
||||
<PackageReference Include="System.Data.HashFunction.CityHash" Version="2.0.0" />
|
||||
<PackageReference Include="ToastNotifications" Version="2.5.1" />
|
||||
<PackageReference Include="ToastNotifications.Messages" Version="2.5.1" />
|
||||
<PackageReference Include="WriteableBitmapEx" Version="1.6.7" />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Objects;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Pak;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.Properties;
|
||||
using ToastNotifications;
|
||||
using ToastNotifications.Lifetime;
|
||||
|
|
@ -17,6 +18,10 @@ namespace FModel
|
|||
/// PakFileReader is the reader where you can grab the FPakEntries, MountPoint and more
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, PakFileReader> CachedPakFiles = new Dictionary<string, PakFileReader>();
|
||||
public static readonly Dictionary<string, FFileIoStoreReader> CachedIoStores = new Dictionary<string, FFileIoStoreReader>();
|
||||
public static FIoGlobalData GlobalData = null;
|
||||
public static Dictionary<string, Dictionary<int, PropertyInfo>> TypeMappings;
|
||||
public static Dictionary<string, Dictionary<int, string>> EnumMappings;
|
||||
public static readonly Notifier gNotifier = new Notifier(cfg =>
|
||||
{
|
||||
cfg.LifetimeSupervisor = new TimeAndCountBasedLifetimeSupervisor(TimeSpan.FromSeconds(7), MaximumNotificationCount.FromCount(15));
|
||||
|
|
|
|||
|
|
@ -8,11 +8,9 @@ using System.Runtime.CompilerServices;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using FModel.PakReader;
|
||||
using FModel.Utils;
|
||||
|
||||
using PakReader;
|
||||
|
||||
namespace FModel.Grabber.Manifests
|
||||
{
|
||||
public class ValorantAPIManifest
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ using EpicManifestParser.Objects;
|
|||
|
||||
using FModel.Grabber.Manifests;
|
||||
using FModel.Logger;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Pak;
|
||||
using FModel.Utils;
|
||||
using FModel.ViewModels.MenuItem;
|
||||
using FModel.Windows.Launcher;
|
||||
|
||||
using PakReader.Pak;
|
||||
|
||||
namespace FModel.Grabber.Paks
|
||||
{
|
||||
static class PaksGrabber
|
||||
|
|
@ -143,6 +143,7 @@ namespace FModel.Grabber.Paks
|
|||
// define the current game thank to the pak path
|
||||
Folders.SetGameName(Properties.Settings.Default.PakPath);
|
||||
|
||||
// paks
|
||||
string[] paks = Directory.GetFiles(Properties.Settings.Default.PakPath, "*.pak");
|
||||
for (int i = 0; i < paks.Length; i++)
|
||||
{
|
||||
|
|
@ -171,6 +172,33 @@ namespace FModel.Grabber.Paks
|
|||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[PAK]", "[Locked]", paks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// io stores
|
||||
var utocs = Directory.GetFiles(Properties.Settings.Default.PakPath, "*.utoc");
|
||||
foreach (var utoc in utocs)
|
||||
{
|
||||
var ucas = utoc.Replace(".utoc", ".ucas");
|
||||
if (!Utils.Paks.IsFileReadLocked(new FileInfo(utoc)) && !Utils.Paks.IsFileReadLocked(new FileInfo(ucas)))
|
||||
{
|
||||
var utocStream = new MemoryStream(await File.ReadAllBytesAsync(utoc));
|
||||
var ucasStream = File.OpenRead(ucas);
|
||||
var ioStore = new FFileIoStoreReader(ucas.SubstringAfterLast('\\'), utocStream, ucasStream);
|
||||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[IO Store]", "[Registering]", $"{ioStore.FileName} with GUID {ioStore.TocResource.Header.EncryptionKeyGuid.Hex}");
|
||||
await Application.Current.Dispatcher.InvokeAsync(delegate
|
||||
{
|
||||
MenuItems.pakFiles.Add(new PakMenuItemViewModel
|
||||
{
|
||||
IoStore = ioStore,
|
||||
IsEnabled = false
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
FConsole.AppendText(string.Format(Properties.Resources.PakFileLocked, Path.GetFileNameWithoutExtension(utoc)), FColors.Red, true);
|
||||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[IO Store]", "[Locked]", utoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<CommandBinding Command="{x:Static utils:Commands.AutoSaveImage}" Executed="OnAutoShortcutPressed"/>
|
||||
<CommandBinding Command="{x:Static utils:Commands.AutoOpenSounds}" Executed="OnAutoShortcutPressed"/>
|
||||
<CommandBinding Command="{x:Static utils:Commands.OpenImageDoubleClick}" Executed="OnImageOpenClick"/>
|
||||
<CommandBinding Command="{x:Static utils:Commands.ReloadTypeMappings}" Executed="ReloadMappings"/>
|
||||
</Window.CommandBindings>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="F1" Command="{x:Static utils:Commands.OpenGeneralSettings}"/>
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
<KeyBinding Key="F5" Command="{x:Static utils:Commands.AutoSave}"/>
|
||||
<KeyBinding Key="F6" Command="{x:Static utils:Commands.AutoSaveImage}"/>
|
||||
<KeyBinding Key="F7" Command="{x:Static utils:Commands.AutoOpenSounds}"/>
|
||||
<KeyBinding Key="F12" Command="{x:Static utils:Commands.ReloadTypeMappings}"/>
|
||||
</Window.InputBindings>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
|
|
|
|||
|
|
@ -23,14 +23,19 @@ using FModel.Windows.Search;
|
|||
using FModel.Windows.Settings;
|
||||
using FModel.Windows.SoundPlayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using PropertyInfo = FModel.PakReader.IO.PropertyInfo;
|
||||
|
||||
namespace FModel
|
||||
{
|
||||
|
|
@ -72,6 +77,8 @@ namespace FModel
|
|||
if (!Properties.Settings.Default.SkipVersion) Updater.CheckForUpdate();
|
||||
DebugHelper.WriteUserSettings();
|
||||
Folders.CheckWatermarks();
|
||||
|
||||
LoadMappings();
|
||||
|
||||
await Task.WhenAll(Init()).ContinueWith(t =>
|
||||
{
|
||||
|
|
@ -97,6 +104,52 @@ namespace FModel
|
|||
await Folders.DownloadAndExtractVgm().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void LoadMappings()
|
||||
{
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
string rawMappings = null;
|
||||
string rawEnumMappings = null;
|
||||
try
|
||||
{
|
||||
rawMappings = await File.ReadAllTextAsync("TypeMappings.json");
|
||||
rawEnumMappings = await File.ReadAllTextAsync("EnumMappings.json");
|
||||
}
|
||||
catch
|
||||
{
|
||||
rawMappings ??= await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_TYPE_MAPPINGS);
|
||||
rawEnumMappings ??= await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_ENUM_MAPPINGS);
|
||||
}
|
||||
#else
|
||||
var rawMappings = await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_TYPE_MAPPINGS);
|
||||
var rawEnumMappings = await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_ENUM_MAPPINGS);
|
||||
#endif
|
||||
var serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{NamingStrategy = new CamelCaseNamingStrategy(false, false)}
|
||||
};
|
||||
Globals.TypeMappings =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, Dictionary<int, PropertyInfo>>>(rawMappings,
|
||||
serializerSettings);
|
||||
Globals.EnumMappings = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<int, string>>>(rawEnumMappings,
|
||||
serializerSettings);
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
DebugHelper.WriteException(exception, "Failed to load Mappings");
|
||||
Globals.TypeMappings ??= new Dictionary<string, Dictionary<int, PropertyInfo>>();
|
||||
Globals.EnumMappings ??= new Dictionary<string, Dictionary<int, string>>();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadMappings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LoadMappings();
|
||||
}
|
||||
|
||||
private void AeConfiguration()
|
||||
{
|
||||
AvalonEditFindReplaceHelper Frm = new AvalonEditFindReplaceHelper
|
||||
|
|
@ -206,7 +259,7 @@ namespace FModel
|
|||
FModel_AssetsList.SelectedItem is ListBoxViewModel selectedItem)
|
||||
{
|
||||
bool autoExport = FModel_AssetsList.SelectedItems.Count > 1;
|
||||
if (!autoExport) Assets.Export(selectedItem.PakEntry, false); // manual export if one
|
||||
if (!autoExport) Assets.Export(selectedItem.ReaderEntry, false); // manual export if one
|
||||
else
|
||||
{
|
||||
bool ret = Properties.Settings.Default.AutoExport;
|
||||
|
|
@ -375,7 +428,7 @@ namespace FModel
|
|||
{
|
||||
FModel_TabCtrl.SelectedIndex = 1;
|
||||
ExtractStopVm.extractViewModel.IsEnabled = true;
|
||||
AssetPropertiesVm.assetPropertiesViewModel.Set(selectedItem.PakEntry);
|
||||
AssetPropertiesVm.assetPropertiesViewModel.Set(selectedItem.ReaderEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace PakReader
|
||||
namespace FModel.PakReader
|
||||
{
|
||||
static class AESDecryptor
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PakReader
|
||||
namespace FModel.PakReader
|
||||
{
|
||||
static class BinaryHelper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Addressable chunk types.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public enum EIoContainerFlags : byte
|
||||
{
|
||||
16
FModel/PakReader/IO/FArc.cs
Normal file
16
FModel/PakReader/IO/FArc.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FArc
|
||||
{
|
||||
public readonly uint FromNodeIndex;
|
||||
public readonly uint ToNodeIndex;
|
||||
|
||||
public FArc(BinaryReader reader)
|
||||
{
|
||||
FromNodeIndex = reader.ReadUInt32();
|
||||
ToNodeIndex = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
FModel/PakReader/IO/FContainerHeader.cs
Normal file
37
FModel/PakReader/IO/FContainerHeader.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FContainerHeader
|
||||
{
|
||||
public readonly FIoContainerId ContainerId;
|
||||
public readonly uint PackageCount;
|
||||
public readonly byte[] Names;
|
||||
public readonly byte[] NameHashes;
|
||||
public readonly FPackageId[] PackageIds;
|
||||
public readonly byte[] StoreEntries;
|
||||
public readonly Dictionary<string, (FPackageId source, FPackageId localized)[]> CulturePackageMap;
|
||||
public readonly (FPackageId source, FPackageId target)[] PackageRedirects;
|
||||
|
||||
public FContainerHeader(BinaryReader reader)
|
||||
{
|
||||
ContainerId = new FIoContainerId(reader);
|
||||
PackageCount = reader.ReadUInt32();
|
||||
Names = reader.ReadBytes(reader.ReadInt32());
|
||||
NameHashes = reader.ReadBytes(reader.ReadInt32());
|
||||
PackageIds = reader.ReadTArray(() => new FPackageId(reader));
|
||||
StoreEntries = reader.ReadBytes(reader.ReadInt32());
|
||||
var culturePackageMapCount = reader.ReadInt32();
|
||||
CulturePackageMap = new Dictionary<string, (FPackageId source, FPackageId localized)[]>(culturePackageMapCount);
|
||||
for (int i = 0; i < culturePackageMapCount; i++)
|
||||
{
|
||||
CulturePackageMap.Add(
|
||||
reader.ReadFString(),
|
||||
reader.ReadTArray(() => (new FPackageId(reader), new FPackageId(reader))));
|
||||
}
|
||||
|
||||
PackageRedirects = reader.ReadTArray(() => (new FPackageId(reader), new FPackageId(reader)));
|
||||
}
|
||||
}
|
||||
}
|
||||
25
FModel/PakReader/IO/FExportBundleEntry.cs
Normal file
25
FModel/PakReader/IO/FExportBundleEntry.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public struct FExportBundleEntry
|
||||
{
|
||||
public const int SIZE = 8;
|
||||
|
||||
public uint LocalExportIndex;
|
||||
public EExportCommandType CommandType;
|
||||
|
||||
public FExportBundleEntry(BinaryReader reader)
|
||||
{
|
||||
LocalExportIndex = reader.ReadUInt32();
|
||||
CommandType = (EExportCommandType) reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public enum EExportCommandType
|
||||
{
|
||||
ExportCommandType_Create,
|
||||
ExportCommandType_Serialize,
|
||||
ExportCommandType_Count
|
||||
};
|
||||
}
|
||||
16
FModel/PakReader/IO/FExportDesc.cs
Normal file
16
FModel/PakReader/IO/FExportDesc.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FExportDesc
|
||||
{
|
||||
public FPackageDesc Package = null;
|
||||
public FName Name;
|
||||
public FName FullName;
|
||||
public FPackageObjectIndex OuterIndex;
|
||||
public FPackageObjectIndex ClassIndex;
|
||||
public FPackageObjectIndex SuperIndex;
|
||||
public FPackageObjectIndex TemplateIndex;
|
||||
public FPackageObjectIndex GlobalImportIndex;
|
||||
}
|
||||
}
|
||||
37
FModel/PakReader/IO/FExportMapEntry.cs
Normal file
37
FModel/PakReader/IO/FExportMapEntry.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using FModel.PakReader.Parsers;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FExportMapEntry
|
||||
{
|
||||
public const int SIZE = 72;
|
||||
|
||||
public readonly ulong CookedSerialOffset;
|
||||
public readonly ulong CookedSerialSize;
|
||||
public readonly FMappedName ObjectName;
|
||||
public readonly FPackageObjectIndex OuterIndex;
|
||||
public readonly FPackageObjectIndex ClassIndex;
|
||||
public readonly FPackageObjectIndex SuperIndex;
|
||||
public readonly FPackageObjectIndex TemplateIndex;
|
||||
public readonly FPackageObjectIndex GlobalImportIndex;
|
||||
public readonly EObjectFlags ObjectFlags;
|
||||
public readonly EExportFilterFlags FilterFlags;
|
||||
|
||||
public FExportMapEntry(IoPackageReader reader)
|
||||
{
|
||||
CookedSerialOffset = reader.ReadUInt64();
|
||||
CookedSerialSize = reader.ReadUInt64();
|
||||
ObjectName = new FMappedName(reader);
|
||||
OuterIndex = new FPackageObjectIndex(reader);
|
||||
ClassIndex = new FPackageObjectIndex(reader);
|
||||
SuperIndex = new FPackageObjectIndex(reader);
|
||||
TemplateIndex = new FPackageObjectIndex(reader);
|
||||
GlobalImportIndex = new FPackageObjectIndex(reader);
|
||||
|
||||
ObjectFlags = (EObjectFlags) reader.ReadUInt32();
|
||||
FilterFlags = (EExportFilterFlags) reader.ReadByte();
|
||||
reader.SkipBytes(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public struct FFileIoStoreContainerFile
|
||||
{
|
||||
|
|
@ -1,27 +1,34 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using FModel.Logger;
|
||||
using FModel.PakReader.Parsers;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.Utils;
|
||||
using Ionic.Zlib;
|
||||
using PakReader.Parsers;
|
||||
using PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FFileIoStoreReader
|
||||
public class FFileIoStoreReader : IReadOnlyDictionary<string, FIoStoreEntry>
|
||||
{
|
||||
public readonly string FileName;
|
||||
public readonly FIoStoreTocResource TocResource;
|
||||
public readonly Dictionary<FIoChunkId, FIoOffsetAndLength> Toc;
|
||||
public readonly FFileIoStoreContainerFile ContainerFile;
|
||||
public readonly FIoContainerId ContainerId;
|
||||
|
||||
public bool IsInitialized => Files != null;
|
||||
|
||||
private byte[] _aesKey;
|
||||
public byte[] AesKey
|
||||
{
|
||||
get => _aesKey;
|
||||
set
|
||||
{
|
||||
if (!HasDirectoryIndex) return;
|
||||
if (value != null && !TestAesKey(value)) //if value not null, test but fail, throw not working
|
||||
throw new ArgumentException(string.Format(FModel.Properties.Resources.AesNotWorking, value.ToStringKey(), ContainerFile.FileName));
|
||||
_aesKey = value; // else, even if value is null, set it
|
||||
|
|
@ -29,22 +36,29 @@ namespace PakReader.Pak.IO
|
|||
}
|
||||
}
|
||||
|
||||
public readonly bool CaseSensitive;
|
||||
|
||||
public bool HasDirectoryIndex => TocResource.DirectoryIndexBuffer != null;
|
||||
|
||||
public FGuid EncryptionKeyGuid => ContainerFile.EncryptionKeyGuid;
|
||||
public bool IsEncrypted => ContainerFile.ContainerFlags.HasAnyFlags(EIoContainerFlags.Encrypted);
|
||||
|
||||
|
||||
public Dictionary<string, FIoStoreEntry> Files;
|
||||
|
||||
public FIoDirectoryIndexResource _directoryIndex;
|
||||
private byte[] _directoryIndexBuffer;
|
||||
|
||||
public FFileIoStoreReader(Stream tocStream, Stream containerStream, EIoStoreTocReadOptions tocReadOptions = EIoStoreTocReadOptions.ReadDirectoryIndex)
|
||||
public FFileIoStoreReader(string fileName, Stream tocStream, Stream containerStream, bool caseSensitive = true, EIoStoreTocReadOptions tocReadOptions = EIoStoreTocReadOptions.ReadDirectoryIndex)
|
||||
{
|
||||
FileName = fileName;
|
||||
CaseSensitive = caseSensitive;
|
||||
ContainerFile.FileHandle = containerStream;
|
||||
var tocResource = new FIoStoreTocResource(tocStream, tocReadOptions);
|
||||
TocResource = tocResource;
|
||||
|
||||
var containerUncompressedSize = tocResource.Header.TocCompressedBlockEntryCount > 0
|
||||
? tocResource.Header.TocCompressedBlockEntryCount * tocResource.Header.CompressionBlockSize
|
||||
: containerStream.Length;
|
||||
? (ulong) tocResource.Header.TocCompressedBlockEntryCount * (ulong) tocResource.Header.CompressionBlockSize
|
||||
: (ulong) containerStream.Length;
|
||||
|
||||
Toc = new Dictionary<FIoChunkId, FIoOffsetAndLength>((int) tocResource.Header.TocEntryCount);
|
||||
|
||||
|
|
@ -79,18 +93,40 @@ namespace PakReader.Pak.IO
|
|||
_directoryIndexBuffer = tocResource.DirectoryIndexBuffer;
|
||||
}
|
||||
|
||||
public void ReadIndex()
|
||||
public bool ReadDirectoryIndex()
|
||||
{
|
||||
using Stream indexStream = IsEncrypted
|
||||
? new MemoryStream(AESDecryptor.DecryptAES(_directoryIndexBuffer, _aesKey))
|
||||
: new MemoryStream(_directoryIndexBuffer);
|
||||
_directoryIndex = new FIoDirectoryIndexResource(indexStream);
|
||||
try
|
||||
{
|
||||
if (HasDirectoryIndex)
|
||||
{
|
||||
using Stream indexStream = IsEncrypted
|
||||
? new MemoryStream(AESDecryptor.DecryptAES(_directoryIndexBuffer, _aesKey))
|
||||
: new MemoryStream(_directoryIndexBuffer);
|
||||
_directoryIndex = new FIoDirectoryIndexResource(indexStream, CaseSensitive);
|
||||
|
||||
var firstEntry = GetChildDirectory(FIoDirectoryIndexHandle.Root);
|
||||
|
||||
var tempFiles = new Dictionary<string, FIoStoreEntry>();
|
||||
ReadIndex("", firstEntry, tempFiles);
|
||||
Paks.Merge(tempFiles, out Files, _directoryIndex.MountPoint);
|
||||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[FFileIoStoreReader]", "[ReadDirectoryIndex]", $"{FileName} contains {Files.Count} files, mount point: \"{MountPoint}\", version: {(int)TocResource.Header.Version}");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugHelper.WriteLine(e.ToString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string MountPoint => _directoryIndex.MountPoint;
|
||||
|
||||
public bool TestAesKey(byte[] key)
|
||||
{
|
||||
if (!HasDirectoryIndex)
|
||||
return false;
|
||||
if (!IsEncrypted)
|
||||
return true;
|
||||
return TestAesKey(_directoryIndexBuffer, key);
|
||||
|
|
@ -122,6 +158,8 @@ namespace PakReader.Pak.IO
|
|||
}
|
||||
}
|
||||
|
||||
public bool DoesChunkExist(FIoChunkId chunkId) => Toc.ContainsKey(chunkId);
|
||||
|
||||
public byte[] Read(FIoChunkId chunkId)
|
||||
{
|
||||
var offsetAndLength = Toc[chunkId];
|
||||
|
|
@ -129,16 +167,15 @@ namespace PakReader.Pak.IO
|
|||
var compressionBlockSize = tocResource.Header.CompressionBlockSize;
|
||||
var dst = new byte[offsetAndLength.Length];
|
||||
var firstBlockIndex = (int) (offsetAndLength.Offset / compressionBlockSize);
|
||||
var lastBlockIndex = (int) ((BinaryHelper.Align(offsetAndLength.Offset + dst.Length, compressionBlockSize) - 1) / compressionBlockSize);
|
||||
var offsetInBlock = offsetAndLength.Offset % compressionBlockSize;
|
||||
|
||||
byte[] src;
|
||||
var lastBlockIndex = (int) ((BinaryHelper.Align((long) offsetAndLength.Offset + dst.Length, compressionBlockSize) - 1) / compressionBlockSize);
|
||||
var offsetInBlock = (int) offsetAndLength.Offset % compressionBlockSize;
|
||||
var remainingSize = dst.Length;
|
||||
var dstOffset = 0;
|
||||
for (int blockIndex = firstBlockIndex; blockIndex < lastBlockIndex; blockIndex++)
|
||||
|
||||
for (int blockIndex = firstBlockIndex; blockIndex <= lastBlockIndex; blockIndex++)
|
||||
{
|
||||
var compressionBlock = tocResource.CompressionBlocks[blockIndex];
|
||||
|
||||
|
||||
var rawSize = BinaryHelper.Align(compressionBlock.CompressedSize, AESDecryptor.ALIGN);
|
||||
var compressedBuffer = new byte[rawSize];
|
||||
|
||||
|
|
@ -153,6 +190,8 @@ namespace PakReader.Pak.IO
|
|||
compressedBuffer = AESDecryptor.DecryptAES(compressedBuffer, _aesKey);
|
||||
}
|
||||
|
||||
byte[] src;
|
||||
|
||||
if (compressionBlock.CompressionMethodIndex == 0)
|
||||
{
|
||||
src = compressedBuffer;
|
||||
|
|
@ -164,7 +203,7 @@ namespace PakReader.Pak.IO
|
|||
src = uncompressedBuffer;
|
||||
}
|
||||
|
||||
var sizeInBlock = (int) Math.Min(compressionBlockSize - offsetInBlock, remainingSize);
|
||||
var sizeInBlock = (int)Math.Min(compressionBlockSize - offsetInBlock, remainingSize);
|
||||
Buffer.BlockCopy(src, (int) offsetInBlock, dst, dstOffset, sizeInBlock);
|
||||
offsetInBlock = 0;
|
||||
remainingSize -= sizeInBlock;
|
||||
|
|
@ -249,5 +288,111 @@ namespace PakReader.Pak.IO
|
|||
};
|
||||
compressionStream.Read(outData, 0, outData.Length);
|
||||
}
|
||||
|
||||
private void ReadIndex(string directoryName, FIoDirectoryIndexHandle dir, IDictionary<string, FIoStoreEntry> outFiles)
|
||||
{
|
||||
|
||||
while (dir.IsValid())
|
||||
{
|
||||
var subDirectoryName = string.Concat(directoryName, GetDirectoryName(dir), "/");
|
||||
|
||||
var file = GetFile(dir);
|
||||
while (file.IsValid())
|
||||
{
|
||||
var name = GetFileName(file);
|
||||
var path = string.Concat(subDirectoryName, name);
|
||||
var data = GetFileData(file);
|
||||
outFiles[path] = new FIoStoreEntry(this, data, path, CaseSensitive);
|
||||
file = GetNextFile(file);
|
||||
}
|
||||
|
||||
ReadIndex(subDirectoryName, GetChildDirectory(dir), outFiles);
|
||||
|
||||
dir = GetNextDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFile(string path, out ArraySegment<byte> ret1, out ArraySegment<byte> ret2, out ArraySegment<byte> ret3)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(path) && Files.TryGetValue(CaseSensitive ? path : path.ToLowerInvariant(), out var entry))
|
||||
{
|
||||
ret1 = entry.GetData();
|
||||
if (entry.HasUexp())
|
||||
{
|
||||
ret2 = (entry.Uexp as FIoStoreEntry)?.GetData();
|
||||
ret3 = (entry.Ubulk as FIoStoreEntry)?.GetData();
|
||||
return true;
|
||||
}
|
||||
else // return a fail but keep the uasset data
|
||||
{
|
||||
ret2 = null;
|
||||
ret3 = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ret1 = null;
|
||||
ret2 = null;
|
||||
ret3 = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetPartialKey(string partialKey, out string key)
|
||||
{
|
||||
foreach (string path in Files.Keys)
|
||||
{
|
||||
if (Regex.Match(path, partialKey, RegexOptions.IgnoreCase).Success)
|
||||
{
|
||||
key = path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
key = string.Empty;
|
||||
return false;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetCaseInsensiteveValue(string key, out FIoStoreEntry value)
|
||||
{
|
||||
foreach (var r in Files)
|
||||
{
|
||||
if (r.Key.Equals(key, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
value = r.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, FIoStoreEntry>> GetEnumerator()
|
||||
{
|
||||
return Files.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable) Files).GetEnumerator();
|
||||
}
|
||||
|
||||
public int Count => Files.Count;
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return Files.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out FIoStoreEntry value)
|
||||
{
|
||||
return Files.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public FIoStoreEntry this[string key] => Files[key];
|
||||
|
||||
public IEnumerable<string> Keys => Files.Keys;
|
||||
|
||||
public IEnumerable<FIoStoreEntry> Values => Files.Values;
|
||||
}
|
||||
}
|
||||
26
FModel/PakReader/IO/FFragment.cs
Normal file
26
FModel/PakReader/IO/FFragment.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FFragment
|
||||
{
|
||||
public const uint SkipMax = 127;
|
||||
public const uint ValueMax = 127;
|
||||
|
||||
public const uint SkipNumMask = 0x007fu;
|
||||
public const uint HasZeroMask = 0x0080u;
|
||||
public const int ValueNumShift = 9;
|
||||
public const uint IsLastMask = 0x0100u;
|
||||
|
||||
public readonly byte SkipNum; // Number of properties to skip before values
|
||||
public readonly bool HasAnyZeroes;
|
||||
public readonly byte ValueNum; // Number of subsequent property values stored
|
||||
public readonly bool IsLast; // Is this the last fragment of the header?
|
||||
|
||||
public FFragment(ushort packed)
|
||||
{
|
||||
SkipNum = (byte) (packed & SkipNumMask);
|
||||
HasAnyZeroes = (packed & HasZeroMask) != 0;
|
||||
ValueNum = (byte) (packed >> ValueNumShift);
|
||||
IsLast = (packed & IsLastMask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
FModel/PakReader/IO/FImportDesc.cs
Normal file
11
FModel/PakReader/IO/FImportDesc.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FImportDesc
|
||||
{
|
||||
public FName Name;
|
||||
public FPackageObjectIndex GlobalImportIndex;
|
||||
public FExportDesc Export;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public struct FIoChunkHash
|
||||
{
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoChunkId
|
||||
{
|
||||
|
|
@ -18,11 +17,18 @@ namespace PakReader.Pak.IO
|
|||
Id = reader.ReadBytes(12);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public FIoChunkId(ulong chunkId, ushort chunkIndex, EIoChunkType ioChunkType)
|
||||
{
|
||||
Id = new byte[12];
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(chunkId), 0, Id, 0, sizeof(ulong));
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(chunkIndex), 0, Id, sizeof(ulong), sizeof(ushort));
|
||||
Id[11] = (byte)ioChunkType;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 5381;
|
||||
for (int i = 0; i < 12; i++)
|
||||
for (var i = 0; i < 12; i++)
|
||||
{
|
||||
hash = hash * 33 + Id[i];
|
||||
}
|
||||
|
|
@ -30,18 +36,29 @@ namespace PakReader.Pak.IO
|
|||
return hash;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (!(obj is FIoChunkId cast)) return false;
|
||||
if (!(obj is FIoChunkId cast))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Id.SequenceEqual(cast.Id);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(FIoChunkId a, FIoChunkId b) => a.Id.SequenceEqual(b.Id);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(FIoChunkId a, FIoChunkId b) => !a.Id.SequenceEqual(b.Id);
|
||||
|
||||
public override string ToString() => BitConverter.ToString(Id).Replace("-","");
|
||||
public static bool operator ==(FIoChunkId a, FIoChunkId b)
|
||||
{
|
||||
return a.Id.SequenceEqual(b.Id);
|
||||
}
|
||||
|
||||
public static bool operator !=(FIoChunkId a, FIoChunkId b)
|
||||
{
|
||||
return !a.Id.SequenceEqual(b.Id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return BitConverter.ToString(Id).Replace("-", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public struct FIoContainerId
|
||||
{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoDirectoryIndexEntry
|
||||
{
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoDirectoryIndexHandle
|
||||
{
|
||||
public static FIoDirectoryIndexHandle InvalidHandle = new FIoDirectoryIndexHandle(uint.MaxValue);
|
||||
public static FIoDirectoryIndexHandle Root = new FIoDirectoryIndexHandle(0);
|
||||
private readonly uint _handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FIoDirectoryIndexResource
|
||||
{
|
||||
|
|
@ -9,18 +9,22 @@ namespace PakReader.Pak.IO
|
|||
public readonly FIoFileIndexEntry[] FileEntries;
|
||||
public readonly string[] StringTable;
|
||||
|
||||
public FIoDirectoryIndexResource(Stream directoryIndexStream)
|
||||
public FIoDirectoryIndexResource(Stream directoryIndexStream, bool caseSensitive)
|
||||
{
|
||||
using var reader = new BinaryReader(directoryIndexStream);
|
||||
MountPoint = reader.ReadFString();
|
||||
if (MountPoint.StartsWith("../../.."))
|
||||
{
|
||||
MountPoint = MountPoint[9..];
|
||||
MountPoint = MountPoint[8..];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Weird mount point location...
|
||||
MountPoint = "";
|
||||
MountPoint = "/";
|
||||
}
|
||||
if (!caseSensitive)
|
||||
{
|
||||
MountPoint = MountPoint.ToLowerInvariant();
|
||||
}
|
||||
DirectoryEntries = reader.ReadTArray(() => new FIoDirectoryIndexEntry(reader));
|
||||
FileEntries = reader.ReadTArray(() => new FIoFileIndexEntry(reader));
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoFileIndexEntry
|
||||
{
|
||||
133
FModel/PakReader/IO/FIoGlobalData.cs
Normal file
133
FModel/PakReader/IO/FIoGlobalData.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FIoGlobalData
|
||||
{
|
||||
public readonly FNameEntrySerialized[] GlobalNameMap;
|
||||
public readonly List<ulong> GlobalNameHashes;
|
||||
public readonly Dictionary<FPackageObjectIndex, FScriptObjectDesc> ScriptObjectByGlobalId;
|
||||
|
||||
public FIoGlobalData(FFileIoStoreReader globalReader, IReadOnlyCollection<FFileIoStoreReader> allReaders)
|
||||
{
|
||||
var globalNamesIoBuffer = globalReader.Read(new FIoChunkId(0, 0, EIoChunkType.LoaderGlobalNames));
|
||||
var globalNameHashesIoBuffer = globalReader.Read(new FIoChunkId(0, 0, EIoChunkType.LoaderGlobalNameHashes));
|
||||
var globalNameMap = new List<FNameEntrySerialized>();
|
||||
GlobalNameHashes = new List<ulong>();
|
||||
FNameEntrySerialized.LoadNameBatch(globalNameMap, GlobalNameHashes, globalNamesIoBuffer, globalNameHashesIoBuffer);
|
||||
GlobalNameMap = globalNameMap.ToArray();
|
||||
|
||||
var initialLoadIoBuffer = globalReader.Read(new FIoChunkId(0, 0, EIoChunkType.LoaderInitialLoadMeta));
|
||||
var initialLoadIoReader = new BinaryReader(new MemoryStream(initialLoadIoBuffer, false));
|
||||
var numScriptObjects = initialLoadIoReader.ReadInt32();
|
||||
|
||||
var scriptObjectByGlobalIdKeys = new FPackageObjectIndex[numScriptObjects];
|
||||
var scriptObjectByGlobalIdValues = new FScriptObjectDesc[numScriptObjects];
|
||||
var globalIndices = new Dictionary<FPackageObjectIndex, int>(numScriptObjects);
|
||||
|
||||
for (var i = 0; i < numScriptObjects; i++)
|
||||
{
|
||||
var scriptObjectEntry = new FScriptObjectEntry(initialLoadIoReader);
|
||||
globalIndices.TryAdd(scriptObjectEntry.GlobalIndex, i);
|
||||
var mappedName = new FMappedName(scriptObjectEntry.ObjectName, GlobalNameMap, null);
|
||||
|
||||
if (!mappedName.IsGlobal())
|
||||
{
|
||||
Debug.WriteLine(i);
|
||||
}
|
||||
|
||||
scriptObjectByGlobalIdKeys[i] = scriptObjectEntry.GlobalIndex;
|
||||
scriptObjectByGlobalIdValues[i] = new FScriptObjectDesc(GlobalNameMap[(int)mappedName.GetIndex()], mappedName, scriptObjectEntry);
|
||||
}
|
||||
|
||||
for (var i = 0; i < numScriptObjects; i++)
|
||||
{
|
||||
var scriptObjectDesc = scriptObjectByGlobalIdValues[i];
|
||||
|
||||
if (!scriptObjectDesc.FullName.IsNone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var scriptObjectStack = new Stack<FScriptObjectDesc>();
|
||||
var current = i;
|
||||
string fullName = string.Empty;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
var currentDesc = scriptObjectByGlobalIdValues[current];
|
||||
|
||||
if (!currentDesc.FullName.IsNone)
|
||||
{
|
||||
fullName = currentDesc.FullName.String;
|
||||
break;
|
||||
}
|
||||
|
||||
scriptObjectStack.Push(currentDesc);
|
||||
globalIndices.TryGetValue(currentDesc.OuterIndex, out current);
|
||||
}
|
||||
|
||||
while (scriptObjectStack.Count != 0)
|
||||
{
|
||||
var currentStack = scriptObjectStack.Pop();
|
||||
|
||||
if (fullName.Length == 0 || fullName.EndsWith('/'))
|
||||
{
|
||||
fullName = string.Concat(fullName, currentStack.Name.String);
|
||||
}
|
||||
else
|
||||
{
|
||||
fullName = string.Concat(fullName, "/", currentStack.Name.String);
|
||||
}
|
||||
|
||||
currentStack.FullName = new FName(fullName);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptObjectByGlobalId = Enumerable.Range(0, numScriptObjects).ToDictionary(i => scriptObjectByGlobalIdKeys[i], i => scriptObjectByGlobalIdValues[i]);
|
||||
|
||||
var packageByPackageIdMap = new Dictionary<FPackageId, FPackageStoreEntry>();
|
||||
foreach (var reader in allReaders)
|
||||
{
|
||||
var headerChunkId = new FIoChunkId(reader.ContainerId.Id, 0, EIoChunkType.ContainerHeader);
|
||||
if (reader.DoesChunkExist(headerChunkId) && !reader.IsEncrypted)
|
||||
{
|
||||
var buffer = reader.Read(headerChunkId);
|
||||
using var headerReader = new BinaryReader(new MemoryStream(buffer, false));
|
||||
|
||||
var containerHeader = new FContainerHeader(headerReader);
|
||||
|
||||
using var storeEntryReader = new BinaryReader(new MemoryStream(containerHeader.StoreEntries));
|
||||
|
||||
var containerPackages = new List<FPackageDesc>();
|
||||
|
||||
for (var i = 0; i < containerHeader.PackageCount; i++)
|
||||
{
|
||||
var containerEntry = new FPackageStoreEntry(storeEntryReader);
|
||||
|
||||
var packageId = containerHeader.PackageIds[i];
|
||||
if (!packageByPackageIdMap.TryGetValue(packageId, out var packageDesc))
|
||||
{
|
||||
/*
|
||||
packageDesc = new FPackageDesc();
|
||||
packageDesc.PackageId = packageId;
|
||||
packageDesc.Size = containerEntry.ExportBundlesSize;
|
||||
packageDesc.Exports = new FExportDesc[containerEntry.ExportCount];
|
||||
packageDesc.ExportBundleCount = containerEntry.ExportBundleCount;
|
||||
packageDesc.LoadOrder = containerEntry.LoadOrder;
|
||||
*/
|
||||
packageByPackageIdMap[packageId] = containerEntry;
|
||||
}
|
||||
//containerPackages.Add(packageDesc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
FModel/PakReader/IO/FIoOffsetAndLength.cs
Normal file
26
FModel/PakReader/IO/FIoOffsetAndLength.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoOffsetAndLength
|
||||
{
|
||||
// We use 5 bytes for offset and size, this is enough to represent
|
||||
// an offset and size of 1PB
|
||||
public readonly byte[] OffsetAndLength;
|
||||
public ulong Offset => OffsetAndLength[4]
|
||||
| ((ulong) OffsetAndLength[3] << 8)
|
||||
| ((ulong) OffsetAndLength[2] << 16)
|
||||
| ((ulong) OffsetAndLength[1] << 24)
|
||||
| ((ulong) OffsetAndLength[0] << 32);
|
||||
public ulong Length => OffsetAndLength[9]
|
||||
| ((ulong) OffsetAndLength[8] << 8)
|
||||
| ((ulong) OffsetAndLength[7] << 16)
|
||||
| ((ulong) OffsetAndLength[6] << 24)
|
||||
| ((ulong) OffsetAndLength[5] << 32);
|
||||
|
||||
public FIoOffsetAndLength(BinaryReader reader)
|
||||
{
|
||||
OffsetAndLength = reader.ReadBytes(5 + 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
FModel/PakReader/IO/FIoStoreEntry.cs
Normal file
28
FModel/PakReader/IO/FIoStoreEntry.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FIoStoreEntry : ReaderEntry
|
||||
{
|
||||
public readonly FFileIoStoreReader ioStore;
|
||||
public override string ContainerName => ioStore.FileName;
|
||||
public override string Name { get; }
|
||||
public readonly uint UserData;
|
||||
|
||||
public FIoChunkId ChunkId => ioStore.TocResource.ChunkIds[UserData];
|
||||
public FIoOffsetAndLength OffsetLength => ioStore.Toc[ChunkId];
|
||||
public long Offset => (long) OffsetLength.Offset;
|
||||
public long Length => (long) OffsetLength.Length;
|
||||
|
||||
public FIoStoreEntry(FFileIoStoreReader ioStore, uint userData, string name, bool caseSensitive)
|
||||
{
|
||||
this.ioStore = ioStore;
|
||||
UserData = userData;
|
||||
if (!caseSensitive)
|
||||
name = name.ToLowerInvariant();
|
||||
if (name.StartsWith('/'))
|
||||
name = name.Substring(1);
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public byte[] GetData() => ioStore.Read(ChunkId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoStoreTocCompressedBlockEntry
|
||||
{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FIoStoreTocEntryMeta
|
||||
{
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public enum FIoStoreTocEntryMetaFlags : byte
|
||||
{
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public enum EIoStoreTocVersion : byte
|
||||
{
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.Utils;
|
||||
using PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public enum EIoStoreTocReadOptions
|
||||
{
|
||||
|
|
@ -87,7 +85,7 @@ namespace PakReader.Pak.IO
|
|||
ChunkBlockSignatures[i] = new FSHAHash(reader);
|
||||
}
|
||||
|
||||
// You could very hashes here but nah
|
||||
// You could verify hashes here but nah
|
||||
}
|
||||
|
||||
// Directory index
|
||||
74
FModel/PakReader/IO/FIterator.cs
Normal file
74
FModel/PakReader/IO/FIterator.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FIterator : IEnumerator<(int Val, bool IsNonZero)>
|
||||
{
|
||||
private int _schemaIt;
|
||||
private readonly BitArray _zeroMask;
|
||||
private int _zeroMaskIndex;
|
||||
private readonly IEnumerator<FFragment> _fragmentIt;
|
||||
|
||||
private int _remainingFragmentValues;
|
||||
|
||||
public FIterator(FUnversionedHeader header)
|
||||
{
|
||||
_zeroMask = header.ZeroMask;
|
||||
_fragmentIt = header.Fragments.GetEnumerator();
|
||||
Skip();
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_schemaIt++;
|
||||
_remainingFragmentValues--;
|
||||
if (_fragmentIt.Current.HasAnyZeroes)
|
||||
_zeroMaskIndex++;
|
||||
|
||||
if (_remainingFragmentValues == 0)
|
||||
{
|
||||
if (_fragmentIt.Current.IsLast)
|
||||
return false;
|
||||
|
||||
_fragmentIt.MoveNext();
|
||||
Skip();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Skip()
|
||||
{
|
||||
_schemaIt += _fragmentIt.Current.SkipNum;
|
||||
|
||||
while (_fragmentIt.Current.ValueNum == 0)
|
||||
{
|
||||
if (_fragmentIt.Current.IsLast)
|
||||
throw new FileLoadException("Cannot receive last fragment in Skip()");
|
||||
_fragmentIt.MoveNext();
|
||||
_schemaIt += _fragmentIt.Current.SkipNum;
|
||||
}
|
||||
|
||||
_remainingFragmentValues = _fragmentIt.Current.ValueNum;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_schemaIt = _zeroMaskIndex = _remainingFragmentValues = 0;
|
||||
_fragmentIt.Reset();
|
||||
Skip();
|
||||
}
|
||||
|
||||
public bool IsNonZero => !_fragmentIt.Current.HasAnyZeroes || !_zeroMask[_zeroMaskIndex];
|
||||
|
||||
public (int Val, bool IsNonZero) Current => (_schemaIt, IsNonZero);
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_fragmentIt.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
71
FModel/PakReader/IO/FMappedName.cs
Normal file
71
FModel/PakReader/IO/FMappedName.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using FModel.PakReader.Parsers;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FMappedName
|
||||
{
|
||||
public const uint InvalidIndex = ~0u;
|
||||
public const uint IndexBits = 30u;
|
||||
public const uint IndexMask = (1u << (int)IndexBits) - 1u;
|
||||
public const uint TypeMask = ~IndexMask;
|
||||
public const uint TypeShift = IndexBits;
|
||||
|
||||
private readonly IoPackageReader _reader;
|
||||
|
||||
private readonly FNameEntrySerialized[] _globalNameMap =>
|
||||
_reader != null ? _reader.GlobalData.GlobalNameMap : __globalNameMap;
|
||||
private readonly FNameEntrySerialized[] __globalNameMap;
|
||||
private readonly FNameEntrySerialized[] _localNameMap =>
|
||||
_reader != null ? _reader.NameMap : __localNameMap;
|
||||
private readonly FNameEntrySerialized[] __localNameMap;
|
||||
|
||||
public readonly uint Index;
|
||||
public readonly uint Number;
|
||||
|
||||
public string String
|
||||
{
|
||||
get
|
||||
{
|
||||
var index = GetIndex();
|
||||
var nameMap = IsGlobal() ? _globalNameMap : _localNameMap;
|
||||
if (nameMap != null && index < _globalNameMap.Length)
|
||||
{
|
||||
return nameMap[index].Name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public FMappedName(IoPackageReader reader)
|
||||
{
|
||||
Index = reader.ReadUInt32();
|
||||
Number = reader.ReadUInt32();
|
||||
_reader = reader;
|
||||
__globalNameMap = null;
|
||||
__localNameMap = null;
|
||||
}
|
||||
|
||||
public FMappedName(FMinimalName minimalName, FNameEntrySerialized[] globalNameMap, FNameEntrySerialized[] localNameMap)
|
||||
{
|
||||
Index = minimalName.Index.Value;
|
||||
Number = (uint)minimalName.Number;
|
||||
_reader = null;
|
||||
__globalNameMap = globalNameMap;
|
||||
__localNameMap = localNameMap;
|
||||
}
|
||||
|
||||
public uint GetIndex()
|
||||
{
|
||||
return Index & IndexMask;
|
||||
}
|
||||
|
||||
public bool IsGlobal()
|
||||
{
|
||||
return (Index & TypeMask) >> (int)TypeShift != 0;
|
||||
}
|
||||
|
||||
public override string ToString() => String;
|
||||
}
|
||||
}
|
||||
22
FModel/PakReader/IO/FMinimalName.cs
Normal file
22
FModel/PakReader/IO/FMinimalName.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FMinimalName
|
||||
{
|
||||
public readonly FNameEntryId Index;
|
||||
public readonly int Number; // #define NAME_NO_NUMBER_INTERNAL 0
|
||||
|
||||
public FMinimalName(BinaryReader reader)
|
||||
{
|
||||
Index = new FNameEntryId(reader);
|
||||
Number = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public FMinimalName(FNameEntryId inIndex, int inNumber)
|
||||
{
|
||||
Index = inIndex;
|
||||
Number = inNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
FModel/PakReader/IO/FNameEntryId.cs
Normal file
45
FModel/PakReader/IO/FNameEntryId.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FNameEntryId : IEquatable<FNameEntryId>
|
||||
{
|
||||
public readonly uint Value;
|
||||
|
||||
public FNameEntryId(uint value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public FNameEntryId(BinaryReader reader)
|
||||
{
|
||||
Value = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public bool Equals(FNameEntryId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is FNameEntryId other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)Value;
|
||||
}
|
||||
|
||||
public static bool operator ==(FNameEntryId left, FNameEntryId right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(FNameEntryId left, FNameEntryId right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
FModel/PakReader/IO/FPackageDesc.cs
Normal file
18
FModel/PakReader/IO/FPackageDesc.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FPackageDesc
|
||||
{
|
||||
public FPackageId PackageId;
|
||||
public FName PackageName;
|
||||
public ulong Size = 0;
|
||||
public uint LoadOrder = uint.MaxValue;
|
||||
public uint PackageFlags = 0;
|
||||
public int NameCount = -1;
|
||||
public int ExportBundleCount = -1;
|
||||
public FPackageLocation[] Locations;
|
||||
public FImportDesc[] Imports;
|
||||
public FExportDesc[] Exports;
|
||||
}
|
||||
}
|
||||
21
FModel/PakReader/IO/FPackageId.cs
Normal file
21
FModel/PakReader/IO/FPackageId.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FPackageId
|
||||
{
|
||||
public readonly ulong Id;
|
||||
|
||||
public FPackageId(ulong id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public FPackageId(BinaryReader reader)
|
||||
{
|
||||
Id = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
public FIoChunkId CreateIoChunkId(ushort chunkIndex, EIoChunkType type = EIoChunkType.ExportBundleData) => new FIoChunkId(Id, chunkIndex, type);
|
||||
}
|
||||
}
|
||||
10
FModel/PakReader/IO/FPackageLocation.cs
Normal file
10
FModel/PakReader/IO/FPackageLocation.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FPackageLocation
|
||||
{
|
||||
public readonly FName ContainerName;
|
||||
public readonly ulong Offset;
|
||||
}
|
||||
}
|
||||
71
FModel/PakReader/IO/FPackageObjectIndex.cs
Normal file
71
FModel/PakReader/IO/FPackageObjectIndex.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FPackageObjectIndex : IEquatable<FPackageObjectIndex>
|
||||
{
|
||||
public const int IndexBits = 62;
|
||||
public const ulong IndexMask = (1UL << IndexBits) - 1UL;
|
||||
public const ulong TypeMask = ~IndexMask;
|
||||
public const int TypeShift = IndexBits;
|
||||
public const ulong Invalid = ~0UL;
|
||||
|
||||
|
||||
private readonly ulong _typeAndId;
|
||||
public EType Type => (EType) (_typeAndId >> TypeShift);
|
||||
public ulong Value => _typeAndId & IndexMask;
|
||||
|
||||
public bool IsNull => _typeAndId == Invalid;
|
||||
public bool IsExport => Type == EType.Export;
|
||||
public bool IsImport => IsScriptImport || IsPackageImport;
|
||||
public bool IsScriptImport => Type == EType.ScriptImport;
|
||||
public bool IsPackageImport => Type == EType.PackageImport;
|
||||
public uint AsExport => (uint) _typeAndId;
|
||||
|
||||
public FPackageObjectIndex(BinaryReader reader)
|
||||
{
|
||||
//TypeAndId = Invalid;
|
||||
_typeAndId = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
public FPackageObjectIndex(ulong typeAndId)
|
||||
{
|
||||
_typeAndId = typeAndId;
|
||||
}
|
||||
|
||||
public bool Equals(FPackageObjectIndex other)
|
||||
{
|
||||
return _typeAndId == other._typeAndId;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is FPackageObjectIndex other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _typeAndId.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(FPackageObjectIndex left, FPackageObjectIndex right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(FPackageObjectIndex left, FPackageObjectIndex right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EType
|
||||
{
|
||||
Export,
|
||||
ScriptImport,
|
||||
PackageImport,
|
||||
Null,
|
||||
TypeCount = Null
|
||||
};
|
||||
}
|
||||
36
FModel/PakReader/IO/FPackageStoreEntry.cs
Normal file
36
FModel/PakReader/IO/FPackageStoreEntry.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FPackageStoreEntry
|
||||
{
|
||||
public readonly ulong ExportBundlesSize;
|
||||
public readonly int ExportCount;
|
||||
public readonly int ExportBundleCount;
|
||||
public readonly uint LoadOrder;
|
||||
public readonly FPackageId[] ImportedPackages;
|
||||
|
||||
|
||||
public FPackageStoreEntry(BinaryReader reader)
|
||||
{
|
||||
ExportBundlesSize = reader.ReadUInt64();
|
||||
ExportCount = reader.ReadInt32();
|
||||
ExportBundleCount = reader.ReadInt32();
|
||||
LoadOrder = reader.ReadUInt32();
|
||||
reader.BaseStream.Position += 4; // Padding
|
||||
|
||||
var pos = reader.BaseStream.Position;
|
||||
var packageStoreArrayNum = reader.ReadInt32();
|
||||
var packageStoreOffsetToData = reader.ReadUInt32();
|
||||
reader.BaseStream.Position = pos + packageStoreOffsetToData;
|
||||
ImportedPackages = new FPackageId[packageStoreArrayNum];
|
||||
|
||||
for (var i = 0; i < packageStoreArrayNum; i++)
|
||||
{
|
||||
ImportedPackages[i] = new FPackageId(reader);
|
||||
}
|
||||
|
||||
reader.BaseStream.Position = pos + 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
FModel/PakReader/IO/FPackageSummary.cs
Normal file
39
FModel/PakReader/IO/FPackageSummary.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using FModel.PakReader.Parsers;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FPackageSummary
|
||||
{
|
||||
public readonly FMappedName Name;
|
||||
public readonly FMappedName SourceName;
|
||||
public readonly uint PackageFlags;
|
||||
public readonly uint CookedHeaderSize;
|
||||
public readonly int NameMapNamesOffset;
|
||||
public readonly int NameMapNamesSize;
|
||||
public readonly int NameMapHashesOffset;
|
||||
public readonly int NameMapHashesSize;
|
||||
public readonly int ImportMapOffset;
|
||||
public readonly int ExportMapOffset;
|
||||
public readonly int ExportBundlesOffset;
|
||||
public readonly int GraphDataOffset;
|
||||
public readonly int GraphDataSize;
|
||||
|
||||
public FPackageSummary(IoPackageReader reader)
|
||||
{
|
||||
Name = new FMappedName(reader);
|
||||
SourceName = new FMappedName(reader);
|
||||
PackageFlags = reader.ReadUInt32();
|
||||
CookedHeaderSize = reader.ReadUInt32();
|
||||
NameMapNamesOffset = reader.ReadInt32();
|
||||
NameMapNamesSize = reader.ReadInt32();
|
||||
NameMapHashesOffset = reader.ReadInt32();
|
||||
NameMapHashesSize = reader.ReadInt32();
|
||||
ImportMapOffset = reader.ReadInt32();
|
||||
ExportMapOffset = reader.ReadInt32();
|
||||
ExportBundlesOffset = reader.ReadInt32();
|
||||
GraphDataOffset = reader.ReadInt32();
|
||||
GraphDataSize = reader.ReadInt32();
|
||||
reader.SkipBytes(4); // Padding
|
||||
}
|
||||
}
|
||||
}
|
||||
20
FModel/PakReader/IO/FScriptObjectDesc.cs
Normal file
20
FModel/PakReader/IO/FScriptObjectDesc.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FScriptObjectDesc
|
||||
{
|
||||
public readonly FName Name;
|
||||
public FName FullName;
|
||||
public readonly FPackageObjectIndex GlobalImportIndex;
|
||||
public readonly FPackageObjectIndex OuterIndex;
|
||||
|
||||
public FScriptObjectDesc(FNameEntrySerialized name, FMappedName fMappedName, FScriptObjectEntry fScriptObjectEntry)
|
||||
{
|
||||
Name = new FName(name.Name, (int)fMappedName.Index, (int)fMappedName.Number);
|
||||
FullName = default;
|
||||
GlobalImportIndex = fScriptObjectEntry.GlobalIndex;
|
||||
OuterIndex = fScriptObjectEntry.OuterIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
FModel/PakReader/IO/FScriptObjectEntry.cs
Normal file
20
FModel/PakReader/IO/FScriptObjectEntry.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FScriptObjectEntry
|
||||
{
|
||||
public readonly FMinimalName ObjectName;
|
||||
public readonly FPackageObjectIndex GlobalIndex;
|
||||
public readonly FPackageObjectIndex OuterIndex;
|
||||
public readonly FPackageObjectIndex CDOClassIndex;
|
||||
|
||||
public FScriptObjectEntry(BinaryReader reader)
|
||||
{
|
||||
ObjectName = new FMinimalName(reader);
|
||||
GlobalIndex = new FPackageObjectIndex(reader);
|
||||
OuterIndex = new FPackageObjectIndex(reader);
|
||||
CDOClassIndex = new FPackageObjectIndex(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
FModel/PakReader/IO/FSerializedNameHeader.cs
Normal file
17
FModel/PakReader/IO/FSerializedNameHeader.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System.IO;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public readonly struct FSerializedNameHeader
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
|
||||
public bool IsUtf16 => (_data[0] & 0x80u) != 0;
|
||||
public uint Length => ((_data[0] & 0x7Fu) << 8) + _data[1];
|
||||
|
||||
public FSerializedNameHeader(BinaryReader reader)
|
||||
{
|
||||
_data = reader.ReadBytes(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
FModel/PakReader/IO/FUnversionedHeader.cs
Normal file
65
FModel/PakReader/IO/FUnversionedHeader.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FModel.Utils;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FUnversionedHeader
|
||||
{
|
||||
public List<FFragment> Fragments = new List<FFragment>();
|
||||
public BitArray ZeroMask;
|
||||
public readonly bool HasNonZeroValues;
|
||||
public bool HasValues => HasNonZeroValues | (ZeroMask.Count > 0);
|
||||
|
||||
public FUnversionedHeader(BinaryReader reader)
|
||||
{
|
||||
FFragment fragment;
|
||||
int zeroMaskNum = 0;
|
||||
uint unmaskedNum = 0;
|
||||
do
|
||||
{
|
||||
fragment = new FFragment(reader.ReadUInt16());
|
||||
|
||||
Fragments.Add(fragment);
|
||||
if (fragment.HasAnyZeroes)
|
||||
zeroMaskNum += fragment.ValueNum;
|
||||
else
|
||||
unmaskedNum += fragment.ValueNum;
|
||||
} while (!fragment.IsLast);
|
||||
|
||||
if (zeroMaskNum > 0)
|
||||
{
|
||||
LoadZeroMaskData(reader, zeroMaskNum, out ZeroMask);
|
||||
HasNonZeroValues = unmaskedNum > 0 || ZeroMask.Contains(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZeroMask = new BitArray(new int[8]);
|
||||
HasNonZeroValues = unmaskedNum > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadZeroMaskData(BinaryReader reader, int numBits, out BitArray data)
|
||||
{
|
||||
if (numBits <= 8)
|
||||
{
|
||||
data = new BitArray(new[] { reader.ReadByte() });
|
||||
}
|
||||
else if (numBits <= 16)
|
||||
{
|
||||
data = new BitArray(new []{ (int) reader.ReadUInt16() });
|
||||
}
|
||||
else
|
||||
{
|
||||
var num = numBits.DivideAndRoundUp(32);
|
||||
var intData = new int[num];
|
||||
for (int idx = 0; idx < num; idx++)
|
||||
{
|
||||
intData[idx] = reader.ReadInt32();
|
||||
}
|
||||
data = new BitArray(intData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
FModel/PakReader/IO/IoPackage.cs
Normal file
74
FModel/PakReader/IO/IoPackage.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System.IO;
|
||||
using FModel.PakReader.Parsers;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class IoPackage : Package
|
||||
{
|
||||
private byte[] UAsset;
|
||||
private byte[] UBulk;
|
||||
private FIoStoreEntry _entry;
|
||||
private IoPackageReader _reader;
|
||||
private string _jsonData = null;
|
||||
|
||||
internal IoPackage(byte[] asset, byte[] bulk, FIoStoreEntry entry)
|
||||
{
|
||||
UAsset = asset;
|
||||
UBulk = bulk;
|
||||
_entry = entry;
|
||||
}
|
||||
|
||||
public IoPackageReader Reader
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_reader == null)
|
||||
{
|
||||
var asset = new MemoryStream(UAsset);
|
||||
var bulk = UBulk != null ? new MemoryStream(UBulk) : null;
|
||||
asset.Position = 0;
|
||||
if (bulk != null)
|
||||
bulk.Position = 0;
|
||||
|
||||
return _reader = new IoPackageReader(asset, bulk, Globals.GlobalData, _entry.ioStore, true);
|
||||
}
|
||||
|
||||
return _reader;
|
||||
}
|
||||
}
|
||||
|
||||
public override string JsonData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_jsonData))
|
||||
{
|
||||
var ret = new JsonExport[Exports.Length];
|
||||
for (int i = 0; i < ret.Length; i++)
|
||||
{
|
||||
ret[i] = new JsonExport
|
||||
{
|
||||
ExportType = ExportTypes[i].String,
|
||||
ExportValue = (FModel.EJsonType)FModel.Properties.Settings.Default.AssetsJsonType switch
|
||||
{
|
||||
FModel.EJsonType.Default => Exports[i].GetJsonDict(),
|
||||
_ => Exports[i]
|
||||
}
|
||||
};
|
||||
}
|
||||
#if DEBUG
|
||||
return JsonConvert.SerializeObject(ret, Formatting.Indented);
|
||||
#else
|
||||
return _jsonData = JsonConvert.SerializeObject(ret, Formatting.Indented);
|
||||
#endif
|
||||
}
|
||||
return _jsonData;
|
||||
}
|
||||
}
|
||||
public override FName[] ExportTypes => Reader.DataExportTypes;
|
||||
public override IUExport[] Exports => Reader.DataExports;
|
||||
}
|
||||
}
|
||||
31
FModel/PakReader/IO/PropertyInfo.cs
Normal file
31
FModel/PakReader/IO/PropertyInfo.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class PropertyInfo
|
||||
{
|
||||
public string Name;
|
||||
public string Type;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string StructType;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? Bool;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string EnumName;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string InnerType;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ValueType;
|
||||
|
||||
public PropertyInfo(string name, string type, string structType = null, bool? b = null, string enumName = null, string innerType = null, string valueType = null)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
StructType = structType;
|
||||
Bool = b;
|
||||
EnumName = enumName;
|
||||
InnerType = innerType;
|
||||
ValueType = valueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader
|
||||
namespace FModel.PakReader
|
||||
{
|
||||
public class LocMetaReader
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader
|
||||
namespace FModel.PakReader
|
||||
{
|
||||
public class LocResReader
|
||||
{
|
||||
|
|
|
|||
64
FModel/PakReader/Package.cs
Normal file
64
FModel/PakReader/Package.cs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader
|
||||
{
|
||||
public abstract class Package
|
||||
{
|
||||
public abstract string JsonData { get; }
|
||||
public abstract FName[] ExportTypes { get; }
|
||||
public abstract IUExport[] Exports { get; }
|
||||
|
||||
public T GetExport<T>() where T : IUExport
|
||||
{
|
||||
var exports = Exports;
|
||||
for (int i = 0; i < exports.Length; i++)
|
||||
{
|
||||
if (exports[i] is T)
|
||||
return (T)exports[i];
|
||||
}
|
||||
return default;
|
||||
}
|
||||
public T GetIndexedExport<T>(int index) where T : IUExport
|
||||
{
|
||||
var exports = Exports;
|
||||
var foundCount = 0;
|
||||
for (var i = 0; i < exports.Length; i++)
|
||||
{
|
||||
if (exports[i] is T cast)
|
||||
{
|
||||
if (foundCount == index)
|
||||
return cast;
|
||||
foundCount++;
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
public T GetTypedExport<T>(string exportType) where T : IUExport
|
||||
{
|
||||
int index = 0;
|
||||
var exportTypes = ExportTypes;
|
||||
for (int i = 0; i < exportTypes.Length; i++)
|
||||
{
|
||||
if (exportTypes[i].String == exportType)
|
||||
index = i;
|
||||
}
|
||||
return (T)Exports[index];
|
||||
}
|
||||
|
||||
public bool HasExport() => Exports != default;
|
||||
}
|
||||
|
||||
public sealed class ExportList
|
||||
{
|
||||
public string JsonData;
|
||||
public FName[] ExportTypes;
|
||||
public IUExport[] Exports;
|
||||
}
|
||||
|
||||
public sealed class JsonExport
|
||||
{
|
||||
public string ExportType;
|
||||
public object ExportValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
class DefaultPakFilter : IPakFilter
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
using System.IO;
|
||||
|
||||
namespace PakReader.Pak.IO
|
||||
{
|
||||
public readonly struct FIoOffsetAndLength
|
||||
{
|
||||
// We use 5 bytes for offset and size, this is enough to represent
|
||||
// an offset and size of 1PB
|
||||
public readonly byte[] OffsetAndLength;
|
||||
public long Offset => OffsetAndLength[4]
|
||||
| ((long) OffsetAndLength[3] << 8)
|
||||
| ((long) OffsetAndLength[2] << 16)
|
||||
| ((long) OffsetAndLength[1] << 24)
|
||||
| ((long) OffsetAndLength[0] << 32);
|
||||
public long Length => OffsetAndLength[9]
|
||||
| ((long) OffsetAndLength[8] << 8)
|
||||
| ((long) OffsetAndLength[7] << 16)
|
||||
| ((long) OffsetAndLength[6] << 24)
|
||||
| ((long) OffsetAndLength[5] << 32);
|
||||
|
||||
public FIoOffsetAndLength(BinaryReader reader)
|
||||
{
|
||||
OffsetAndLength = reader.ReadBytes(5 + 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
public interface IPakFilter
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ using System.Text.RegularExpressions;
|
|||
using FModel.Logger;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.Utils;
|
||||
using PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
public sealed class PakFileReader : IReadOnlyDictionary<string, FPakEntry>
|
||||
{
|
||||
|
|
@ -42,7 +41,7 @@ namespace PakReader.Pak
|
|||
|
||||
// Buffered streams increase performance dramatically
|
||||
public PakFileReader(string file, bool caseSensitive = true)
|
||||
: this(file, new BufferedStream(new FileInfo(file).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)), caseSensitive)
|
||||
: this(file, new FileInfo(file).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite), caseSensitive)
|
||||
{ }
|
||||
|
||||
public PakFileReader(string path, Stream stream, bool caseSensitive = true)
|
||||
|
|
@ -446,15 +445,15 @@ namespace PakReader.Pak
|
|||
ret1 = entry.GetData(Stream, AesKey, Info.CompressionMethods);
|
||||
if (entry.HasUexp())
|
||||
{
|
||||
ret2 = entry.Uexp.GetData(Stream, AesKey, Info.CompressionMethods);
|
||||
ret3 = entry.HasUbulk() ? entry.Ubulk.GetData(Stream, AesKey, Info.CompressionMethods) : null;
|
||||
ret2 = ((FPakEntry)entry.Uexp).GetData(Stream, AesKey, Info.CompressionMethods);
|
||||
ret3 = entry.HasUbulk() ? ((FPakEntry)entry.Ubulk).GetData(Stream, AesKey, Info.CompressionMethods) : null;
|
||||
return true;
|
||||
}
|
||||
else // return a fail but keep the uasset data
|
||||
{
|
||||
ret2 = null;
|
||||
ret3 = null;
|
||||
return false;
|
||||
return entry.GetExtension().Contains(".ufont", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
ret1 = null;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
// Currently only supports strings that start with a value
|
||||
// I've just implemented this myself to save tons of memory so you don't have to
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
public class PakIndex : IEnumerable<string>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using FModel.PakReader.Parsers;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using Newtonsoft.Json;
|
||||
using PakReader.Parsers;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Pak
|
||||
namespace FModel.PakReader.Pak
|
||||
{
|
||||
public readonly struct PakPackage
|
||||
public sealed class PakPackage : Package
|
||||
{
|
||||
readonly ArraySegment<byte> UAsset;
|
||||
readonly ArraySegment<byte> UExp;
|
||||
readonly ArraySegment<byte> UBulk;
|
||||
|
||||
internal PakPackage(ArraySegment<byte> asset, ArraySegment<byte> exp, ArraySegment<byte> bulk)
|
||||
{
|
||||
UAsset = asset;
|
||||
UExp = exp;
|
||||
UBulk = bulk;
|
||||
exports = new ExportList();
|
||||
}
|
||||
|
||||
public string JsonData
|
||||
public override string JsonData
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -37,7 +45,7 @@ namespace PakReader.Pak
|
|||
return exports.JsonData;
|
||||
}
|
||||
}
|
||||
public FName[] ExportTypes
|
||||
public override FName[] ExportTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -51,14 +59,14 @@ namespace PakReader.Pak
|
|||
if (bulk != null)
|
||||
bulk.Position = 0;
|
||||
|
||||
var p = new PackageReader(asset, exp, bulk);
|
||||
var p = new LegacyPackageReader(asset, exp, bulk);
|
||||
exports.Exports = p.DataExports;
|
||||
return exports.ExportTypes = p.DataExportTypes;
|
||||
}
|
||||
return exports.ExportTypes;
|
||||
}
|
||||
}
|
||||
public IUExport[] Exports
|
||||
public override IUExport[] Exports
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -72,74 +80,17 @@ namespace PakReader.Pak
|
|||
if (bulk != null)
|
||||
bulk.Position = 0;
|
||||
|
||||
var p = new PackageReader(asset, exp, bulk);
|
||||
var p = new LegacyPackageReader(asset, exp, bulk);
|
||||
exports.ExportTypes = p.DataExportTypes;
|
||||
return exports.Exports = p.DataExports;
|
||||
}
|
||||
return exports.Exports;
|
||||
}
|
||||
}
|
||||
readonly ExportList exports;
|
||||
private readonly ExportList exports;
|
||||
|
||||
internal PakPackage(ArraySegment<byte> asset, ArraySegment<byte> exp, ArraySegment<byte> bulk)
|
||||
{
|
||||
UAsset = asset;
|
||||
UExp = exp;
|
||||
UBulk = bulk;
|
||||
exports = new ExportList();
|
||||
}
|
||||
|
||||
public T GetExport<T>() where T : IUExport
|
||||
{
|
||||
var exports = Exports;
|
||||
for (int i = 0; i < exports.Length; i++)
|
||||
{
|
||||
if (exports[i] is T)
|
||||
return (T)exports[i];
|
||||
}
|
||||
return default;
|
||||
}
|
||||
public T GetIndexedExport<T>(int index) where T : IUExport
|
||||
{
|
||||
var exports = Exports;
|
||||
int foundCount = 0;
|
||||
for (int i = 0; i < exports.Length; i++)
|
||||
{
|
||||
if (exports[i] is T)
|
||||
{
|
||||
if (foundCount == index)
|
||||
return (T)exports[i];
|
||||
foundCount++;
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
public T GetTypedExport<T>(string exportType) where T : IUExport
|
||||
{
|
||||
int index = 0;
|
||||
var exportTypes = ExportTypes;
|
||||
for (int i = 0; i < exportTypes.Length; i++)
|
||||
{
|
||||
if (exportTypes[i].String == exportType)
|
||||
index = i;
|
||||
}
|
||||
return (T)Exports[index];
|
||||
}
|
||||
|
||||
public bool HasExport() => exports != null;
|
||||
|
||||
// hacky way to get the package to be a readonly struct, essentially a double pointer i guess
|
||||
sealed class ExportList
|
||||
{
|
||||
public string JsonData;
|
||||
public FName[] ExportTypes;
|
||||
public IUExport[] Exports;
|
||||
}
|
||||
|
||||
sealed class JsonExport
|
||||
{
|
||||
public string ExportType;
|
||||
public object ExportValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using PakReader.Parsers.PropertyTagData;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
/// <summary>
|
||||
/// IReadOnlyDictionary<string, object> is only used to be able to iterate over properties
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using PakReader.Parsers.Objects;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UAkMediaAssetData : UObject
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
using PakReader.Parsers.Objects;
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UCurveTable : IUExport
|
||||
{
|
||||
public ECurveTableMode CurveTableMode { get; }
|
||||
readonly Dictionary<string, object> RowMap;
|
||||
|
||||
|
||||
internal UCurveTable(PackageReader reader)
|
||||
{
|
||||
_ = new UObject(reader); //will break
|
||||
if (!(reader is IoPackageReader))
|
||||
{
|
||||
_ = new UObject(reader); //will break
|
||||
}
|
||||
|
||||
int NumRows = reader.ReadInt32();
|
||||
CurveTableMode = (ECurveTableMode)reader.ReadByte();
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Utils;
|
||||
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UDataTable : IUExport
|
||||
{
|
||||
/** Map of name of row to row data structure. */
|
||||
readonly Dictionary<string, object> RowMap;
|
||||
private readonly Dictionary<string, object> RowMap;
|
||||
|
||||
internal UDataTable(PackageReader reader)
|
||||
{
|
||||
|
|
@ -30,6 +34,55 @@ namespace PakReader.Parsers.Class
|
|||
}
|
||||
}
|
||||
|
||||
internal UDataTable(IoPackageReader reader, IReadOnlyDictionary<int, PropertyInfo> properties, string type)
|
||||
{
|
||||
var baseObj = new UObject(reader, properties, type: type);
|
||||
|
||||
if (!baseObj.TryGetValue("RowStruct", out var rowStructProp) || !(rowStructProp is ObjectProperty rowStruct) || !rowStruct.Value.IsImport)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var rowStructimportIndex = rowStruct.Value.AsImport;
|
||||
|
||||
if (rowStructimportIndex >= reader.ImportMap.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var rowStructimport = reader.ImportMap[rowStructimportIndex];
|
||||
|
||||
if (rowStructimport.Type != EType.ScriptImport ||
|
||||
!Globals.GlobalData.ScriptObjectByGlobalId.TryGetValue(rowStructimport, out var rowStrucDesc) ||
|
||||
rowStrucDesc.Name.IsNone)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Globals.TypeMappings.TryGetValue(rowStrucDesc.Name.String, out var rowProperties))
|
||||
{
|
||||
FConsole.AppendText($"{reader.Summary.Name.String} can't be parsed yet (RowType: {rowStrucDesc.Name.String})", FColors.Red, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var NumRows = reader.ReadInt32();
|
||||
RowMap = new Dictionary<string, object>();
|
||||
|
||||
for (var i = 0; i < NumRows; i++)
|
||||
{
|
||||
var num = 1;
|
||||
var RowName = reader.ReadFName().String ?? "";
|
||||
var baseName = RowName;
|
||||
|
||||
while (RowMap.ContainsKey(RowName))
|
||||
{
|
||||
RowName = $"{baseName}_NK{num++:00}";
|
||||
}
|
||||
|
||||
RowMap[RowName] = new UObject(reader, rowProperties, true, rowStrucDesc.Name.String);
|
||||
}
|
||||
}
|
||||
|
||||
public object this[string key] => RowMap[key];
|
||||
public IEnumerable<string> Keys => RowMap.Keys;
|
||||
public IEnumerable<object> Values => RowMap.Values;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using PakReader.Parsers.PropertyTagData;
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UFontFace : IUExport
|
||||
{
|
||||
|
|
@ -14,18 +14,16 @@ namespace PakReader.Parsers.Class
|
|||
internal UFontFace(PackageReader reader, Stream ufont)
|
||||
{
|
||||
FontFaceAsset = new UObject(reader, true);
|
||||
foreach (KeyValuePair<string, object> prop in FontFaceAsset)
|
||||
{
|
||||
if (prop.Key.Equals("SourceFilename") && prop.Value is StrProperty str)
|
||||
{
|
||||
string FontFilename = Path.GetFileName(str.Value);
|
||||
string folder = FModel.Properties.Settings.Default.OutputPath + "\\Fonts\\";
|
||||
|
||||
if (ufont != null)
|
||||
{
|
||||
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
|
||||
ufont.CopyTo(fileStream);
|
||||
}
|
||||
if (FontFaceAsset.TryGetValue("SourceFilename", out var prop) && prop is StrProperty str)
|
||||
{
|
||||
string FontFilename = Path.GetFileName(str.Value);
|
||||
string folder = Properties.Settings.Default.OutputPath + "\\Fonts\\";
|
||||
|
||||
if (ufont != null)
|
||||
{
|
||||
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
|
||||
ufont.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,101 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Logger;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
using FModel.Utils;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public class UObject : IUExport, IUStruct
|
||||
{
|
||||
readonly Dictionary<string, object> Dict;
|
||||
private readonly Dictionary<string, object> Dict;
|
||||
|
||||
public UObject(IoPackageReader reader, IReadOnlyDictionary<int, PropertyInfo> properties, bool structFallback = false, string type = null)
|
||||
{
|
||||
Dict = new Dictionary<string, object>();
|
||||
var header = new FUnversionedHeader(reader);
|
||||
using var it = new FIterator(header);
|
||||
|
||||
#if DEBUG
|
||||
|
||||
var headerWritten = false;
|
||||
|
||||
do
|
||||
{
|
||||
if (properties.ContainsKey(it.Current.Val))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!headerWritten)
|
||||
{
|
||||
headerWritten = true;
|
||||
FConsole.AppendText(string.Concat("\n", type ?? "Unknown", ": ", reader.Summary.Name.String), "#CA6C6C", true);
|
||||
}
|
||||
|
||||
FConsole.AppendText($"Val: {it.Current.Val} (IsNonZero: {it.Current.IsNonZero})", FColors.Yellow, true);
|
||||
}
|
||||
while (it.MoveNext());
|
||||
it.Reset();
|
||||
#endif
|
||||
|
||||
var num = 1;
|
||||
|
||||
do
|
||||
{
|
||||
var (val, isNonZero) = it.Current;
|
||||
if (properties.TryGetValue(val, out var propertyInfo))
|
||||
{
|
||||
if (isNonZero)
|
||||
{
|
||||
var obj = BaseProperty.ReadAsObject(reader, new FPropertyTag(propertyInfo), new FName(propertyInfo.Type), ReadType.NORMAL);
|
||||
var key = Dict.ContainsKey(propertyInfo.Name) ? $"{propertyInfo.Name}_NK{num++:00}" : propertyInfo.Name;
|
||||
Dict[key] = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = BaseProperty.ReadAsZeroObject(reader, new FPropertyTag(propertyInfo),
|
||||
new FName(propertyInfo.Type));
|
||||
var key = Dict.ContainsKey(propertyInfo.Name) ? $"{propertyInfo.Name}_NK{num++:00}" : propertyInfo.Name;
|
||||
Dict[key] = obj;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isNonZero)
|
||||
{
|
||||
// We are lucky: We don't know this property but it also has no content
|
||||
DebugHelper.WriteLine($"Unknown property for {GetType().Name} with value {val} but it's zero so we are good");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugHelper.WriteLine($"Unknown property for {GetType().Name} with value {val}. Can't proceed serialization (Serialized {Dict.Count} properties till now)");
|
||||
return;
|
||||
//throw new FileLoadException($"Unknown property for {GetType().Name} with value {val}. Can't proceed serialization");
|
||||
}
|
||||
}
|
||||
} while (it.MoveNext());
|
||||
if (!structFallback && reader.ReadInt32() != 0/* && reader.Position + 16 <= maxSize*/)
|
||||
{
|
||||
new FGuid(reader);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty UObject used for new package format when a property is zero
|
||||
public UObject()
|
||||
{
|
||||
Dict = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
// https://github.com/EpicGames/UnrealEngine/blob/bf95c2cbc703123e08ab54e3ceccdd47e48d224a/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp#L930
|
||||
public UObject(PackageReader reader) : this(reader, reader.ExportMap.Sum(e => e.SerialSize), false) { }
|
||||
public UObject(PackageReader reader, bool structFallback) : this(reader, reader.ExportMap.Sum(e => e.SerialSize), structFallback) { }
|
||||
public UObject(PackageReader reader, long maxSize) : this(reader, maxSize, false) { }
|
||||
public UObject(PackageReader reader) : this(reader, false) { }
|
||||
|
||||
// Structs that don't use binary serialization
|
||||
// https://github.com/EpicGames/UnrealEngine/blob/7d9919ac7bfd80b7483012eab342cb427d60e8c9/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp#L2197
|
||||
internal UObject(PackageReader reader, long maxSize, bool structFallback)
|
||||
internal UObject(PackageReader reader, bool structFallback)
|
||||
{
|
||||
var properties = new Dictionary<string, object>();
|
||||
int num = 1;
|
||||
|
|
@ -46,7 +123,7 @@ namespace PakReader.Parsers.Class
|
|||
}
|
||||
Dict = properties;
|
||||
|
||||
if (!structFallback && reader.ReadInt32() != 0 && reader.Position + 16 <= maxSize)
|
||||
if (!structFallback && reader.ReadInt32() != 0/* && reader.Position + 16 <= maxSize*/)
|
||||
{
|
||||
new FGuid(reader);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class USoundWave : UObject
|
||||
{
|
||||
|
|
@ -51,7 +53,18 @@ namespace PakReader.Parsers.Class
|
|||
}
|
||||
}
|
||||
|
||||
internal USoundWave(IoPackageReader reader, Dictionary<int, PropertyInfo> properties, Stream ubulk,
|
||||
long ubulkOffset) : base(reader, properties)
|
||||
{
|
||||
Serialize(reader, ubulk, ubulkOffset);
|
||||
}
|
||||
|
||||
internal USoundWave(PackageReader reader, Stream ubulk, long ubulkOffset) : base(reader)
|
||||
{
|
||||
Serialize(reader, ubulk, ubulkOffset);
|
||||
}
|
||||
|
||||
private void Serialize(PackageReader reader, Stream ubulk, long ubulkOffset)
|
||||
{
|
||||
// if UE4.25+ && Windows -> True
|
||||
bStreaming = FModel.Globals.Game.Version >= EPakVersion.PATH_HASH_INDEX;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using PakReader.Parsers.Objects;
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UStringTable : IUExport
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Textures;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.Textures;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace PakReader.Parsers.Class
|
||||
namespace FModel.PakReader.Parsers.Class
|
||||
{
|
||||
public sealed class UTexture2D : UObject
|
||||
{
|
||||
public FTexturePlatformData[] PlatformDatas { get; }
|
||||
public FTexturePlatformData[] PlatformDatas { get; private set; }
|
||||
|
||||
SKImage image;
|
||||
public SKImage Image
|
||||
|
|
@ -49,7 +50,17 @@ namespace PakReader.Parsers.Class
|
|||
}
|
||||
}
|
||||
|
||||
internal UTexture2D(IoPackageReader reader, Dictionary<int, PropertyInfo> properties, Stream ubulk,
|
||||
long bulkOffset) : base(reader, properties)
|
||||
{
|
||||
Serialize(reader, ubulk, bulkOffset);
|
||||
}
|
||||
internal UTexture2D(PackageReader reader, Stream ubulk, long bulkOffset) : base(reader)
|
||||
{
|
||||
Serialize(reader, ubulk, bulkOffset);
|
||||
}
|
||||
|
||||
private void Serialize(PackageReader reader, Stream ubulk, long bulkOffset)
|
||||
{
|
||||
new FStripDataFlags(reader); // and I quote, "still no idea"
|
||||
new FStripDataFlags(reader); // "why there are two" :)
|
||||
|
|
|
|||
176
FModel/PakReader/Parsers/IoPackageReader.cs
Normal file
176
FModel/PakReader/Parsers/IoPackageReader.cs
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.Utils;
|
||||
|
||||
namespace FModel.PakReader.Parsers
|
||||
{
|
||||
public sealed class IoPackageReader : PackageReader
|
||||
{
|
||||
public readonly FIoGlobalData GlobalData;
|
||||
public readonly FPackageSummary Summary;
|
||||
public readonly FPackageObjectIndex[] ImportMap;
|
||||
public readonly FExportMapEntry[] ExportMap;
|
||||
|
||||
internal List<FObjectResource> FakeImportMap;
|
||||
|
||||
public override FNameEntrySerialized[] NameMap { get; }
|
||||
|
||||
private IUExport[] _dataExports;
|
||||
private Stream _ubulk;
|
||||
public override IUExport[] DataExports {
|
||||
get
|
||||
{
|
||||
if (_dataExports == null)
|
||||
ReadContent();
|
||||
return _dataExports;
|
||||
}
|
||||
}
|
||||
|
||||
private FName[] _dataExportTypes;
|
||||
public override FName[] DataExportTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dataExportTypes == null)
|
||||
ReadContent();
|
||||
return _dataExportTypes;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<FPackageObjectIndex, string> _importMappings;
|
||||
|
||||
public IoPackageReader(Stream uasset, Stream ubulk, FIoGlobalData globalData, FFileIoStoreReader reader, bool onlyInfo = false) : this(new BinaryReader(uasset),
|
||||
ubulk, globalData, reader, onlyInfo) { }
|
||||
public IoPackageReader(BinaryReader uasset, Stream ubulk, FIoGlobalData globalData, FFileIoStoreReader reader, bool onlyInfo = false)
|
||||
{
|
||||
Loader = uasset;
|
||||
_ubulk = ubulk;
|
||||
GlobalData = globalData;
|
||||
Summary = new FPackageSummary(this);
|
||||
|
||||
var nameMap = new List<FNameEntrySerialized>();
|
||||
var nameHashes = new List<ulong>();
|
||||
if (Summary.NameMapNamesSize > 0)
|
||||
{
|
||||
Loader.BaseStream.Position = Summary.NameMapNamesOffset;
|
||||
var nameMapNames = Loader.ReadBytes(Summary.NameMapNamesSize);
|
||||
Loader.BaseStream.Position = Summary.NameMapHashesOffset;
|
||||
var nameMapHashes = Loader.ReadBytes(Summary.NameMapHashesSize);
|
||||
|
||||
FNameEntrySerialized.LoadNameBatch(nameMap, nameHashes, nameMapNames, nameMapHashes);
|
||||
}
|
||||
|
||||
NameMap = nameMap.ToArray();
|
||||
|
||||
Loader.BaseStream.Position = Summary.ImportMapOffset;
|
||||
var importMapCount = (Summary.ExportMapOffset - Summary.ImportMapOffset) / /*sizeof(FPackageObjectIndex)*/ sizeof(ulong);
|
||||
ImportMap = new FPackageObjectIndex[importMapCount];
|
||||
for (int i = 0; i < importMapCount; i++)
|
||||
{
|
||||
ImportMap[i] = new FPackageObjectIndex(Loader);
|
||||
}
|
||||
|
||||
Loader.BaseStream.Position = Summary.ExportMapOffset;
|
||||
var exportMapCount = (Summary.ExportBundlesOffset - Summary.ExportMapOffset) / FExportMapEntry.SIZE;
|
||||
ExportMap = new FExportMapEntry[exportMapCount];
|
||||
for (int i = 0; i < exportMapCount; i++)
|
||||
{
|
||||
ExportMap[i] = new FExportMapEntry(this);
|
||||
}
|
||||
|
||||
if (!onlyInfo)
|
||||
ReadContent();
|
||||
}
|
||||
|
||||
private void ReadContent()
|
||||
{
|
||||
Loader.BaseStream.Position = Summary.GraphDataOffset;
|
||||
var referencedPackagesCount = Loader.ReadInt32();
|
||||
var graphData = new (FPackageId importedPackageId, FArc[] arcs)[referencedPackagesCount];
|
||||
_importMappings = new Dictionary<FPackageObjectIndex, string>(referencedPackagesCount);
|
||||
FakeImportMap = new List<FObjectResource>();
|
||||
for (int i = 0; i < ImportMap.Length; i++)
|
||||
FakeImportMap.Add(new FObjectResource(new FName(), new FPackageIndex()));
|
||||
for (int i = 0; i < referencedPackagesCount; i++)
|
||||
{
|
||||
var importedPackageId = new FPackageId(Loader);
|
||||
var arcs = Loader.ReadTArray(() => new FArc(Loader));
|
||||
graphData[i] = (importedPackageId, arcs);
|
||||
var importedPackageName = Creator.Utils.GetFullPath(importedPackageId)
|
||||
?.Replace($"{Folders.GetGameName()}/Content", "Game");
|
||||
var package = Creator.Utils.GetPropertyPakPackage(importedPackageName) as IoPackage;
|
||||
if (package == null) continue;
|
||||
foreach (var export in package.Reader.ExportMap)
|
||||
{
|
||||
var realImportIndex = Array.FindIndex(ImportMap, it => it == export.GlobalImportIndex);
|
||||
var nextIndex = FakeImportMap.Count;
|
||||
FakeImportMap[realImportIndex] = new FObjectResource(new FName(export.ObjectName.String), new FPackageIndex(this, -(nextIndex + 1)));
|
||||
var outerResource = new FObjectResource(new FName(string.Concat(package.Reader.Summary.Name.String, ".", export.ObjectName.String)), new FPackageIndex());
|
||||
FakeImportMap.Add(outerResource);
|
||||
}
|
||||
}
|
||||
|
||||
var beginExportOffset = Summary.GraphDataOffset + Summary.GraphDataSize;
|
||||
var currentExportDataOffset = beginExportOffset;
|
||||
_dataExports = new IUExport[ExportMap.Length];
|
||||
_dataExportTypes = new FName[ExportMap.Length];
|
||||
for (var i = 0; i < ExportMap.Length; i++)
|
||||
{
|
||||
var exportMapEntry = ExportMap[i];
|
||||
FName exportType;
|
||||
|
||||
if (GlobalData != null && GlobalData.ScriptObjectByGlobalId.TryGetValue(exportMapEntry.ClassIndex, out var scriptObject))
|
||||
{
|
||||
exportType = scriptObject.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
exportType = new FName("Unknown");
|
||||
}
|
||||
|
||||
Loader.BaseStream.Position = currentExportDataOffset;
|
||||
|
||||
if (Globals.TypeMappings.TryGetValue(exportType.String, out var properties))
|
||||
{
|
||||
_dataExports[i] = exportType.String switch
|
||||
{
|
||||
"Texture2D" => new UTexture2D(this, properties, _ubulk,
|
||||
ExportMap.Sum(e => (long) e.CookedSerialSize) + beginExportOffset),
|
||||
"VirtualTexture2D" => new UTexture2D(this, properties, _ubulk, ExportMap.Sum(e => (long) e.CookedSerialSize) + beginExportOffset),
|
||||
//"CurveTable" => new UCurveTable(this),
|
||||
"DataTable" => new UDataTable(this, properties, exportType.String),
|
||||
//"FontFace" => new UFontFace(this, ubulk),
|
||||
"SoundWave" => new USoundWave(this, properties, _ubulk, ExportMap.Sum(e => (long) e.CookedSerialSize) + beginExportOffset),
|
||||
//"StringTable" => new UStringTable(this),
|
||||
//"AkMediaAssetData" => new UAkMediaAssetData(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
_ => new UObject(this, properties, type: exportType.String),
|
||||
};
|
||||
_dataExportTypes[i] = exportType;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
var header = new FUnversionedHeader(this);
|
||||
using var it = new FIterator(header);
|
||||
|
||||
FConsole.AppendText(string.Concat("\n", exportType.String, ": ", Summary.Name.String), "#CA6C6C", true);
|
||||
|
||||
do
|
||||
{
|
||||
FConsole.AppendText($"Val: {it.Current.Val} (IsNonZero: {it.Current.IsNonZero})", FColors.Yellow, true);
|
||||
}
|
||||
while (it.MoveNext());
|
||||
#endif
|
||||
}
|
||||
currentExportDataOffset += (int) exportMapEntry.CookedSerialSize;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => Summary.Name.String;
|
||||
}
|
||||
}
|
||||
123
FModel/PakReader/Parsers/LegacyPackageReader.cs
Normal file
123
FModel/PakReader/Parsers/LegacyPackageReader.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FModel.PakReader.Parsers.Class;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
|
||||
namespace FModel.PakReader.Parsers
|
||||
{
|
||||
public sealed class LegacyPackageReader : PackageReader
|
||||
{
|
||||
public FPackageFileSummary PackageFileSummary { get; }
|
||||
public FObjectImport[] ImportMap { get; }
|
||||
public FObjectExport[] ExportMap { get; }
|
||||
|
||||
public override FNameEntrySerialized[] NameMap { get; }
|
||||
|
||||
public override IUExport[] DataExports { get; }
|
||||
public override FName[] DataExportTypes { get; }
|
||||
|
||||
public LegacyPackageReader(string uasset, string uexp, string ubulk) : this(File.OpenRead(uasset), File.OpenRead(uexp), File.Exists(ubulk) ? File.OpenRead(ubulk) : null) { }
|
||||
public LegacyPackageReader(Stream uasset, Stream uexp, Stream ubulk) : this(new BinaryReader(uasset), new BinaryReader(uexp), ubulk) { }
|
||||
|
||||
LegacyPackageReader(BinaryReader uasset, BinaryReader uexp, Stream ubulk)
|
||||
{
|
||||
Loader = uasset;
|
||||
PackageFileSummary = new FPackageFileSummary(Loader);
|
||||
|
||||
NameMap = SerializeNameMap();
|
||||
ImportMap = SerializeImportMap();
|
||||
ExportMap = SerializeExportMap();
|
||||
DataExports = new IUExport[ExportMap.Length];
|
||||
DataExportTypes = new FName[ExportMap.Length];
|
||||
Loader = uexp;
|
||||
for(int i = 0; i < ExportMap.Length; i++)
|
||||
{
|
||||
FObjectExport Export = ExportMap[i];
|
||||
{
|
||||
FName ExportType;
|
||||
if (Export.ClassIndex.IsNull)
|
||||
ExportType = DataExportTypes[i] = ReadFName(); // check if this is true, I don't know if Fortnite ever uses this
|
||||
else if (Export.ClassIndex.IsExport)
|
||||
ExportType = DataExportTypes[i] = ExportMap[Export.ClassIndex.AsExport].SuperIndex.Resource.ObjectName;
|
||||
else if (Export.ClassIndex.IsImport)
|
||||
ExportType = DataExportTypes[i] = ImportMap[Export.ClassIndex.AsImport].ObjectName;
|
||||
else
|
||||
throw new FileLoadException("Can't get class name"); // Shouldn't reach this unless the laws of math have bent to MagmaReef's will
|
||||
|
||||
var pos = Position = Export.SerialOffset - PackageFileSummary.TotalHeaderSize;
|
||||
DataExports[i] = ExportType.String switch
|
||||
{
|
||||
"Texture2D" => new UTexture2D(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
"VirtualTexture2D" => new UTexture2D(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
"CurveTable" => new UCurveTable(this),
|
||||
"DataTable" => new UDataTable(this),
|
||||
"FontFace" => new UFontFace(this, ubulk),
|
||||
"SoundWave" => new USoundWave(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
"StringTable" => new UStringTable(this),
|
||||
"AkMediaAssetData" => new UAkMediaAssetData(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
_ => new UObject(this),
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
if (pos + Export.SerialSize != Position)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[ExportType={ExportType.String}] Didn't read {Export.ObjectName} correctly (at {Position}, should be {pos + Export.SerialSize}, {pos + Export.SerialSize - Position} behind)");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
FNameEntrySerialized[] SerializeNameMap()
|
||||
{
|
||||
if (PackageFileSummary.NameCount > 0)
|
||||
{
|
||||
Loader.BaseStream.Position = PackageFileSummary.NameOffset;
|
||||
|
||||
var OutNameMap = new FNameEntrySerialized[PackageFileSummary.NameCount];
|
||||
for (int NameMapIdx = 0; NameMapIdx < PackageFileSummary.NameCount; ++NameMapIdx)
|
||||
{
|
||||
// Read the name entry from the file.
|
||||
OutNameMap[NameMapIdx] = new FNameEntrySerialized(Loader);
|
||||
}
|
||||
return OutNameMap;
|
||||
}
|
||||
return Array.Empty<FNameEntrySerialized>();
|
||||
}
|
||||
|
||||
FObjectImport[] SerializeImportMap()
|
||||
{
|
||||
if (PackageFileSummary.ImportCount > 0)
|
||||
{
|
||||
Loader.BaseStream.Position = PackageFileSummary.ImportOffset;
|
||||
|
||||
var OutImportMap = new FObjectImport[PackageFileSummary.ImportCount];
|
||||
for (int ImportMapIdx = 0; ImportMapIdx < PackageFileSummary.ImportCount; ++ImportMapIdx)
|
||||
{
|
||||
OutImportMap[ImportMapIdx] = new FObjectImport(this);
|
||||
}
|
||||
return OutImportMap;
|
||||
}
|
||||
return Array.Empty<FObjectImport>();
|
||||
}
|
||||
|
||||
FObjectExport[] SerializeExportMap()
|
||||
{
|
||||
if (PackageFileSummary.ExportCount > 0)
|
||||
{
|
||||
Loader.BaseStream.Position = PackageFileSummary.ExportOffset;
|
||||
|
||||
var OutExportMap = new FObjectExport[PackageFileSummary.ExportCount];
|
||||
for (int ExportMapIdx = 0; ExportMapIdx < PackageFileSummary.ExportCount; ++ExportMapIdx)
|
||||
{
|
||||
OutExportMap[ExportMapIdx] = new FObjectExport(this);
|
||||
}
|
||||
return OutExportMap;
|
||||
}
|
||||
return Array.Empty<FObjectExport>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Parsers.Objects
|
||||
namespace FModel.PakReader.Parsers.Objects
|
||||
{
|
||||
public enum EAnimationCompressionFormat
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace PakReader.Parsers.Objects
|
||||
namespace FModel.PakReader.Parsers.Objects
|
||||
{
|
||||
public enum EAnimationKeyFormat : byte
|
||||
{
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user