Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions FModel/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ protected override void OnStartup(StartupEventArgs e)
UserSettings.Default.AudioDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
}

if (!Directory.Exists(UserSettings.Default.CodeDirectory))
{
createMe = true;
UserSettings.Default.CodeDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
}

if (!Directory.Exists(UserSettings.Default.ModelDirectory))
{
createMe = true;
Expand Down
47 changes: 47 additions & 0 deletions FModel/Creator/Bases/FN/BaseAssembledMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Objects;
using CUE4Parse.UE4.Objects.UObject;
using SkiaSharp;

namespace FModel.Creator.Bases.FN;

public class BaseAssembledMesh : UCreator
{
public BaseAssembledMesh(UObject uObject, EIconStyle style) : base(uObject, style)
{

}

public override void ParseForInfo()
{
if (Object.TryGetValue(out FInstancedStruct[] additionalData, "AdditionalData"))
{
foreach (var data in additionalData)
{
if (data.NonConstStruct?.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "SmallPreviewImage") == true)
{
Preview = Utils.GetBitmap(largePreview);
}
}
}
}

public override SKBitmap[] Draw()
{
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
using var c = new SKCanvas(ret);

switch (Style)
{
case EIconStyle.NoBackground:
DrawPreview(c);
break;
default:
DrawBackground(c);
DrawPreview(c);
break;
}

return new[] { ret };
}
}
10 changes: 10 additions & 0 deletions FModel/Creator/Bases/FN/BaseIconStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public override void ParseForInfo()
weaponRowValue.TryGetValue(out float dmgPb, "DmgPB"); //Damage at point blank
weaponRowValue.TryGetValue(out float mdpc, "MaxDamagePerCartridge"); //Max damage a weapon can do in a single hit, usually used for shotguns
weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"); //Headshot multiplier
weaponRowValue.TryGetValue(out float envDmgPb, "EnvDmgPB"); //Structure damage at point blank
weaponRowValue.TryGetValue(out int clipSize, "ClipSize"); //Item magazine size
weaponRowValue.TryGetValue(out float firingRate, "FiringRate"); //Item firing rate, value is shots per second
weaponRowValue.TryGetValue(out float swingTime, "SwingTime"); //Item swing rate, value is swing per second
Expand Down Expand Up @@ -115,6 +116,15 @@ public override void ParseForInfo()
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 160));
}
}
{
var envdmgmultiplier = bpc != 0f ? bpc : 1;
if (envDmgPb != 0f)

{
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "11AF67134E0F4E27E5E588806AB475BE", "Structure Damage"), envDmgPb * envdmgmultiplier, 160));
}
}

if (clipSize > 999f || clipSize == 0f)
{
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), Utils.GetLocalizedResource("", "0FAE8E5445029F2AA209ADB0FE49B23C", "Infinite"), -1));
Expand Down
5 changes: 5 additions & 0 deletions FModel/Creator/CreatorPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator)
case "FortCodeTokenItemDefinition":
case "FortSchematicItemDefinition":
case "FortAlterableItemDefinition":
case "SproutHousingItemDefinition":
case "SparksKeyboardItemDefinition":
case "FortWorldMultiItemDefinition":
case "FortAlterationItemDefinition":
case "FortExpeditionItemDefinition":
case "FortIngredientItemDefinition":
case "FortConsumableItemDefinition":
case "SproutBuildingItemDefinition":
case "StWFortAccoladeItemDefinition":
case "FortAccountBuffItemDefinition":
case "FortFOBCoreDecoItemDefinition":
Expand Down Expand Up @@ -163,6 +165,9 @@ public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator)
case "JunoAthenaDanceItemOverrideDefinition":
creator = new BaseJuno(_object.Value, _style);
return true;
case "AssembledMeshSchema":
creator = new BaseAssembledMesh(_object.Value, _style);
return true;
case "FortTandemCharacterData":
creator = new BaseTandem(_object.Value, _style);
return true;
Expand Down
13 changes: 11 additions & 2 deletions FModel/Resources/Cpp.xshd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Color name="BooleanConstants" foreground="#569cd6" fontWeight="bold" />

<RuleSet ignoreCase="false">
<Rule color="Comment">(\/\/.*|\/\*[\s\S]*?\*\/)</Rule>
<Span color="String" begin="&quot;" end="&quot;" />
<!-- UE Macros -->
<Keywords color="UEMacro">
Expand All @@ -44,10 +45,19 @@
<Word>Int16</Word>
<Word>Int32</Word>
<Word>Int64</Word>
<Word>int8</Word>
<Word>int16</Word>
<Word>int32</Word>
<Word>int64</Word>
<Word>uint</Word>
<Word>UInt8</Word>
<Word>UInt16</Word>
<Word>UInt32</Word>
<Word>UInt64</Word>
<Word>uint8</Word>
<Word>uint16</Word>
<Word>uint32</Word>
<Word>uint64</Word>
<Word>float</Word>
<Word>double</Word>
<Word>bool</Word>
Expand Down Expand Up @@ -83,6 +93,7 @@
<Word>inline</Word>
<Word>constexpr</Word>
<Word>default</Word>
<Word>&amp;&amp;</Word>
</Keywords>

<Keywords color="Pointer">
Expand Down Expand Up @@ -120,8 +131,6 @@

<Rule color="Brace">[\[\]\{\}]</Rule>

<Rule color="Comment">(\/\/.*|\/\*[\s\S]*?\*\/)</Rule>

<!-- Template Functions -->
<Rule color="Function">\b[A-Za-z_][A-Za-z0-9_]*\b(?=&lt;)</Rule>

Expand Down
7 changes: 7 additions & 0 deletions FModel/Settings/UserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ public string AudioDirectory
set => SetProperty(ref _audioDirectory, value);
}

private string _codeDirectory;
public string CodeDirectory
{
get => _codeDirectory;
set => SetProperty(ref _codeDirectory, value);
}

private string _modelDirectory;
public string ModelDirectory
{
Expand Down
14 changes: 14 additions & 0 deletions FModel/ViewModels/ApiEndpoints/DillyApiEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace FModel.ViewModels.ApiEndpoints;
public class DillyApiEndpoint : AbstractApiProvider
{
private Backup[] _backups;
private ManifestInfoDilly[] _manifests;

public DillyApiEndpoint(RestClient client) : base(client) { }

Expand All @@ -27,6 +28,19 @@ public Backup[] GetBackups(CancellationToken token)
return _backups ??= GetBackupsAsync(token).GetAwaiter().GetResult();
}

public async Task<ManifestInfoDilly[]> GetManifestsAsync(CancellationToken token)
{
var request = new FRestRequest($"https://export-service-new.dillyapis.com/v1/manifests");
var response = await _client.ExecuteAsync<ManifestInfoDilly[]>(request, token).ConfigureAwait(false);
Log.Information("[{Method}] [{Status}({StatusCode})] '{Resource}'", request.Method, response.StatusDescription, (int) response.StatusCode, response.ResponseUri?.OriginalString);
return response.Data;
}

public ManifestInfoDilly[] GetManifests(CancellationToken token)
{
return _manifests ??= GetManifestsAsync(token).GetAwaiter().GetResult();
}

public async Task<IDictionary<string, IDictionary<string, string>>> GetHotfixesAsync(CancellationToken token, string language = "en")
{
var request = new FRestRequest("https://api.fortniteapi.com/v1/cloudstorage/hotfixes")
Expand Down
7 changes: 7 additions & 0 deletions FModel/ViewModels/ApiEndpoints/Models/FModelResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ public class Backup
[J] public string Url { get; private set; }
}

[DebuggerDisplay("{" + nameof(AppName) + "}")]
public class ManifestInfoDilly
{
[J] public string AppName { get; private set; }
[J] public string DownloadUrl { get; private set; }
}

public class Donator
{
[J] public string Username { get; private set; }
Expand Down
3 changes: 1 addition & 2 deletions FModel/ViewModels/ApplicationViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public ApplicationViewModel()
if (UserSettings.Default.CurrentDir is null)
{
//If no game is selected, many things will break before a shutdown request is processed in the normal way.
//A hard exit is preferable to an unhandled expection in this case
//A hard exit is preferable to an unhandled exception in this case
Environment.Exit(0);
}

Expand All @@ -126,7 +126,6 @@ public ApplicationViewModel()
if (sender is not IAesVfsReader reader) return;
CUE4Parse.GameDirectory.Disable(reader);
};

CustomDirectories = new CustomDirectoriesViewModel();
SettingsView = new SettingsViewModel();
AesManager = new AesManagerViewModel(CUE4Parse);
Expand Down
41 changes: 34 additions & 7 deletions FModel/ViewModels/CUE4ParseViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,12 @@ public CUE4ParseViewModel()

public async Task Initialize()
{
await _apiEndpointView.EpicApi.VerifyAuth(CancellationToken.None);
await _threadWorkerView.Begin(cancellationToken =>
{
Provider.OnDemandOptions = new IoStoreOnDemandOptions
{
ChunkHostUri = new Uri("https://download.epicgames.com/", UriKind.Absolute),
ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")),
Authorization = new AuthenticationHeaderValue("Bearer", UserSettings.Default.LastAuthResponse.AccessToken),
Timeout = TimeSpan.FromSeconds(30)
};

Expand Down Expand Up @@ -287,6 +285,20 @@ await _threadWorkerView.Begin(cancellationToken =>
it => new FRandomAccessStreamArchive(it, manifest.FindFile(it)!.GetStream(), p.Versions));
});

var manifests = _apiEndpointView.DillyApi.GetManifests(cancellationToken);
var downloadUrl = manifests.First(x => x.AppName == "Fortnite_Studio").DownloadUrl;

using var client = new HttpClient();
var manifestBytes = client.GetByteArrayAsync(downloadUrl).GetAwaiter().GetResult();

var uefnManifest = FBuildPatchAppManifest.Deserialize(manifestBytes, manifestOptions);

Parallel.ForEach(uefnManifest.Files.Where(x => _fnLiveRegex.IsMatch(x.FileName)), fileManifest =>
{
p.RegisterVfs(fileManifest.FileName, [fileManifest.GetStream()],
it => new FRandomAccessStreamArchive(it, uefnManifest.FindFile(it)!.GetStream(), p.Versions));
});

var elapsedTime = Stopwatch.GetElapsedTime(startTs);
FLogger.Append(ELog.Information, () =>
FLogger.Text($"Fortnite [LIVE] has been loaded successfully in {elapsedTime.TotalMilliseconds:F1}ms", Constants.WHITE, true));
Expand Down Expand Up @@ -622,6 +634,9 @@ public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder
public void AudioFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto));

public void CodeFolder(CancellationToken cancellationToken, TreeItem folder)
=> BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Code | EBulkType.Auto));

public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None)
{
ApplicationService.ApplicationView.IsAssetsExplorerVisible = false;
Expand All @@ -635,6 +650,7 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
var saveProperties = HasFlag(bulk, EBulkType.Properties);
var saveTextures = HasFlag(bulk, EBulkType.Textures);
var saveAudio = HasFlag(bulk, EBulkType.Audio);
var saveDecompiled = HasFlag(bulk, EBulkType.Code);
switch (entry.Extension)
{
case "uasset":
Expand All @@ -649,6 +665,13 @@ public void Extract(CancellationToken cancellationToken, GameFile entry, bool ad
if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons
}

if (saveDecompiled)
{
if (Decompile(entry, false))
TabControl.SelectedTab.SaveDecompiled(updateUi);
break;
}

for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++)
{
if (CheckExport(cancellationToken, result.Package, i, bulk))
Expand Down Expand Up @@ -1363,11 +1386,13 @@ public void FindReferences(GameFile entry)
}


public void Decompile(GameFile entry)
public bool Decompile(GameFile entry, bool AddTab = true)
{
ApplicationService.ApplicationView.IsAssetsExplorerVisible = false;

if (TabControl.CanAddTabs) TabControl.AddTab(entry);
if (TabControl.CanAddTabs && AddTab)
{
ApplicationService.ApplicationView.IsAssetsExplorerVisible = false;
TabControl.AddTab(entry);
}
else TabControl.SelectedTab.SoftReset(entry);

TabControl.SelectedTab.TitleExtra = "Decompiled";
Expand Down Expand Up @@ -1405,9 +1430,11 @@ public void Decompile(GameFile entry)
cpp = Regex.Replace(cpp, "__verse_0x[a-fA-F0-9]{8}_", ""); // UnmangleCasedName
}
cpp = Regex.Replace(cpp, @"CallFunc_([A-Za-z0-9_]+)_ReturnValue", "$1");

cpp = Regex.Replace(cpp, @"K2Node_DynamicCast_([A-Za-z0-9_]+)", "$1");
cpp = Regex.Replace(cpp, @"K2Node_([A-Za-z0-9_]+)", "$1");

TabControl.SelectedTab.SetDocumentText(cpp, false, false);
return cpp.Length > 0;
}

private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool saveAudio, bool updateUi)
Expand Down
2 changes: 2 additions & 0 deletions FModel/ViewModels/Commands/RightClickMenuCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public override async void Execute(ApplicationViewModel contextViewModel, object
"Save_Models" => (EAction.Export, EShowAssetType.None, EBulkType.Meshes),
"Save_Animations" => (EAction.Export, EShowAssetType.None, EBulkType.Animations),
"Save_Audio" => (EAction.Export, EShowAssetType.None, EBulkType.Audio),
"Save_Code" => (EAction.Export, EShowAssetType.None, EBulkType.Code),

_ => throw new ArgumentOutOfRangeException("Unsupported asset action."),
};
Expand Down Expand Up @@ -109,6 +110,7 @@ await _threadWorkerView.Begin(cancellationToken =>
EBulkType.Meshes => (UserSettings.Default.ModelDirectory, "models"),
EBulkType.Animations => (UserSettings.Default.ModelDirectory, "animations"),
EBulkType.Audio => (UserSettings.Default.AudioDirectory, "audio files"),
EBulkType.Code => (UserSettings.Default.CodeDirectory, "code files"),
_ => (null, null),
};

Expand Down
8 changes: 2 additions & 6 deletions FModel/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ public ulong CriwareDecryptionKey
private string _propertiesSnapshot;
private string _textureSnapshot;
private string _audioSnapshot;
private string _codeSnapshot;
private string _modelSnapshot;
private string _gameSnapshot;
private ETexturePlatform _uePlatformSnapshot;
Expand Down Expand Up @@ -227,6 +228,7 @@ public void Initialize()
_propertiesSnapshot = UserSettings.Default.PropertiesDirectory;
_textureSnapshot = UserSettings.Default.TextureDirectory;
_audioSnapshot = UserSettings.Default.AudioDirectory;
_codeSnapshot = UserSettings.Default.CodeDirectory;
_modelSnapshot = UserSettings.Default.ModelDirectory;
_gameSnapshot = UserSettings.Default.GameDirectory;
_uePlatformSnapshot = UserSettings.Default.CurrentDir.TexturePlatform;
Expand Down Expand Up @@ -303,12 +305,6 @@ public bool Save(out List<SettingsOut> whatShouldIDo)
if (_ueGameSnapshot != SelectedUeGame || _customVersionsSnapshot != SelectedCustomVersions ||
_uePlatformSnapshot != SelectedUePlatform || _optionsSnapshot != SelectedOptions || // combobox
_mapStructTypesSnapshot != SelectedMapStructTypes ||
_outputSnapshot != UserSettings.Default.OutputDirectory || // textbox
_rawDataSnapshot != UserSettings.Default.RawDataDirectory || // textbox
_propertiesSnapshot != UserSettings.Default.PropertiesDirectory || // textbox
_textureSnapshot != UserSettings.Default.TextureDirectory || // textbox
_audioSnapshot != UserSettings.Default.AudioDirectory || // textbox
_modelSnapshot != UserSettings.Default.ModelDirectory || // textbox
_gameSnapshot != UserSettings.Default.GameDirectory) // textbox
restart = true;

Expand Down
10 changes: 10 additions & 0 deletions FModel/ViewModels/TabControlViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,17 @@ public void SaveProperty(bool updateUi)
Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text));
SaveCheck(directory, fileName, updateUi);
}
public void SaveDecompiled(bool updateUi)
{
var fileName = Path.ChangeExtension(Entry.Name, ".cpp");
var directory = Path.Combine(UserSettings.Default.PropertiesDirectory,
UserSettings.Default.KeepDirectoryStructure ? Entry.Directory : "", fileName).Replace('\\', '/');

Directory.CreateDirectory(directory.SubstringBeforeLast('/'));

Application.Current.Dispatcher.Invoke(() => File.WriteAllText(directory, Document.Text));
SaveCheck(directory, fileName, updateUi);
}
private void SaveCheck(string path, string fileName, bool updateUi)
{
if (File.Exists(path))
Expand Down
Loading