mirror of
https://github.com/4sval/FModel.git
synced 2026-04-24 14:59:29 -05:00
removed empty paks + fixed backups and loading modes + improved speed
This commit is contained in:
parent
220ed024cd
commit
319a3d52b3
|
|
@ -1,7 +1,6 @@
|
|||
using FModel.Utils;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FModel.PakReader;
|
||||
using FModel.PakReader.IO;
|
||||
|
|
@ -16,11 +15,10 @@ namespace FModel.Creator
|
|||
{
|
||||
foreach (var ioStore in Globals.CachedIoStores.Values)
|
||||
{
|
||||
if (ioStore.IsInitialized)
|
||||
if (ioStore.Chunks.TryGetValue(id.Id, out string path))
|
||||
{
|
||||
var entry = ioStore.Files.FirstOrDefault(it => it.Value.ChunkId.ChunkId == id.Id).Value;
|
||||
if (entry != null)
|
||||
return ioStore.MountPoint + entry.Name;
|
||||
if (ioStore.Files.TryGetValue(ioStore.MountPoint + path.Substring(0, path.LastIndexOf(".")), out FIoStoreEntry value))
|
||||
return ioStore.MountPoint + value.Name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +60,7 @@ namespace FModel.Creator
|
|||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf('.')).Length);
|
||||
return Assets.GetPackage(entry, mount);
|
||||
}
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
|
||||
public static ArraySegment<byte>[] GetPropertyArraySegmentByte(string value)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
|
@ -21,7 +22,7 @@ namespace FModel.Grabber.Paks
|
|||
{
|
||||
static class PaksGrabber
|
||||
{
|
||||
private static readonly Regex _pakFileRegex = new Regex(@"^FortniteGame/Content/Paks/pakchunk(?:0|10.*|\w+)-WindowsClient\.pak$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
private static readonly Regex _pakFileRegex = new Regex(@"^FortniteGame/Content/Paks/pakchunk(?:0|10.*|\w+)-WindowsClient\.(pak|utoc|ucas)$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
public static async Task PopulateMenu()
|
||||
{
|
||||
|
|
@ -81,26 +82,44 @@ namespace FModel.Grabber.Paks
|
|||
continue;
|
||||
}
|
||||
|
||||
var pakStream = fileManifest.GetStream();
|
||||
if (pakStream.Length == 365) continue;
|
||||
|
||||
var pakFileName = fileManifest.Name.Replace('/', '\\');
|
||||
PakFileReader pakFile = new PakFileReader(pakFileName, fileManifest.GetStream());
|
||||
|
||||
if (pakFiles++ == 0)
|
||||
if (pakFileName.EndsWith(".pak"))
|
||||
{
|
||||
// define the current game thank to the pak path
|
||||
Folders.SetGameName(pakFileName);
|
||||
|
||||
Globals.Game.Version = pakFile.Info.Version;
|
||||
Globals.Game.SubVersion = pakFile.Info.SubVersion;
|
||||
}
|
||||
|
||||
await Application.Current.Dispatcher.InvokeAsync(delegate
|
||||
{
|
||||
MenuItems.pakFiles.Add(new PakMenuItemViewModel
|
||||
PakFileReader pakFile = new PakFileReader(pakFileName, pakStream);
|
||||
if (pakFiles++ == 0)
|
||||
{
|
||||
PakFile = pakFile,
|
||||
IsEnabled = false
|
||||
// define the current game thank to the pak path
|
||||
Folders.SetGameName(pakFileName);
|
||||
|
||||
Globals.Game.Version = pakFile.Info.Version;
|
||||
Globals.Game.SubVersion = pakFile.Info.SubVersion;
|
||||
}
|
||||
|
||||
await Application.Current.Dispatcher.InvokeAsync(delegate
|
||||
{
|
||||
MenuItems.pakFiles.Add(new PakMenuItemViewModel
|
||||
{
|
||||
PakFile = pakFile,
|
||||
IsEnabled = false
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (pakFileName.EndsWith(".ucas"))
|
||||
{
|
||||
var utocStream = manifest.FileManifests.FirstOrDefault(x => x.Name.Equals(fileManifest.Name.Replace(".ucas", ".utoc")));
|
||||
var ioStore = new FFileIoStoreReader(pakFileName.SubstringAfterLast('\\'), utocStream.GetStream(), pakStream);
|
||||
await Application.Current.Dispatcher.InvokeAsync(delegate
|
||||
{
|
||||
MenuItems.pakFiles.Add(new PakMenuItemViewModel
|
||||
{
|
||||
IoStore = ioStore,
|
||||
IsEnabled = false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Properties.Settings.Default.PakPath.EndsWith("-val.manifest"))
|
||||
|
|
@ -147,7 +166,13 @@ namespace FModel.Grabber.Paks
|
|||
string[] paks = Directory.GetFiles(Properties.Settings.Default.PakPath, "*.pak");
|
||||
for (int i = 0; i < paks.Length; i++)
|
||||
{
|
||||
if (!Utils.Paks.IsFileReadLocked(new FileInfo(paks[i])))
|
||||
var pakInfo = new FileInfo(paks[i]);
|
||||
if (pakInfo.Length == 365)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Utils.Paks.IsFileReadLocked(pakInfo))
|
||||
{
|
||||
PakFileReader pakFile = new PakFileReader(paks[i]);
|
||||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[PAK]", "[Registering]", $"{pakFile.FileName} with GUID {pakFile.Info.EncryptionKeyGuid.Hex}");
|
||||
|
|
@ -184,6 +209,7 @@ namespace FModel.Grabber.Paks
|
|||
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
|
||||
|
|
@ -198,7 +224,7 @@ namespace FModel.Grabber.Paks
|
|||
FConsole.AppendText(string.Format(Properties.Resources.PakFileLocked, Path.GetFileNameWithoutExtension(utoc)), FColors.Red, true);
|
||||
DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[IO Store]", "[Locked]", utoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,11 +119,11 @@ namespace FModel
|
|||
catch
|
||||
{
|
||||
rawMappings ??= await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_TYPE_MAPPINGS);
|
||||
rawEnumMappings ??= await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_ENUM_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);
|
||||
var rawMappings = await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_TYPE_MAPPINGS);
|
||||
var rawEnumMappings = await Endpoints.GetStringEndpoint(Endpoints.FORTNITE_ENUM_MAPPINGS);
|
||||
#endif
|
||||
var serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
|
|
@ -148,6 +148,7 @@ namespace FModel
|
|||
public void ReloadMappings(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LoadMappings();
|
||||
Globals.gNotifier.ShowCustomMessage("Mappings", "Reloaded successfully");
|
||||
}
|
||||
|
||||
private void AeConfiguration()
|
||||
|
|
@ -161,7 +162,7 @@ namespace FModel
|
|||
CommandBindings.Add(Frm.FindBinding);
|
||||
}
|
||||
|
||||
#region MENU ITEMS
|
||||
#region MENU ITEMS
|
||||
|
||||
private void OnAutoShortcutPressed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
|
@ -388,9 +389,9 @@ namespace FModel
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region TREEVIEW
|
||||
#region TREEVIEW
|
||||
|
||||
private void OnSelectedPathChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
|
|
@ -418,9 +419,9 @@ namespace FModel
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region LISTBOX
|
||||
#region LISTBOX
|
||||
|
||||
private void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
|
|
@ -496,9 +497,9 @@ namespace FModel
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region BUTTONS
|
||||
#region BUTTONS
|
||||
|
||||
private void OnImageOpenClick(object sender, RoutedEventArgs e) => ImageBoxVm.imageBoxViewModel.OpenImage();
|
||||
|
||||
|
|
@ -517,9 +518,9 @@ namespace FModel
|
|||
await Assets.GetUserSelection(FModel_AssetsList.SelectedItems);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region RIGHT CLICK MENUS
|
||||
#region RIGHT CLICK MENUS
|
||||
|
||||
private async void FModel_MI_Directory_Extract_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
|
@ -593,6 +594,6 @@ namespace FModel
|
|||
Assets.Copy(FModel_AssetsList.SelectedItems, ECopy.FileNoExt);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ namespace FModel.PakReader.IO
|
|||
public bool IsEncrypted => ContainerFile.ContainerFlags.HasAnyFlags(EIoContainerFlags.Encrypted);
|
||||
|
||||
public Dictionary<string, FIoStoreEntry> Files;
|
||||
public Dictionary<ulong, string> Chunks;
|
||||
|
||||
public FIoDirectoryIndexResource _directoryIndex;
|
||||
private byte[] _directoryIndexBuffer;
|
||||
|
|
@ -107,7 +108,8 @@ namespace FModel.PakReader.IO
|
|||
var firstEntry = GetChildDirectory(FIoDirectoryIndexHandle.Root);
|
||||
|
||||
var tempFiles = new Dictionary<string, FIoStoreEntry>();
|
||||
ReadIndex("", firstEntry, tempFiles);
|
||||
Chunks = new Dictionary<ulong, string>();
|
||||
ReadIndex("", firstEntry, tempFiles, Chunks);
|
||||
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}");
|
||||
|
||||
|
|
@ -289,9 +291,8 @@ namespace FModel.PakReader.IO
|
|||
compressionStream.Read(outData, 0, outData.Length);
|
||||
}
|
||||
|
||||
private void ReadIndex(string directoryName, FIoDirectoryIndexHandle dir, IDictionary<string, FIoStoreEntry> outFiles)
|
||||
private void ReadIndex(string directoryName, FIoDirectoryIndexHandle dir, IDictionary<string, FIoStoreEntry> outFiles, Dictionary<ulong, string> outChunks)
|
||||
{
|
||||
|
||||
while (dir.IsValid())
|
||||
{
|
||||
var subDirectoryName = string.Concat(directoryName, GetDirectoryName(dir), "/");
|
||||
|
|
@ -302,11 +303,13 @@ namespace FModel.PakReader.IO
|
|||
var name = GetFileName(file);
|
||||
var path = string.Concat(subDirectoryName, name);
|
||||
var data = GetFileData(file);
|
||||
outFiles[path] = new FIoStoreEntry(this, data, path, CaseSensitive);
|
||||
var entry = new FIoStoreEntry(this, data, path, CaseSensitive);
|
||||
outChunks[entry.ChunkId.ChunkId] = path;
|
||||
outFiles[path] = entry;
|
||||
file = GetNextFile(file);
|
||||
}
|
||||
|
||||
ReadIndex(subDirectoryName, GetChildDirectory(dir), outFiles);
|
||||
ReadIndex(subDirectoryName, GetChildDirectory(dir), outFiles, outChunks);
|
||||
|
||||
dir = GetNextDirectory(dir);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace FModel.PakReader.IO
|
|||
return hash;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is FIoChunkId cast))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
namespace FModel.PakReader.IO
|
||||
using FModel.Utils;
|
||||
|
||||
namespace FModel.PakReader.IO
|
||||
{
|
||||
public class FIoStoreEntry : ReaderEntry
|
||||
{
|
||||
public readonly FFileIoStoreReader ioStore;
|
||||
public readonly uint UserData;
|
||||
|
||||
public override string ContainerName => ioStore.FileName;
|
||||
public override string Name { get; }
|
||||
public readonly uint UserData;
|
||||
public override long Size { get; }
|
||||
public override long UncompressedSize { get; }
|
||||
public override int StructSize { get; }
|
||||
public override uint CompressionMethodIndex { get; }
|
||||
public override bool Encrypted { get; }
|
||||
|
||||
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 string CompressionMethodString => ioStore.TocResource.CompressionMethods[CompressionMethodIndex - 1];
|
||||
|
||||
public FIoStoreEntry(FFileIoStoreReader ioStore, uint userData, string name, bool caseSensitive)
|
||||
{
|
||||
|
|
@ -19,8 +28,30 @@
|
|||
if (!caseSensitive)
|
||||
name = name.ToLowerInvariant();
|
||||
if (name.StartsWith('/'))
|
||||
name = name.Substring(1);
|
||||
name = name[1..];
|
||||
Name = name;
|
||||
|
||||
StructSize = 0;
|
||||
Size = 0;
|
||||
UncompressedSize = 0;
|
||||
|
||||
var compressionBlockSize = ioStore.TocResource.Header.CompressionBlockSize;
|
||||
var firstBlockIndex = (int)(Offset / compressionBlockSize);
|
||||
var lastBlockIndex = (int)((BinaryHelper.Align((long)Offset + Length, compressionBlockSize) - 1) / compressionBlockSize);
|
||||
for (int blockIndex = firstBlockIndex; blockIndex <= lastBlockIndex; blockIndex++)
|
||||
{
|
||||
var compressionBlock = ioStore.TocResource.CompressionBlocks[blockIndex];
|
||||
UncompressedSize += compressionBlock.UncompressedSize;
|
||||
CompressionMethodIndex = compressionBlock.CompressionMethodIndex;
|
||||
|
||||
var rawSize = BinaryHelper.Align(compressionBlock.CompressedSize, AESDecryptor.ALIGN);
|
||||
Size += rawSize;
|
||||
|
||||
if (ioStore.TocResource.Header.ContainerFlags.HasAnyFlags(EIoContainerFlags.Encrypted))
|
||||
{
|
||||
Encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetData() => ioStore.Read(ChunkId);
|
||||
|
|
|
|||
|
|
@ -108,10 +108,13 @@ namespace FModel.PakReader.Parsers
|
|||
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);
|
||||
if (realImportIndex > -1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,9 +125,16 @@ namespace FModel.PakReader.Parsers
|
|||
for (var i = 0; i < ExportMap.Length; i++)
|
||||
{
|
||||
var exportMapEntry = ExportMap[i];
|
||||
FName exportType;
|
||||
FPackageObjectIndex trigger;
|
||||
if (exportMapEntry.ClassIndex.IsExport)
|
||||
trigger = exportMapEntry.SuperIndex;
|
||||
else if (exportMapEntry.ClassIndex.IsImport)
|
||||
trigger = exportMapEntry.ClassIndex;
|
||||
else
|
||||
throw new FileLoadException("Can't get class name");
|
||||
|
||||
if (GlobalData != null && GlobalData.ScriptObjectByGlobalId.TryGetValue(exportMapEntry.ClassIndex, out var scriptObject))
|
||||
FName exportType;
|
||||
if (GlobalData != null && GlobalData.ScriptObjectByGlobalId.TryGetValue(trigger, out var scriptObject))
|
||||
{
|
||||
exportType = scriptObject.Name;
|
||||
}
|
||||
|
|
@ -150,10 +160,12 @@ namespace FModel.PakReader.Parsers
|
|||
//"AkMediaAssetData" => new UAkMediaAssetData(this, ubulk, ExportMap.Sum(e => e.SerialSize) + PackageFileSummary.TotalHeaderSize),
|
||||
_ => new UObject(this, properties, type: exportType.String),
|
||||
};
|
||||
_dataExportTypes[i] = exportType;
|
||||
_dataExportTypes[i] = exportType;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataExports[i] = null;
|
||||
_dataExportTypes[i] = exportType;
|
||||
#if DEBUG
|
||||
var header = new FUnversionedHeader(this);
|
||||
using var it = new FIterator(header);
|
||||
|
|
|
|||
|
|
@ -10,21 +10,25 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
const byte Flag_Encrypted = 0x01;
|
||||
const byte Flag_Deleted = 0x02;
|
||||
|
||||
public bool Encrypted => (Flags & Flag_Encrypted) != 0;
|
||||
public override bool Encrypted => (Flags & Flag_Encrypted) != 0;
|
||||
public bool Deleted => (Flags & Flag_Deleted) != 0;
|
||||
|
||||
public override string ContainerName { get; }
|
||||
public override string Name => _name;
|
||||
private readonly string _name;
|
||||
public readonly long Offset;
|
||||
public readonly long Size;
|
||||
public readonly long UncompressedSize;
|
||||
public override long Size => _size;
|
||||
private readonly long _size;
|
||||
public override long UncompressedSize => _uncompressedSize;
|
||||
private readonly long _uncompressedSize;
|
||||
public readonly FPakCompressedBlock[] CompressionBlocks;
|
||||
public readonly uint CompressionBlockSize;
|
||||
public readonly uint CompressionMethodIndex;
|
||||
public override uint CompressionMethodIndex => _compressionMethodIndex;
|
||||
private readonly uint _compressionMethodIndex;
|
||||
public readonly byte Flags;
|
||||
|
||||
public readonly int StructSize;
|
||||
public override int StructSize => _structSize;
|
||||
private readonly int _structSize;
|
||||
|
||||
internal FPakEntry(BinaryReader reader, EPakVersion Version, int SubVersion, bool caseSensitive, string pakName)
|
||||
{
|
||||
|
|
@ -39,26 +43,26 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
var StartOffset = reader.BaseStream.Position;
|
||||
|
||||
Offset = reader.ReadInt64();
|
||||
Size = reader.ReadInt64();
|
||||
UncompressedSize = reader.ReadInt64();
|
||||
_size = reader.ReadInt64();
|
||||
_uncompressedSize = reader.ReadInt64();
|
||||
if (Version < EPakVersion.FNAME_BASED_COMPRESSION_METHOD)
|
||||
{
|
||||
var LegacyCompressionMethod = reader.ReadInt32();
|
||||
if (LegacyCompressionMethod == (int)ECompressionFlags.COMPRESS_None)
|
||||
{
|
||||
CompressionMethodIndex = 0;
|
||||
_compressionMethodIndex = 0;
|
||||
}
|
||||
else if ((LegacyCompressionMethod & (int)ECompressionFlags.COMPRESS_ZLIB) != 0)
|
||||
{
|
||||
CompressionMethodIndex = 1;
|
||||
_compressionMethodIndex = 1;
|
||||
}
|
||||
else if ((LegacyCompressionMethod & (int)ECompressionFlags.COMPRESS_GZIP) != 0)
|
||||
{
|
||||
CompressionMethodIndex = 2;
|
||||
_compressionMethodIndex = 2;
|
||||
}
|
||||
else if ((LegacyCompressionMethod & (int)ECompressionFlags.COMPRESS_Custom) != 0)
|
||||
{
|
||||
CompressionMethodIndex = 3;
|
||||
_compressionMethodIndex = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -69,15 +73,15 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
else
|
||||
{
|
||||
if (Version == EPakVersion.FNAME_BASED_COMPRESSION_METHOD && SubVersion == 0)
|
||||
CompressionMethodIndex = reader.ReadByte();
|
||||
_compressionMethodIndex = reader.ReadByte();
|
||||
else
|
||||
CompressionMethodIndex = reader.ReadUInt32();
|
||||
_compressionMethodIndex = reader.ReadUInt32();
|
||||
}
|
||||
if (Version < EPakVersion.NO_TIMESTAMPS) reader.ReadInt64(); // Timestamp
|
||||
reader.ReadBytes(20); // Hash
|
||||
if (Version >= EPakVersion.COMPRESSION_ENCRYPTION)
|
||||
{
|
||||
if (CompressionMethodIndex != 0)
|
||||
if (_compressionMethodIndex != 0)
|
||||
{
|
||||
CompressionBlocks = reader.ReadTArray(() => new FPakCompressedBlock(reader));
|
||||
}
|
||||
|
|
@ -87,7 +91,7 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
}
|
||||
|
||||
// Used to seek ahead to the file data instead of parsing the entry again
|
||||
StructSize = (int)(reader.BaseStream.Position - StartOffset);
|
||||
_structSize = (int)(reader.BaseStream.Position - StartOffset);
|
||||
}
|
||||
|
||||
internal FPakEntry(BinaryReader reader, bool caseSensitive, string pakName)
|
||||
|
|
@ -102,11 +106,11 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
var StartOffset = reader.BaseStream.Position;
|
||||
|
||||
Offset = reader.ReadInt64();
|
||||
Size = reader.ReadInt64();
|
||||
UncompressedSize = reader.ReadInt64();
|
||||
CompressionMethodIndex = reader.ReadUInt32();
|
||||
_size = reader.ReadInt64();
|
||||
_uncompressedSize = reader.ReadInt64();
|
||||
_compressionMethodIndex = reader.ReadUInt32();
|
||||
reader.ReadBytes(20); // Hash
|
||||
if (CompressionMethodIndex != 0)
|
||||
if (_compressionMethodIndex != 0)
|
||||
{
|
||||
CompressionBlocks = reader.ReadTArray(() => new FPakCompressedBlock(reader));
|
||||
}
|
||||
|
|
@ -114,7 +118,7 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
CompressionBlockSize = reader.ReadUInt32();
|
||||
|
||||
// Used to seek ahead to the file data instead of parsing the entry again
|
||||
StructSize = (int)(reader.BaseStream.Position - StartOffset);
|
||||
_structSize = (int)(reader.BaseStream.Position - StartOffset);
|
||||
}
|
||||
|
||||
internal FPakEntry(string pakName, string name, long offset, long size, long uncompressedSize, FPakCompressedBlock[] compressionBlocks, uint compressionBlockSize, uint compressionMethodIndex, byte flags)
|
||||
|
|
@ -122,38 +126,38 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
ContainerName = pakName;
|
||||
_name = name;
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
UncompressedSize = uncompressedSize;
|
||||
_size = size;
|
||||
_uncompressedSize = uncompressedSize;
|
||||
CompressionBlocks = compressionBlocks;
|
||||
CompressionBlockSize = compressionBlockSize;
|
||||
CompressionMethodIndex = compressionMethodIndex;
|
||||
_compressionMethodIndex = compressionMethodIndex;
|
||||
Flags = flags;
|
||||
StructSize = (int)GetSize(EPakVersion.LATEST, compressionMethodIndex, compressionBlocks != null ? (uint)compressionBlocks.Length : 0);
|
||||
_structSize = (int)GetSize(EPakVersion.LATEST, compressionMethodIndex, compressionBlocks != null ? (uint)compressionBlocks.Length : 0);
|
||||
}
|
||||
|
||||
public ArraySegment<byte> GetData(Stream stream, byte[] key, string[] compressionMethods)
|
||||
{
|
||||
lock (stream)
|
||||
{
|
||||
if (CompressionMethodIndex == 0U)
|
||||
if (_compressionMethodIndex == 0U)
|
||||
{
|
||||
stream.Position = Offset + StructSize;
|
||||
stream.Position = Offset + _structSize;
|
||||
if (Encrypted)
|
||||
{
|
||||
var data = new byte[(Size & 15) == 0 ? Size : (Size / 16 + 1) * 16];
|
||||
var data = new byte[(_size & 15) == 0 ? _size : (_size / 16 + 1) * 16];
|
||||
stream.Read(data, 0, data.Length);
|
||||
return new ArraySegment<byte>(AESDecryptor.DecryptAES(data, key), 0, (int)UncompressedSize);
|
||||
return new ArraySegment<byte>(AESDecryptor.DecryptAES(data, key), 0, (int)_uncompressedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = new byte[UncompressedSize];
|
||||
var data = new byte[_uncompressedSize];
|
||||
stream.Read(data, 0, data.Length);
|
||||
return new ArraySegment<byte>(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = new byte[UncompressedSize];
|
||||
var data = new byte[_uncompressedSize];
|
||||
Decompress(stream, key, compressionMethods, data);
|
||||
return new ArraySegment<byte>(data);
|
||||
}
|
||||
|
|
@ -166,7 +170,7 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
if (compressionMethods == null || compressionMethods.Length == 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(compressionMethods), "CompressionMethods are null or empty");
|
||||
|
||||
string compressionMethod = compressionMethods[CompressionMethodIndex - 1]; // -1 because we dont have 'NAME_None' in the array
|
||||
string compressionMethod = compressionMethods[_compressionMethodIndex - 1]; // -1 because we dont have 'NAME_None' in the array
|
||||
int bytesRead = 0;
|
||||
for (int i = 0; i < CompressionBlocks.Length; i++)
|
||||
{
|
||||
|
|
@ -229,6 +233,6 @@ namespace FModel.PakReader.Parsers.Objects
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsCompressed() => UncompressedSize != Size || CompressionMethodIndex != (int)ECompressionFlags.COMPRESS_None;
|
||||
public bool IsCompressed() => _uncompressedSize != _size || _compressionMethodIndex != (int)ECompressionFlags.COMPRESS_None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,14 @@ namespace FModel.PakReader
|
|||
public abstract class ReaderEntry
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
|
||||
public abstract long UncompressedSize { get; }
|
||||
public abstract long Size { get; }
|
||||
public abstract int StructSize { get; }
|
||||
public abstract uint CompressionMethodIndex { get; }
|
||||
public abstract bool Encrypted { get; }
|
||||
public abstract string ContainerName { get; }
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsUE4Package() => Name[Name.LastIndexOf(".")..].Equals(".uasset");
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
|
|||
19
FModel/Properties/Resources.Designer.cs
generated
19
FModel/Properties/Resources.Designer.cs
generated
|
|
@ -567,10 +567,11 @@ namespace FModel.Properties {
|
|||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à • Waddlesworth • Maiky
|
||||
///• FunGames • Not Officer
|
||||
///• FunGames • Officer
|
||||
///• PsychoPast • TSG
|
||||
///• GMatrixGames • Jackson
|
||||
///• XTigerHyperX • FireMonkey.
|
||||
///• XTigerHyperX • FireMonkey
|
||||
///• Mang0e.
|
||||
/// </summary>
|
||||
public static string ContributorsFDetails {
|
||||
get {
|
||||
|
|
@ -771,11 +772,15 @@ namespace FModel.Properties {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recherche une chaîne localisée semblable à • Yanteh • Maiky
|
||||
///• FunGames • HYPEX ♥
|
||||
///• Alexander • Netu ♥
|
||||
///• SexyNutella • imatrix
|
||||
///• Frenzy Leaks • LlamaLeaks.
|
||||
/// Recherche une chaîne localisée semblable à • Maiky ♥ • HYPEX ♥
|
||||
///• VenomLeaks ♥ • JayKeyFN ♥
|
||||
///• Fevers ♥ • Netu ♥
|
||||
///• Quentin Bellus • Yanteh
|
||||
///• Shiina • SexyNutella
|
||||
///• Alexander • imatrix
|
||||
///• Frenzy Leaks • LlamaLeaks
|
||||
///• XTigerHyperX • FunGames
|
||||
///• WeLoveFortnite.
|
||||
/// </summary>
|
||||
public static string DonatorsFDetails {
|
||||
get {
|
||||
|
|
|
|||
|
|
@ -285,10 +285,11 @@ It's now the most used free software to leak on Fortnite.</value>
|
|||
</data>
|
||||
<data name="ContributorsFDetails" xml:space="preserve">
|
||||
<value>• Waddlesworth • Maiky
|
||||
• FunGames • Not Officer
|
||||
• FunGames • Officer
|
||||
• PsychoPast • TSG
|
||||
• GMatrixGames • Jackson
|
||||
• XTigerHyperX • FireMonkey</value>
|
||||
• XTigerHyperX • FireMonkey
|
||||
• Mang0e</value>
|
||||
<comment>Do not translate</comment>
|
||||
</data>
|
||||
<data name="CopySuccess" xml:space="preserve">
|
||||
|
|
@ -350,11 +351,15 @@ It's now the most used free software to leak on Fortnite.</value>
|
|||
<value>Donators</value>
|
||||
</data>
|
||||
<data name="DonatorsFDetails" xml:space="preserve">
|
||||
<value>• Yanteh • Maiky
|
||||
• FunGames • HYPEX ♥
|
||||
• Alexander • Netu ♥
|
||||
• SexyNutella • imatrix
|
||||
• Frenzy Leaks • LlamaLeaks</value>
|
||||
<value>• Maiky ♥ • HYPEX ♥
|
||||
• VenomLeaks ♥ • JayKeyFN ♥
|
||||
• Fevers ♥ • Netu ♥
|
||||
• Quentin Bellus • Yanteh
|
||||
• Shiina • SexyNutella
|
||||
• Alexander • imatrix
|
||||
• Frenzy Leaks • LlamaLeaks
|
||||
• XTigerHyperX • FunGames
|
||||
• WeLoveFortnite</value>
|
||||
<comment>Do not translate</comment>
|
||||
</data>
|
||||
<data name="DownloadError" xml:space="preserve">
|
||||
|
|
|
|||
|
|
@ -343,12 +343,12 @@ namespace FModel.Utils
|
|||
package = new PakPackage(uasset, uexp, ubulk);
|
||||
else
|
||||
package = new IoPackage(uasset, ubulk, ioStoreEntry);
|
||||
#if !DEBUG
|
||||
|
||||
_CachedFiles[entry] = new Dictionary<Package, ArraySegment<byte>[]>
|
||||
{
|
||||
[package] = new ArraySegment<byte>[] { uasset, uexp, ubulk }
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using System.Windows.Controls;
|
|||
using System.Windows.Input;
|
||||
using FModel.PakReader.Pak;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using FModel.PakReader.IO;
|
||||
|
||||
namespace FModel.ViewModels.MenuItem
|
||||
{
|
||||
|
|
@ -196,6 +197,60 @@ namespace FModel.ViewModels.MenuItem
|
|||
}
|
||||
}
|
||||
}
|
||||
FFileIoStoreReader globalReader = null;
|
||||
foreach (FFileIoStoreReader ioStore in MenuItems.pakFiles.GetIoStoreReaders())
|
||||
{
|
||||
if (ioStore.IsEncrypted && ioStore.AesKey == null)
|
||||
continue;
|
||||
|
||||
if (!Globals.CachedIoStores.ContainsKey(ioStore.FileName))
|
||||
{
|
||||
if (ioStore.FileName.Contains("global.ucas", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
globalReader = ioStore;
|
||||
continue;
|
||||
}
|
||||
if (!ioStore.ReadDirectoryIndex())
|
||||
continue;
|
||||
Globals.CachedIoStores[ioStore.FileName] = ioStore;
|
||||
StatusBarVm.statusBarViewModel.Set(string.Format(Properties.Resources.MountedPakTo, ioStore.FileName, ioStore.MountPoint), Properties.Resources.Loading);
|
||||
}
|
||||
|
||||
foreach (var (_, entry) in ioStore)
|
||||
{
|
||||
// uasset or umap or idk
|
||||
writer.Write(entry.Offset);
|
||||
writer.Write(entry.Length);
|
||||
writer.Write(entry.UncompressedSize);
|
||||
writer.Write(entry.Encrypted);
|
||||
writer.Write(entry.StructSize);
|
||||
writer.Write(ioStore.MountPoint + entry.Name);
|
||||
writer.Write(entry.CompressionMethodIndex);
|
||||
|
||||
// uexp
|
||||
if (entry.Uexp != null && entry.Uexp is FIoStoreEntry uexp)
|
||||
{
|
||||
writer.Write(uexp.Offset);
|
||||
writer.Write(uexp.Length);
|
||||
writer.Write(uexp.UncompressedSize);
|
||||
writer.Write(uexp.Encrypted);
|
||||
writer.Write(uexp.StructSize);
|
||||
writer.Write(ioStore.MountPoint + entry.Uexp.Name);
|
||||
writer.Write(uexp.CompressionMethodIndex);
|
||||
}
|
||||
// ubulk
|
||||
if (entry.Ubulk != null && entry.Ubulk is FIoStoreEntry ubulk)
|
||||
{
|
||||
writer.Write(ubulk.Offset);
|
||||
writer.Write(ubulk.Length);
|
||||
writer.Write(ubulk.UncompressedSize);
|
||||
writer.Write(ubulk.Encrypted);
|
||||
writer.Write(ubulk.StructSize);
|
||||
writer.Write(ioStore.MountPoint + entry.Ubulk.Name);
|
||||
writer.Write(ubulk.CompressionMethodIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).ContinueWith(t =>
|
||||
{
|
||||
if (t.Exception != null) Tasks.TaskCompleted(t.Exception);
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ using Microsoft.Win32;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
|
@ -25,6 +23,7 @@ using FModel.PakReader;
|
|||
using FModel.PakReader.IO;
|
||||
using FModel.PakReader.Pak;
|
||||
using FModel.PakReader.Parsers.Objects;
|
||||
using System.Linq;
|
||||
|
||||
namespace FModel.ViewModels.MenuItem
|
||||
{
|
||||
|
|
@ -308,9 +307,9 @@ namespace FModel.ViewModels.MenuItem
|
|||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
|
||||
private Dictionary<string, FPakEntry> GetOldFiles(EPakLoader mode)
|
||||
private Dictionary<string, ReaderEntry> GetOldFiles(EPakLoader mode)
|
||||
{
|
||||
var diff = new Dictionary<string, FPakEntry>();
|
||||
var diff = new Dictionary<string, ReaderEntry>();
|
||||
var ofd = new OpenFileDialog()
|
||||
{
|
||||
Title = Properties.Resources.SelectFile,
|
||||
|
|
@ -357,11 +356,16 @@ namespace FModel.ViewModels.MenuItem
|
|||
}
|
||||
}
|
||||
|
||||
var newFiles = new Dictionary<string, FPakEntry>();
|
||||
var newFiles = new Dictionary<string, ReaderEntry>();
|
||||
|
||||
foreach (var fileReader in Globals.CachedPakFiles)
|
||||
foreach (var files in fileReader.Value)
|
||||
newFiles[files.Key] = files.Value;
|
||||
|
||||
foreach (var fileReader in Globals.CachedIoStores)
|
||||
foreach (var files in fileReader.Value)
|
||||
newFiles[files.Key] = files.Value;
|
||||
|
||||
Paks.Merge(oldFilesTemp, out var oldFiles, string.Empty);
|
||||
|
||||
switch (mode)
|
||||
|
|
@ -369,7 +373,7 @@ namespace FModel.ViewModels.MenuItem
|
|||
case EPakLoader.New:
|
||||
foreach (var kvp in newFiles)
|
||||
{
|
||||
if (!oldFiles.TryGetValue(kvp.Key, out var entry))
|
||||
if (!oldFiles.TryGetValue(kvp.Key, out var _))
|
||||
diff.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
break;
|
||||
|
|
@ -377,7 +381,7 @@ namespace FModel.ViewModels.MenuItem
|
|||
foreach (var kvp in newFiles)
|
||||
{
|
||||
if (oldFiles.TryGetValue(kvp.Key, out var entry))
|
||||
if (entry.UncompressedSize != kvp.Value.UncompressedSize)
|
||||
if (entry.UncompressedSize != kvp.Value.UncompressedSize || entry.Encrypted != kvp.Value.Encrypted)
|
||||
diff.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
break;
|
||||
|
|
@ -386,7 +390,7 @@ namespace FModel.ViewModels.MenuItem
|
|||
{
|
||||
if (oldFiles.TryGetValue(kvp.Key, out var entry))
|
||||
{
|
||||
if (entry.UncompressedSize != kvp.Value.UncompressedSize)
|
||||
if (entry.UncompressedSize != kvp.Value.UncompressedSize || entry.Encrypted != kvp.Value.Encrypted)
|
||||
diff.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
else
|
||||
|
|
@ -395,21 +399,22 @@ namespace FModel.ViewModels.MenuItem
|
|||
break;
|
||||
}
|
||||
|
||||
string cosmeticsPath;
|
||||
if (Globals.Game.ActualGame == EGame.Fortnite)
|
||||
cosmeticsPath = $"/{Folders.GetGameName()}/Content/Athena/Items/Cosmetics/";
|
||||
else if (Globals.Game.ActualGame == EGame.Valorant)
|
||||
cosmeticsPath = $"/{Folders.GetGameName()}/Content/Labels/";
|
||||
else
|
||||
cosmeticsPath = "/gqdozqhndpioqgnq/"; // just corrupting it so it doesn't trigger all assets
|
||||
//string cosmeticsPath;
|
||||
//if (Globals.Game.ActualGame == EGame.Fortnite)
|
||||
// cosmeticsPath = $"/{Folders.GetGameName()}/Content/Athena/Items/Cosmetics/";
|
||||
//else if (Globals.Game.ActualGame == EGame.Valorant)
|
||||
// cosmeticsPath = $"/{Folders.GetGameName()}/Content/Labels/";
|
||||
//else
|
||||
// cosmeticsPath = "/gqdozqhndpioqgnq/"; // just corrupting it so it doesn't trigger all assets
|
||||
|
||||
var deleted = oldFiles.Where(kvp => !newFiles.TryGetValue(kvp.Key, out var _) && kvp.Key.StartsWith(cosmeticsPath)).ToDictionary(x => x.Key, x => x.Value);
|
||||
if (deleted.Count > 0)
|
||||
{
|
||||
FConsole.AppendText(Properties.Resources.RemovedRenamedCosmetics, FColors.Red, true);
|
||||
foreach (var kvp in deleted)
|
||||
FConsole.AppendText($" - {kvp.Value.Name.Substring(1)}", FColors.LightGray, true);
|
||||
}
|
||||
//// Remove this for 14.40
|
||||
//var deleted = oldFiles.Where(kvp => !newFiles.TryGetValue(kvp.Key, out var _) && kvp.Key.StartsWith(cosmeticsPath)).ToDictionary(x => x.Key, x => x.Value);
|
||||
//if (deleted.Count > 0)
|
||||
//{
|
||||
// FConsole.AppendText(Properties.Resources.RemovedRenamedCosmetics, FColors.Red, true);
|
||||
// foreach (var kvp in deleted)
|
||||
// FConsole.AppendText($" - {kvp.Value.Name.Substring(1)}", FColors.LightGray, true);
|
||||
//}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace FModel.ViewModels.TabControl
|
|||
offsets = string.Join(" ", "0x" + (ioEntry.Offset).ToString("X2"),
|
||||
entry.Uexp != null ? "0x" + (((FIoStoreEntry)ioEntry.Uexp).Offset).ToString("X2") : string.Empty,
|
||||
entry.Ubulk != null ? "0x" + (((FIoStoreEntry)ioEntry.Ubulk).Offset).ToString("X2") : string.Empty);
|
||||
tSize = Strings.GetReadableSize(ioEntry.Length + ((ioEntry.Uexp as FIoStoreEntry)?.Length ?? 0) + ((ioEntry.Ubulk as FIoStoreEntry)?.Length ?? 0));
|
||||
tSize = Strings.GetReadableSize(ioEntry.Size + ((ioEntry.Uexp as FIoStoreEntry)?.Size ?? 0) + ((ioEntry.Ubulk as FIoStoreEntry)?.Size ?? 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -40,15 +40,15 @@ namespace FModel.ViewModels.TabControl
|
|||
vm.IncludedExtensions = ext.TrimEnd();
|
||||
vm.Offsets = offsets.TrimEnd();
|
||||
vm.TotalSize = tSize;
|
||||
if (entry is FPakEntry cast)
|
||||
if (entry is FPakEntry c1)
|
||||
{
|
||||
vm.IsEncrypted = cast.Encrypted ? Properties.Resources.Yes : Properties.Resources.No;
|
||||
vm.CompMethod = ((ECompressionFlags)cast.CompressionMethodIndex).ToString();
|
||||
vm.IsEncrypted = c1.Encrypted ? Properties.Resources.Yes : Properties.Resources.No;
|
||||
vm.CompMethod = ((ECompressionFlags)c1.CompressionMethodIndex).ToString();
|
||||
}
|
||||
else
|
||||
else if (entry is FIoStoreEntry c2)
|
||||
{
|
||||
vm.IsEncrypted = Properties.Resources.No;
|
||||
vm.CompMethod = ECompressionFlags.COMPRESS_None.ToString();
|
||||
vm.IsEncrypted = c2.Encrypted ? Properties.Resources.Yes : Properties.Resources.No;
|
||||
vm.CompMethod = c2.CompressionMethodString;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user