private void Extract(string inputHed, string output) { var outputDir = output ?? Path.GetFileNameWithoutExtension(inputHed); using var hedStream = File.OpenRead(inputHed); using var img = File.OpenRead(Path.ChangeExtension(inputHed, "pkg")); foreach (var entry in Hed.Read(hedStream)) { var hash = EpicGamesAssets.ToString(entry.MD5); if (!Names.TryGetValue(hash, out var fileName)) { fileName = $"{hash}.dat"; } var outputFileName = Path.Combine(outputDir, ORIGINAL_FILES_FOLDER_NAME, fileName); if (DoNotExtractAgain && File.Exists(outputFileName)) { continue; } Console.WriteLine(outputFileName); CreateDirectoryForFile(outputFileName); //File.Create(outputFileName).Using(stream => stream.Write(img.SetPosition(entry.Offset).ReadBytes(entry.DataLength))); var hdAsset = new EgsHdAsset(img.SetPosition(entry.Offset)); File.Create(outputFileName).Using(stream => stream.Write(hdAsset.ReadData())); outputFileName = Path.Combine(outputDir, REMASTERED_FILES_FOLDER_NAME, fileName); foreach (var asset in hdAsset.Assets) { var outputFileNameRemastered = Path.Combine(GetHDAssetFolder(outputFileName), asset); Console.WriteLine(outputFileNameRemastered); CreateDirectoryForFile(outputFileNameRemastered); var assetData = hdAsset.ReadRemasteredAsset(asset); File.Create(outputFileNameRemastered).Using(stream => stream.Write(assetData)); } } }
private void Patch(string pkgFile, string inputFolder, string outputFolder) { var outputDir = outputFolder ?? Path.GetFileNameWithoutExtension(pkgFile); // Get files to inject in the PKG var hedFile = Path.ChangeExtension(pkgFile, "hed"); using var hedStream = File.OpenRead(hedFile); using var pkgStream = File.OpenRead(pkgFile); if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } using var patchedHedStream = File.Create(Path.Combine(outputDir, Path.GetFileName(hedFile))); using var patchedPkgStream = File.Create(Path.Combine(outputDir, Path.GetFileName(pkgFile))); var pkgOffset = 0L; pkgStream.SetPosition(0); foreach (var entry in Hed.Read(hedStream)) { var hash = EpicGamesAssets.ToString(entry.MD5); // We don't know this filename, we ignore it if (!Names.TryGetValue(hash, out var filename)) { Console.WriteLine($"No name for hash: {hash}!"); continue; } // Replace the found files var asset = new EgsHdAsset(pkgStream.SetPosition(entry.Offset)); var fileToInject = Path.Combine(inputFolder, filename); var newHedEntry = ReplaceFile(fileToInject, inputFolder, patchedHedStream, patchedPkgStream, asset, entry); pkgOffset += newHedEntry.DataLength; } }
private List <EgsHdAsset.RemasteredEntry> ReplaceRemasteredAssets(string originalFile, EgsHdAsset asset, FileStream pkgStream, byte[] seed, byte[] originalAssetData, byte[] originalUncompressedData) { var newRemasteredHeaders = new List <EgsHdAsset.RemasteredEntry>(); var relativePath = GetRelativePath(originalFile, Path.Combine(InputFolder, ORIGINAL_FILES_FOLDER_NAME)); var baseRemasteredFolder = Path.Combine(InputFolder, REMASTERED_FILES_FOLDER_NAME, relativePath); var remasteredAssetsFolder = GetHDAssetFolder(baseRemasteredFolder); var remasteredAssetFiles = asset.Assets; var allRemasteredAssetsData = new MemoryStream(); // 0x30 is the size of this header var totalRemasteredAssetHeadersSize = (remasteredAssetFiles.Count() * 0x30); var offsetPosition = originalUncompressedData.Length; foreach (var remasteredAssetFile in remasteredAssetFiles) { var assetFilePath = Path.Combine(remasteredAssetsFolder, remasteredAssetFile); var uncompressedData = new byte[] {}; var compressedData = new byte[] {}; var encryptedData = new byte[] {}; if (File.Exists(assetFilePath)) { Console.WriteLine($"Replacing remastered file: {relativePath}/{remasteredAssetFile}"); uncompressedData = File.ReadAllBytes(assetFilePath); compressedData = asset.RemasteredAssetHeaders[remasteredAssetFile].CompressedLength > -1 ? CompressData(uncompressedData) : uncompressedData; Console.WriteLine(asset.RemasteredAssetHeaders[remasteredAssetFile].CompressedLength); //Read the remastered file regardless, to set the correct offset encryptedData = asset.ReadRawRemasteredAsset(remasteredAssetFile); encryptedData = asset.RemasteredAssetHeaders[remasteredAssetFile].CompressedLength > -2 ? EgsEncryption.Encrypt(compressedData, seed) : compressedData; } else { Console.WriteLine($"Keeping remastered file: {relativePath}/{remasteredAssetFile}"); encryptedData = asset.ReadRawRemasteredAsset(remasteredAssetFile); uncompressedData = new byte[asset.RemasteredAssetHeaders[remasteredAssetFile].DecompressedLength]; } var currentOffset = totalRemasteredAssetHeadersSize + offsetPosition + 0x10; var entryCompressedLength = encryptedData.Length; if (asset.RemasteredAssetHeaders[remasteredAssetFile].CompressedLength == -1) { entryCompressedLength = (int)-1; // -1 is uncompressed } if (asset.RemasteredAssetHeaders[remasteredAssetFile].CompressedLength == -2) { entryCompressedLength = (int)-2; // -2 is uncompressed and unencrypted } var remasteredEntry = new EgsHdAsset.RemasteredEntry() { CompressedLength = entryCompressedLength, DecompressedLength = (int)uncompressedData.Length, Name = remasteredAssetFile, Offset = currentOffset, Unknown24 = asset.RemasteredAssetHeaders[remasteredAssetFile].Unknown24 }; newRemasteredHeaders.Add(remasteredEntry); // Write asset header in the PKG stream BinaryMapping.WriteObject <EgsHdAsset.RemasteredEntry>(pkgStream, remasteredEntry); // Don't write into the PKG stream yet as we need to write // all HD assets header juste after original file's data allRemasteredAssetsData.Write(encryptedData); if (encryptedData.Length % 0x10 != 0) { allRemasteredAssetsData.Write(Enumerable.Repeat((byte)0xCD, 16 - (encryptedData.Length % 0x10)).ToArray()); } offsetPosition += uncompressedData.Length; } pkgStream.Write(originalAssetData); pkgStream.Write(allRemasteredAssetsData.ReadAllBytes()); return(newRemasteredHeaders); }
private Hed.Entry ReplaceFile(string filename, string inputFolder, FileStream hedStream, FileStream pkgStream, EgsHdAsset asset, Hed.Entry originalHedHeader = null) { var filesToReplace = GetAllFiles(inputFolder); var originalfilename = GetRelativePath(filename, Path.Combine(InputFolder, ORIGINAL_FILES_FOLDER_NAME)); byte[] data = new byte[asset.OriginalAssetHeader.DecompressedLength]; byte[] compressedData = new byte[] {}; byte[] encryptedData = new byte[] {}; var header = CreateAssetHeader( data, asset.OriginalAssetHeader.CompressedLength, asset.OriginalAssetHeader.RemasteredAssetCount, asset.OriginalAssetHeader.Unknown0c ); var encryptionKey = asset.Key; if (filesToReplace.Contains(originalfilename)) { Console.WriteLine($"Replacing original: {originalfilename}!"); Console.WriteLine(header.CompressedLength); // Read Raw data to set the correct offset even if we're reading from an external file data = asset.ReadRawData(); header.DecompressedLength = data.Length; data = File.ReadAllBytes(filename); header.DecompressedLength = data.Length; compressedData = header.CompressedLength > -1 ? CompressData(data) : data; if (header.CompressedLength > -1) { header.CompressedLength = compressedData.Length; } Console.WriteLine(header.CompressedLength); if (header.CompressedLength > -2) { var seed = new MemoryStream(); // The seed used for encryption is the data header6 BinaryMapping.WriteObject <EgsHdAsset.Header>(seed, header); encryptionKey = seed.ReadAllBytes(); // Encrypt file encryptedData = header.CompressedLength > 2 ? EgsEncryption.Encrypt(compressedData, encryptionKey) : compressedData; } } else { Console.WriteLine($"Keeping original: {originalfilename}!"); //data = asset.ReadRawData(); compressedData = asset.ReadRawData(); encryptedData = compressedData; } var offset = pkgStream.Position; BinaryMapping.WriteObject <EgsHdAsset.Header>(pkgStream, header); var remasteredHeaders = new List <EgsHdAsset.RemasteredEntry>(); // Is there remastered assets? if (header.RemasteredAssetCount > 0) { remasteredHeaders = ReplaceRemasteredAssets(originalfilename, asset, pkgStream, encryptionKey, encryptedData, data); // If a remastered asset is not replaced, we still want to count its size for the HED entry foreach (var remasteredAssetName in asset.RemasteredAssetHeaders.Keys) { if (!remasteredHeaders.Exists(header => header.Name == remasteredAssetName)) { remasteredHeaders.Add(asset.RemasteredAssetHeaders[remasteredAssetName]); } } } else { // Make sure to write the original file after remastered assets headers pkgStream.Write(encryptedData); } // Write a new entry in the HED stream var hedEntry = CreateHedEntry(originalfilename, data, (int)(pkgStream.Position - offset), offset, remasteredHeaders); BinaryMapping.WriteObject <Hed.Entry>(hedStream, hedEntry); return(hedEntry); }