mirror of
https://github.com/4sval/FModel.git
synced 2026-06-20 15:00:12 -05:00
a bunch of stuff (#663)
Some checks are pending
FModel QA Builder / build (push) Waiting to run
Some checks are pending
FModel QA Builder / build (push) Waiting to run
Co-authored-by: Chompster86 <chompster86@gmail.com> Co-authored-by: Asval <asval.contactme@gmail.com>
This commit is contained in:
parent
2212605825
commit
7e7d6d5bc6
|
|
@ -1 +1 @@
|
|||
Subproject commit 1320fd09f6373c997758ae8160d6f8035c4c8b93
|
||||
Subproject commit 8fc2c250ab0e581878813127610c28e49e947239
|
||||
|
|
@ -92,6 +92,12 @@ public partial class App
|
|||
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;
|
||||
|
|
|
|||
47
FModel/Creator/Bases/FN/BaseAssembledMesh.cs
Normal file
47
FModel/Creator/Bases/FN/BaseAssembledMesh.cs
Normal file
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
@ -87,6 +87,7 @@ public class BaseIconStats : BaseIcon
|
|||
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
|
||||
|
|
@ -115,6 +116,15 @@ public class BaseIconStats : BaseIcon
|
|||
_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));
|
||||
|
|
|
|||
|
|
@ -100,12 +100,14 @@ public class CreatorPackage : IDisposable
|
|||
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":
|
||||
|
|
@ -163,6 +165,9 @@ public class CreatorPackage : IDisposable
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<Color name="BooleanConstants" foreground="#569cd6" fontWeight="bold" />
|
||||
|
||||
<RuleSet ignoreCase="false">
|
||||
<Rule color="Comment">(\/\/.*|\/\*[\s\S]*?\*\/)</Rule>
|
||||
<Span color="String" begin=""" end=""" />
|
||||
<!-- UE Macros -->
|
||||
<Keywords color="UEMacro">
|
||||
|
|
@ -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>
|
||||
|
|
@ -83,6 +93,7 @@
|
|||
<Word>inline</Word>
|
||||
<Word>constexpr</Word>
|
||||
<Word>default</Word>
|
||||
<Word>&&</Word>
|
||||
</Keywords>
|
||||
|
||||
<Keywords color="Pointer">
|
||||
|
|
@ -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(?=<)</Rule>
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,13 @@ namespace FModel.Settings
|
|||
set => SetProperty(ref _audioDirectory, value);
|
||||
}
|
||||
|
||||
private string _codeDirectory;
|
||||
public string CodeDirectory
|
||||
{
|
||||
get => _codeDirectory;
|
||||
set => SetProperty(ref _codeDirectory, value);
|
||||
}
|
||||
|
||||
private string _modelDirectory;
|
||||
public string ModelDirectory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace FModel.ViewModels.ApiEndpoints;
|
|||
public class DillyApiEndpoint : AbstractApiProvider
|
||||
{
|
||||
private Backup[] _backups;
|
||||
private ManifestInfoDilly[] _manifests;
|
||||
|
||||
public DillyApiEndpoint(RestClient client) : base(client) { }
|
||||
|
||||
|
|
@ -27,6 +28,19 @@ public class DillyApiEndpoint : AbstractApiProvider
|
|||
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")
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public class ApplicationViewModel : ViewModel
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +126,6 @@ public class ApplicationViewModel : ViewModel
|
|||
if (sender is not IAesVfsReader reader) return;
|
||||
CUE4Parse.GameDirectory.Disable(reader);
|
||||
};
|
||||
|
||||
CustomDirectories = new CustomDirectoriesViewModel();
|
||||
SettingsView = new SettingsViewModel();
|
||||
AesManager = new AesManagerViewModel(CUE4Parse);
|
||||
|
|
|
|||
|
|
@ -225,14 +225,12 @@ public class CUE4ParseViewModel : ViewModel
|
|||
|
||||
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)
|
||||
};
|
||||
|
||||
|
|
@ -287,6 +285,20 @@ public class CUE4ParseViewModel : ViewModel
|
|||
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));
|
||||
|
|
@ -622,6 +634,9 @@ public class CUE4ParseViewModel : ViewModel
|
|||
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;
|
||||
|
|
@ -635,6 +650,7 @@ public class CUE4ParseViewModel : ViewModel
|
|||
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":
|
||||
|
|
@ -649,6 +665,13 @@ public class CUE4ParseViewModel : ViewModel
|
|||
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))
|
||||
|
|
@ -1365,11 +1388,13 @@ public class CUE4ParseViewModel : ViewModel
|
|||
}
|
||||
|
||||
|
||||
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";
|
||||
|
|
@ -1398,18 +1423,21 @@ public class CUE4ParseViewModel : ViewModel
|
|||
if (dummy is not UClass || pointer.Object.Value is not UClass blueprint)
|
||||
continue;
|
||||
|
||||
cppList.Add(blueprint.DecompileBlueprintToPseudo(cookedMetaData));
|
||||
cppList.Add(blueprint.DecompileBlueprintToPseudo(pkg.Mappings, cookedMetaData));
|
||||
}
|
||||
|
||||
if (cppList.Count == 0) return false;
|
||||
var cpp = cppList.Count > 1 ? string.Join("\n\n", cppList) : cppList.FirstOrDefault() ?? string.Empty;
|
||||
if (entry.Path.Contains("_Verse.uasset"))
|
||||
{
|
||||
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 true;
|
||||
}
|
||||
|
||||
private void SaveAndPlaySound(CancellationToken cancellationToken, string fullPath, string ext, byte[] data, bool saveAudio, bool updateUi)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
"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."),
|
||||
};
|
||||
|
|
@ -109,6 +110,7 @@ public class RightClickMenuCommand : ViewModelCommand<ApplicationViewModel>
|
|||
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),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ public class SettingsViewModel : ViewModel
|
|||
private string _propertiesSnapshot;
|
||||
private string _textureSnapshot;
|
||||
private string _audioSnapshot;
|
||||
private string _codeSnapshot;
|
||||
private string _modelSnapshot;
|
||||
private string _gameSnapshot;
|
||||
private ETexturePlatform _uePlatformSnapshot;
|
||||
|
|
@ -227,6 +228,7 @@ public class SettingsViewModel : ViewModel
|
|||
_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;
|
||||
|
|
@ -303,12 +305,6 @@ public class SettingsViewModel : ViewModel
|
|||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,17 @@ public class TabItem : ViewModel
|
|||
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))
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ public partial class UpdateViewModel : ViewModel
|
|||
if (username.Equals("Asval", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
username = "4sval"; // found out the hard way co-authored usernames can't be trusted
|
||||
} else if (username.Equals("Krowe Moh", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
username = "Krowe-moh";
|
||||
}
|
||||
|
||||
coAuthorMap[commit].Add(username);
|
||||
|
|
@ -101,7 +104,7 @@ public partial class UpdateViewModel : ViewModel
|
|||
}
|
||||
catch
|
||||
{
|
||||
//
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,26 @@
|
|||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Decompiled Blueprints"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
<MultiBinding Converter="{x:Static converters:MultiParameterConverter.Instance}">
|
||||
<Binding Source="Save_Code" />
|
||||
<Binding Path="Tag"
|
||||
RelativeSource="{RelativeSource AncestorType=ContextMenu}" />
|
||||
</MultiBinding>
|
||||
</MenuItem.CommandParameter>
|
||||
<MenuItem.Icon>
|
||||
<Viewbox Width="16"
|
||||
Height="16">
|
||||
<Canvas Width="24"
|
||||
Height="24">
|
||||
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.ForegroundBrush}}"
|
||||
Data="{StaticResource CodeIcon}" />
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Save Folder's Packages Models"
|
||||
Command="{Binding RightClickMenuCommand}">
|
||||
<MenuItem.CommandParameter>
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ public partial class EndpointEditor
|
|||
|
||||
private void OnEvaluator(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo { FileName = "https://jsonpath.herokuapp.com/", UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo { FileName = "https://jsonpath.com/", UseShellExecute = true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FModel.Views.Resources.Converters
|
||||
{
|
||||
public class FileNameWithoutExtensionConverter : IValueConverter
|
||||
{
|
||||
public static readonly FileNameWithoutExtensionConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> value is string s ? Path.GetFileNameWithoutExtension(s) : value;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using FModel.Extensions;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
|
|
|
|||
23
FModel/Views/Resources/Converters/TextToRefreshConverter.cs
Normal file
23
FModel/Views/Resources/Converters/TextToRefreshConverter.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FModel.Views.Resources.Converters;
|
||||
|
||||
public class TextToRefreshConverter : IValueConverter
|
||||
{
|
||||
public static readonly TextToRefreshConverter Instance = new();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is DateTime dt && dt != DateTime.MaxValue)
|
||||
return $"Next Refresh: {dt:MMM d, yyyy}";
|
||||
|
||||
return "Next Refresh: Never";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -771,9 +771,15 @@
|
|||
</Image.ContextMenu>
|
||||
</Image>
|
||||
</Border>
|
||||
<TextBlock Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Center"
|
||||
Visibility="{Binding SelectedItem.HasMultipleImages, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Text="{Binding SelectedItem.Page, ElementName=TabControlName}" />
|
||||
<StackPanel Grid.Column="2" VerticalAlignment="Bottom" HorizontalAlignment="Center">
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Text="{Binding SelectedItem.SelectedImage.ExportName, Converter={x:Static converters:FileNameWithoutExtensionConverter.Instance}, ElementName=TabControlName}"
|
||||
FontSize="10" FontWeight="SemiBold"
|
||||
Visibility="{Binding SelectedItem.HasImage, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Visibility="{Binding SelectedItem.HasMultipleImages, ElementName=TabControlName, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Text="{Binding SelectedItem.Page, ElementName=TabControlName}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding SelectedItem.HasImage, ElementName=TabControlName}" Value="False">
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@
|
|||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="* Require a restart for changes to take effect"
|
||||
<TextBlock Grid.Column="0" Text="* May Require a restart for changes to take effect"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="11" Margin="0 0 10 0"
|
||||
Foreground="{DynamicResource {x:Static adonisUi:Brushes.Layer1InteractionForegroundBrush}}" />
|
||||
<Button Grid.Column="1" MinWidth="78" Margin="0 0 12 0" IsDefault="True" IsCancel="False"
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ public partial class SettingsView
|
|||
|
||||
_applicationView.CUE4Parse.Provider.ReadScriptData = UserSettings.Default.ReadScriptData;
|
||||
_applicationView.CUE4Parse.Provider.ReadShaderMaps = UserSettings.Default.ReadShaderMaps;
|
||||
|
||||
UserSettings.Save();
|
||||
}
|
||||
|
||||
private void OnBrowseOutput(object sender, RoutedEventArgs e)
|
||||
|
|
@ -74,6 +76,7 @@ public partial class SettingsView
|
|||
UserSettings.Default.PropertiesDirectory = path;
|
||||
UserSettings.Default.TextureDirectory = path;
|
||||
UserSettings.Default.AudioDirectory = path;
|
||||
UserSettings.Default.CodeDirectory = path;
|
||||
}
|
||||
|
||||
private void OnBrowseDirectories(object sender, RoutedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
</adonisControls:SplitButton.SplitMenu>
|
||||
</adonisControls:SplitButton>
|
||||
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Right" FontSize="10" Margin="0 2.5 0 0"
|
||||
Text="{Binding NextUpdateCheck, Source={x:Static local:Settings.UserSettings.Default}, StringFormat=Next Refresh: {0:MMM d, yyyy}}" />
|
||||
Text="{Binding NextUpdateCheck, Source={x:Static local:Settings.UserSettings.Default}, Converter={x:Static converters:TextToRefreshConverter.Instance}}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user