cost less export check

This commit is contained in:
Asval 2025-01-20 19:53:05 +01:00
parent 7431ef5592
commit 73fffc5545
5 changed files with 85 additions and 75 deletions

@ -1 +1 @@
Subproject commit 095d8457e2e4a2fb307b9ac688420ed30b6207bf
Subproject commit ff03587d77e77bb7f7c9b927a38d87e3bdbaa07c

View File

@ -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<UObject> _object;
private EIconStyle _style;
public CreatorPackage(UObject uObject, EIconStyle style)
public CreatorPackage(string packageName, string exportType, Lazy<UObject> 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()
{

View File

@ -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<byte[]>("SvgData");
var sourceFile = svgasset.GetOrDefault<string>("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;
}

View File

@ -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<UObject> 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<UObject> 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)

View File

@ -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<UObject> export)
{
Renderer.Load(cancellationToken, export);
Renderer.Load(cancellationToken, dummy, export);
return Renderer.Options.Models.Count > 0;
}