private static Hed.Entry AddFile(string inputFolder, string filename, FileStream hedStream, FileStream pkgStream, bool shouldCompressData = false, bool shouldEncryptData = false) { var completeFilePath = Path.Combine(inputFolder, ORIGINAL_FILES_FOLDER_NAME, filename); var offset = pkgStream.Position; #region Data using var newFileStream = File.OpenRead(completeFilePath); var header = new EgsHdAsset.Header() { // CompressedLenght => -2: no compression and encryption, -1: no compression CompressedLength = !shouldCompressData ? !shouldEncryptData ? -2 : -1 : 0, DecompressedLength = (int)newFileStream.Length, RemasteredAssetCount = 0, CreationDate = -1 }; var decompressedData = newFileStream.ReadAllBytes(); var compressedData = decompressedData.ToArray(); if (shouldCompressData) { compressedData = Helpers.CompressData(decompressedData); header.CompressedLength = compressedData.Length; } // Encrypt and write current file data in the PKG stream // The seed used for encryption is the original data header var seed = new MemoryStream(); BinaryMapping.WriteObject <EgsHdAsset.Header>(seed, header); var encryptionSeed = seed.ReadAllBytes(); var encryptedData = header.CompressedLength > -2 ? EgsEncryption.Encrypt(compressedData, encryptionSeed) : compressedData; // Write original file header BinaryMapping.WriteObject <EgsHdAsset.Header>(pkgStream, header); // Make sure to write the original file after remastered assets headers pkgStream.Write(encryptedData); #endregion // Write a new entry in the HED stream var hedHeader = new Hed.Entry() { MD5 = Helpers.ToBytes(Helpers.CreateMD5(filename)), ActualLength = (int)newFileStream.Length, DataLength = (int)(pkgStream.Position - offset), Offset = offset }; BinaryMapping.WriteObject <Hed.Entry>(hedStream, hedHeader); return(hedHeader); }
private static List <EgsHdAsset.RemasteredEntry> ReplaceRemasteredAssets(string inputFolder, string originalFile, EgsHdAsset asset, FileStream pkgStream, byte[] seed, byte[] originalAssetData) { var newRemasteredHeaders = new List <EgsHdAsset.RemasteredEntry>(); var relativePath = Helpers.GetRelativePath(originalFile, Path.Combine(inputFolder, ORIGINAL_FILES_FOLDER_NAME)); var remasteredAssetsFolder = Path.Combine(inputFolder, REMASTERED_FILES_FOLDER_NAME, relativePath); var allRemasteredAssetsData = new MemoryStream(); // 0x30 is the size of this header var totalRemasteredAssetHeadersSize = asset.RemasteredAssetHeaders.Count() * 0x30; // This offset is relative to the original asset data var offset = totalRemasteredAssetHeadersSize + 0x10 + asset.OriginalAssetHeader.DecompressedLength; if (offset != asset.RemasteredAssetHeaders.Values.First().Offset) { throw new Exception("Something is wrong here!"); } foreach (var remasteredAssetHeader in asset.RemasteredAssetHeaders.Values) { var filename = remasteredAssetHeader.Name; var assetFilePath = Path.Combine(remasteredAssetsFolder, filename); // Use base remastered asset data var assetData = asset.RemasteredAssetsCompressedData[filename]; var decompressedLength = remasteredAssetHeader.DecompressedLength; if (File.Exists(assetFilePath)) { Console.WriteLine($"Replacing remastered file: {relativePath}/{filename}"); assetData = File.ReadAllBytes(assetFilePath); decompressedLength = assetData.Length; assetData = remasteredAssetHeader.CompressedLength > -1 ? Helpers.CompressData(assetData) : assetData; assetData = remasteredAssetHeader.CompressedLength > -2 ? EgsEncryption.Encrypt(assetData, seed) : assetData; } else { Console.WriteLine($"Keeping remastered file: {relativePath}/{filename}"); // The original file have been replaced, we need to encrypt all remastered asset with the new key if (!seed.SequenceEqual(asset.Seed)) { assetData = asset.RemasteredAssetsDecompressedData[filename]; assetData = remasteredAssetHeader.CompressedLength > -1 ? Helpers.CompressData(assetData) : assetData; assetData = remasteredAssetHeader.CompressedLength > -2 ? EgsEncryption.Encrypt(assetData, seed) : assetData; } } var compressedLength = remasteredAssetHeader.CompressedLength > -1 ? assetData.Length : remasteredAssetHeader.CompressedLength; var newRemasteredAssetHeader = new EgsHdAsset.RemasteredEntry() { CompressedLength = compressedLength, DecompressedLength = decompressedLength, Name = filename, Offset = offset, Unknown24 = remasteredAssetHeader.Unknown24 }; newRemasteredHeaders.Add(newRemasteredAssetHeader); // Write asset header in the PKG stream BinaryMapping.WriteObject <EgsHdAsset.RemasteredEntry>(pkgStream, newRemasteredAssetHeader); // 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(assetData); // Make sure to align remastered asset data on 16 bytes if (assetData.Length % 0x10 != 0) { allRemasteredAssetsData.Write(Enumerable.Repeat((byte)0xCD, 16 - (assetData.Length % 0x10)).ToArray()); } offset += decompressedLength; } pkgStream.Write(originalAssetData); pkgStream.Write(allRemasteredAssetsData.ReadAllBytes()); return(newRemasteredHeaders); }
private static Hed.Entry ReplaceFile( string inputFolder, string filename, FileStream hedStream, FileStream pkgStream, EgsHdAsset asset, Hed.Entry originalHedHeader = null) { var completeFilePath = Path.Combine(inputFolder, ORIGINAL_FILES_FOLDER_NAME, filename); var offset = pkgStream.Position; var originalHeader = asset.OriginalAssetHeader; // Clone the original asset header var header = new EgsHdAsset.Header() { CompressedLength = originalHeader.CompressedLength, DecompressedLength = originalHeader.DecompressedLength, RemasteredAssetCount = originalHeader.RemasteredAssetCount, CreationDate = originalHeader.CreationDate }; // Use the base original asset data by default var decompressedData = asset.OriginalData; var encryptedData = asset.OriginalRawData; var encryptionSeed = asset.Seed; // We want to replace the original file if (File.Exists(completeFilePath)) { Console.WriteLine($"Replacing original: {filename}!"); using var newFileStream = File.OpenRead(completeFilePath); decompressedData = newFileStream.ReadAllBytes(); var compressedData = decompressedData.ToArray(); var compressedDataLenght = originalHeader.CompressedLength; // CompressedLenght => -2: no compression and encryption, -1: no compression if (originalHeader.CompressedLength > -1) { compressedData = Helpers.CompressData(decompressedData); compressedDataLenght = compressedData.Length; } header.CompressedLength = compressedDataLenght; header.DecompressedLength = decompressedData.Length; // Encrypt and write current file data in the PKG stream // The seed used for encryption is the original data header var seed = new MemoryStream(); BinaryMapping.WriteObject <EgsHdAsset.Header>(seed, header); encryptionSeed = seed.ReadAllBytes(); encryptedData = header.CompressedLength > -2 ? EgsEncryption.Encrypt(compressedData, encryptionSeed) : compressedData; } // Write original file header BinaryMapping.WriteObject <EgsHdAsset.Header>(pkgStream, header); var remasteredHeaders = new List <EgsHdAsset.RemasteredEntry>(); // Is there remastered assets? if (header.RemasteredAssetCount > 0) { remasteredHeaders = ReplaceRemasteredAssets(inputFolder, filename, asset, pkgStream, encryptionSeed, encryptedData); } else { // Make sure to write the original file after remastered assets headers pkgStream.Write(encryptedData); } // Write a new entry in the HED stream var hedHeader = new Hed.Entry() { MD5 = Helpers.ToBytes(Helpers.CreateMD5(filename)), ActualLength = decompressedData.Length, DataLength = (int)(pkgStream.Position - offset), Offset = offset }; // For unknown reason, some files have a data length of 0 if (originalHedHeader.DataLength == 0) { Console.WriteLine($"{filename} => {originalHedHeader.ActualLength} ({originalHedHeader.DataLength})"); hedHeader.ActualLength = originalHedHeader.ActualLength; hedHeader.DataLength = originalHedHeader.DataLength; } BinaryMapping.WriteObject <Hed.Entry>(hedStream, hedHeader); return(hedHeader); }