diff --git a/CUE4Parse b/CUE4Parse index 095d8457..ff03587d 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 095d8457e2e4a2fb307b9ac688420ed30b6207bf +Subproject commit ff03587d77e77bb7f7c9b927a38d87e3bdbaa07c diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 91804547..ee169896 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -2,20 +2,22 @@ using System; using System.Runtime.CompilerServices; using CUE4Parse.UE4.Assets.Exports; using FModel.Creator.Bases; -using FModel.Creator.Bases.BB; using FModel.Creator.Bases.FN; using FModel.Creator.Bases.MV; -using FModel.Creator.Bases.SB; namespace FModel.Creator; public class CreatorPackage : IDisposable { - private UObject _object; + private string _pkgName; + private string _exportType; + private Lazy _object; private EIconStyle _style; - public CreatorPackage(UObject uObject, EIconStyle style) + public CreatorPackage(string packageName, string exportType, Lazy uObject, EIconStyle style) { + _pkgName = packageName; + _exportType = exportType; _object = uObject; _style = style; } @@ -30,7 +32,7 @@ public class CreatorPackage : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryConstructCreator(out UCreator creator) { - switch (_object.ExportType) + switch (_exportType) { // Fortnite case "AthenaConsumableEmoteItemDefinition": @@ -145,16 +147,16 @@ public class CreatorPackage : IDisposable case "FortVehicleCosmeticsItemDefinition_EngineAudio": creator = _style switch { - EIconStyle.Cataba => new BaseCommunity(_object, _style, "Cataba"), - _ => new BaseIcon(_object, _style) + EIconStyle.Cataba => new BaseCommunity(_object.Value, _style, "Cataba"), + _ => new BaseIcon(_object.Value, _style) }; return true; case "JunoAthenaCharacterItemOverrideDefinition": case "JunoAthenaDanceItemOverrideDefinition": - creator = new BaseJuno(_object, _style); + creator = new BaseJuno(_object.Value, _style); return true; case "FortTandemCharacterData": - creator = new BaseTandem(_object, _style); + creator = new BaseTandem(_object.Value, _style); return true; case "FortTrapItemDefinition": case "FortSpyTechItemDefinition": @@ -166,26 +168,25 @@ public class CreatorPackage : IDisposable case "FortWeaponMeleeDualWieldItemDefinition": case "FortCreativeWeaponRangedItemDefinition": case "Daybreak_LevelExitVehicle_PartItemDefinition_C": - creator = new BaseIconStats(_object, _style); + creator = new BaseIconStats(_object.Value, _style); return true; case "FortItemSeriesDefinition": - creator = new BaseSeries(_object, _style); + creator = new BaseSeries(_object.Value, _style); return true; case "MaterialInstanceConstant" - when _object.Owner != null && - (_object.Owner.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || - _object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}", StringComparison.OrdinalIgnoreCase) || - _object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}", StringComparison.OrdinalIgnoreCase)): - creator = new BaseMaterialInstance(_object, _style); + when _pkgName.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || + _pkgName.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || + _pkgName.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase): + creator = new BaseMaterialInstance(_object.Value, _style); return true; case "AthenaItemShopOfferDisplayData": - creator = new BaseOfferDisplayData(_object, _style); + creator = new BaseOfferDisplayData(_object.Value, _style); return true; case "FortMtxOfferData": - creator = new BaseMtxOffer(_object, _style); + creator = new BaseMtxOffer(_object.Value, _style); return true; case "FortPlaylistAthena": - creator = new BasePlaylist(_object, _style); + creator = new BasePlaylist(_object.Value, _style); return true; case "FortFeatItemDefinition": case "FortQuestItemDefinition": @@ -193,17 +194,17 @@ public class CreatorPackage : IDisposable case "FortQuestItemDefinition_Campaign": case "AthenaDailyQuestDefinition": case "FortUrgentQuestItemDefinition": - creator = new Bases.FN.BaseQuest(_object, _style); + creator = new Bases.FN.BaseQuest(_object.Value, _style); return true; case "FortCompendiumItemDefinition": case "FortChallengeBundleItemDefinition": - creator = new BaseBundle(_object, _style); + creator = new BaseBundle(_object.Value, _style); return true; // case "AthenaSeasonItemDefinition": // creator = new BaseSeason(_object, _style); // return true; case "FortItemAccessTokenType": - creator = new BaseItemAccessToken(_object, _style); + creator = new BaseItemAccessToken(_object.Value, _style); return true; case "FortCreativeOption": case "PlaylistUserOptionEnum": @@ -217,14 +218,14 @@ public class CreatorPackage : IDisposable case "PlaylistUserTintedIconIntEnum": case "PlaylistUserOptionPrimaryAsset": case "PlaylistUserOptionCollisionProfileEnum": - creator = new BaseUserControl(_object, _style); + creator = new BaseUserControl(_object.Value, _style); return true; // PandaGame case "CharacterData": - creator = new BaseFighter(_object, _style); + creator = new BaseFighter(_object.Value, _style); return true; case "PerkGroup": - creator = new BasePerkGroup(_object, _style); + creator = new BasePerkGroup(_object.Value, _style); return true; case "StatTrackingBundleData": case "HydraSyncedDataAsset": @@ -237,10 +238,10 @@ public class CreatorPackage : IDisposable case "TauntData": case "SkinData": case "PerkData": - creator = new BasePandaIcon(_object, _style); + creator = new BasePandaIcon(_object.Value, _style); return true; case "QuestData": - creator = new Bases.MV.BaseQuest(_object, _style); + creator = new Bases.MV.BaseQuest(_object.Value, _style); return true; default: creator = null; @@ -248,7 +249,7 @@ public class CreatorPackage : IDisposable } } - public override string ToString() => $"{_object.ExportType} | {_style}"; + public override string ToString() => $"{_exportType} | {_style}"; public void Dispose() { diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index 69020523..e56328fd 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -53,7 +53,8 @@ using CUE4Parse.UE4.Wwise; using CUE4Parse_Conversion; using CUE4Parse_Conversion.Sounds; - +using CUE4Parse.UE4.Assets; +using CUE4Parse.UE4.Objects.UObject; using EpicManifestParser; using EpicManifestParser.UE; using EpicManifestParser.ZlibngDotNetDecompressor; @@ -620,13 +621,16 @@ public class CUE4ParseViewModel : ViewModel case "uasset": case "umap": { - var exports = Provider.LoadAllObjects(fullPath); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), saveProperties, updateUi); - if (HasFlag(bulk, EBulkType.Properties)) break; // do not search for viewable exports if we are dealing with jsons - - foreach (var e in exports) + var pkg = Provider.LoadPackage(fullPath); + if (saveProperties || updateUi) { - if (CheckExport(cancellationToken, e, bulk)) + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), saveProperties, updateUi); + if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons + } + + for (var i = 0; i < pkg.ExportMapLength; i++) + { + if (CheckExport(cancellationToken, pkg, i, bulk)) break; } @@ -807,25 +811,30 @@ public class CUE4ParseViewModel : ViewModel TabControl.AddTab(fullPath.SubstringAfterLast('/'), fullPath.SubstringBeforeLast('/'), parentExportType); TabControl.SelectedTab.ScrollTrigger = objectName; - var exports = Provider.LoadAllObjects(fullPath); + var pkg = Provider.LoadPackage(fullPath); TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); // json - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(exports, Formatting.Indented), false, false); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), false, false); - foreach (var e in exports) + for (var i = 0; i < pkg.ExportMapLength; i++) { - if (CheckExport(cancellationToken, e)) + if (CheckExport(cancellationToken, pkg, i)) break; } } - private bool CheckExport(CancellationToken cancellationToken, UObject export, EBulkType bulk = EBulkType.None) // return true once you wanna stop searching for exports + private bool CheckExport(CancellationToken cancellationToken, IPackage pkg, int index, EBulkType bulk = EBulkType.None) // return true once you wanna stop searching for exports { var isNone = bulk == EBulkType.None; var updateUi = !HasFlag(bulk, EBulkType.Auto); var saveTextures = HasFlag(bulk, EBulkType.Textures); - switch (export) + + var pointer = new FPackageIndex(pkg, index + 1).ResolvedObject; + if (pointer?.Object is null) return false; + + var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); + switch (dummy) { - case UVerseDigest verseDigest when isNone: + case UVerseDigest when isNone && pointer.Object.Value is UVerseDigest verseDigest: { if (!TabControl.CanAddTabs) return false; @@ -834,20 +843,21 @@ public class CUE4ParseViewModel : ViewModel TabControl.SelectedTab.SetDocumentText(verseDigest.ReadableCode, false, false); return true; } - case UTexture texture when isNone || saveTextures: + case UTexture when (isNone || saveTextures) && pointer.Object.Value is UTexture texture: { TabControl.SelectedTab.AddImage(texture, saveTextures, updateUi); return false; } - case USvgAsset svgasset when isNone || saveTextures: + case USvgAsset when (isNone || saveTextures) && pointer.Object.Value is USvgAsset svgasset: { + const int size = 512; var data = svgasset.GetOrDefault("SvgData"); var sourceFile = svgasset.GetOrDefault("SourceFile"); using var stream = new MemoryStream(data) { Position = 0 }; - var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(512, 512)); + var svg = new SkiaSharp.Extended.Svg.SKSvg(new SKSize(size, size)); svg.Load(stream); - var bitmap = new SKBitmap(512, 512); + var bitmap = new SKBitmap(size, size); using (var canvas = new SKCanvas(bitmap)) using (var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium }) { @@ -857,11 +867,10 @@ public class CUE4ParseViewModel : ViewModel if (saveTextures) { var fileName = sourceFile.SubstringAfterLast('/'); - var t = new TabImage(fileName, false, bitmap); var path = Path.Combine(UserSettings.Default.TextureDirectory, UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Directory : "", fileName!).Replace('\\', '/'); - System.IO.Directory.CreateDirectory(path.SubstringBeforeLast('/')); + Directory.CreateDirectory(path.SubstringBeforeLast('/')); using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); fs.Write(data, 0, data.Length); @@ -892,9 +901,9 @@ public class CUE4ParseViewModel : ViewModel case USoundWave when isNone: { var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed; - export.Decode(shouldDecompress, out var audioFormat, out var data); + pointer.Object.Value.Decode(shouldDecompress, out var audioFormat, out var data); var hasAf = !string.IsNullOrEmpty(audioFormat); - if (data == null || !hasAf || export.Owner == null) + if (data == null || !hasAf) { if (hasAf) FLogger.Append(ELog.Warning, () => FLogger.Text($"Unsupported audio format '{audioFormat}'", Constants.WHITE, true)); return false; @@ -916,16 +925,16 @@ public class CUE4ParseViewModel : ViewModel case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh: case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial && - !(Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && export.Owner != null && - (export.Owner.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || - export.Owner.Name.EndsWith($"/RenderSwitch_Materials/{export.Name}", StringComparison.OrdinalIgnoreCase) || - export.Owner.Name.EndsWith($"/MI_BPTile/{export.Name}", StringComparison.OrdinalIgnoreCase))): + !(Provider.InternalGameName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && + (pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || + pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || + pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))): { - if (SnooperViewer.TryLoadExport(cancellationToken, export)) + if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object)) SnooperViewer.Run(); return true; } - case UMaterialInstance m when isNone && ModelIsOverwritingMaterial: + case UMaterialInstance when isNone && ModelIsOverwritingMaterial && pointer.Object.Value is UMaterialInstance m: { SnooperViewer.Renderer.Swap(m); SnooperViewer.Run(); @@ -935,7 +944,7 @@ public class CUE4ParseViewModel : ViewModel case UAnimMontage when isNone && ModelIsWaitingAnimation: case UAnimComposite when isNone && ModelIsWaitingAnimation: { - SnooperViewer.Renderer.Animate(export); + SnooperViewer.Renderer.Animate(pointer.Object); SnooperViewer.Run(); return true; } @@ -947,19 +956,19 @@ public class CUE4ParseViewModel : ViewModel case UAnimMontage when HasFlag(bulk, EBulkType.Animations): case UAnimComposite when HasFlag(bulk, EBulkType.Animations): { - SaveExport(export, updateUi); + SaveExport(pointer.Object.Value, updateUi); return true; } default: { if (!isNone && !saveTextures) return false; - using var package = new CreatorPackage(export, UserSettings.Default.CosmeticStyle); - if (!package.TryConstructCreator(out var creator)) + using var cPackage = new CreatorPackage(pkg.Name, dummy.ExportType, pointer.Object, UserSettings.Default.CosmeticStyle); + if (!cPackage.TryConstructCreator(out var creator)) return false; creator.ParseForInfo(); - TabControl.SelectedTab.AddImage(export.Name, false, creator.Draw(), saveTextures, updateUi); + TabControl.SelectedTab.AddImage(pointer.Object.Value.Name, false, creator.Draw(), saveTextures, updateUi); return true; } diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index f00961a5..32c6dee4 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -77,33 +77,33 @@ public class Renderer : IDisposable Color = VertexColor.Default; } - public void Load(CancellationToken cancellationToken, UObject export) + public void Load(CancellationToken cancellationToken, UObject dummy, Lazy export) { ShowLights = false; Color = VertexColor.Default; - _saveCameraMode = export is not UWorld and not UBlueprintGeneratedClass; - switch (export) + _saveCameraMode = dummy is not UWorld and not UBlueprintGeneratedClass; + switch (dummy) { - case UStaticMesh st: + case UStaticMesh when export.Value is UStaticMesh st: LoadStaticMesh(st); break; - case USkeletalMesh sk: + case USkeletalMesh when export.Value is USkeletalMesh sk: LoadSkeletalMesh(sk); break; - case USkeleton skel: + case USkeleton when export.Value is USkeleton skel: LoadSkeleton(skel); break; - case UMaterialInstance mi: + case UMaterialInstance when export.Value is UMaterialInstance mi: LoadMaterialInstance(mi); break; - case UWorld wd: + case UWorld when export.Value is UWorld wd: LoadWorld(cancellationToken, wd, Transform.Identity); break; - case UBlueprintGeneratedClass bp: + case UBlueprintGeneratedClass when export.Value is UBlueprintGeneratedClass bp: LoadJunoWorld(cancellationToken, bp, Transform.Identity); Color = VertexColor.Colors; break; - case UPaperSprite ps: + case UPaperSprite when export.Value is UPaperSprite ps: LoadPaperSprite(ps); break; } @@ -119,7 +119,7 @@ public class Renderer : IDisposable Application.Current.Dispatcher.Invoke(() => model.Materials[section.MaterialIndex].Setup(Options, model.UvCount)); } - public void Animate(UObject anim) => Animate(anim, Options.SelectedModel); + public void Animate(Lazy anim) => Animate(anim.Value, Options.SelectedModel); private void Animate(UObject anim, FGuid guid) { if (!Options.TryGetModel(guid, out var m) || m is not SkeletalModel model) diff --git a/FModel/Views/Snooper/Snooper.cs b/FModel/Views/Snooper/Snooper.cs index 210b7100..dc3165f5 100644 --- a/FModel/Views/Snooper/Snooper.cs +++ b/FModel/Views/Snooper/Snooper.cs @@ -34,9 +34,9 @@ public class Snooper : GameWindow _init = false; } - public bool TryLoadExport(CancellationToken cancellationToken, UObject export) + public bool TryLoadExport(CancellationToken cancellationToken, UObject dummy, Lazy export) { - Renderer.Load(cancellationToken, export); + Renderer.Load(cancellationToken, dummy, export); return Renderer.Options.Models.Count > 0; }