public EgsHdAsset(Stream stream) { _stream = stream; _baseOffset = stream.Position; _seed = stream.ReadBytes(0x10); _key = EgsEncryption.GenerateKey(_seed, PASS_COUNT); _header = BinaryMapping.ReadObject <Header>(new MemoryStream(_seed)); var entries = Enumerable .Range(0, _header.RemasteredAssetCount) .Select(_ => BinaryMapping.ReadObject <RemasteredEntry>(stream)) .ToList(); _entries = entries.ToDictionary(x => x.Name, x => x); _dataOffset = stream.Position; Assets = entries.Select(x => x.Name).ToArray(); ReadData(); foreach (var remasteredAssetName in Assets) { ReadRemasteredAsset(remasteredAssetName); } stream.SetPosition(_dataOffset); }
public byte[] DecompressRemasteredData(byte[] data, RemasteredEntry entry) { var paddedData = data; if (data.Length % 16 != 0) { paddedData = new byte[data.Length + 16 - (data.Length % 16)]; } Array.Copy(data, 0, paddedData, 0, data.Length); if (entry.CompressedLength >= -1) { for (var i = 0; i < Math.Min(paddedData.Length, 0x100); i += 0x10) { EgsEncryption.DecryptChunk(_key, paddedData, i, PassCount); } } if (entry.CompressedLength >= 0) { using var compressedStream = new MemoryStream(paddedData); using var deflate = new DeflateStream(compressedStream.SetPosition(2), CompressionMode.Decompress); var decompressedData = new byte[entry.DecompressedLength]; deflate.Read(decompressedData, 0, decompressedData.Length); return(decompressedData); } return(paddedData); }
private byte[] ReadData() { var dataLength = _header.CompressedLength >= 0 ? _header.CompressedLength : _header.DecompressedLength; var data = _stream.SetPosition(_dataOffset).ReadBytes(dataLength); _originalRawData = data.ToArray(); if (_header.CompressedLength > -2) { for (var i = 0; i < Math.Min(dataLength, 0x100); i += 0x10) { EgsEncryption.DecryptChunk(_key, data, i, PASS_COUNT); } } if (_header.CompressedLength > -1) { using var compressedStream = new MemoryStream(data); using var deflate = new DeflateStream(compressedStream.SetPosition(2), CompressionMode.Decompress); var decompressedData = new byte[_header.DecompressedLength]; deflate.Read(decompressedData, 0, decompressedData.Length); data = decompressedData; } _originalData = data.ToArray(); return(data); }
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); }
public static byte[] Encrypt(byte[] data, byte[] seed) { const int PassCount = 10; var key = EgsEncryption.GenerateKey(seed, PassCount); for (var i = 0; i < Math.Min(data.Length, 0x100); i += 0x10) { EgsEncryption.EncryptChunk(key, data, i, PassCount); } return(data); }
public EgsHdAsset(Stream stream) { _stream = stream; _baseOffset = stream.Position; var seed = stream.ReadBytes(0x10); _key = EgsEncryption.GenerateKey(seed, PassCount); Key = _key; _header = BinaryMapping.ReadObject <Header>(new MemoryStream(seed)); var entries = Enumerable .Range(0, _header.RemasteredAssetCount) .Select(_ => BinaryMapping.ReadObject <RemasteredEntry>(stream)) .ToList(); _entries = entries.ToDictionary(x => x.Name, x => x); _dataOffset = stream.Position; Assets = entries.Select(x => x.Name).ToArray(); }
public byte[] DecompressData(byte[] data) { if (_header.CompressedLength >= -1) { for (var i = 0; i < Math.Min(data.Length, 0x100); i += 0x10) { EgsEncryption.DecryptChunk(_key, data, i, PassCount); } } if (_header.CompressedLength >= 0) { using var compressedStream = new MemoryStream(data); using var deflate = new DeflateStream(compressedStream.SetPosition(2), CompressionMode.Decompress); var decompressedData = new byte[_header.DecompressedLength]; deflate.Read(decompressedData, 0, decompressedData.Length); return(decompressedData); } return(data); }
private byte[] ReadRemasteredAsset(string assetName) { var header = _entries[assetName]; var dataLength = header.CompressedLength >= 0 ? header.CompressedLength : header.DecompressedLength; if (dataLength % 16 != 0) { dataLength += 16 - (dataLength % 16); } var data = _stream.AlignPosition(0x10).ReadBytes(dataLength); _remasteredAssetsRawData.Add(assetName, data.ToArray()); if (header.CompressedLength > -2) { for (var i = 0; i < Math.Min(dataLength, 0x100); i += 0x10) { EgsEncryption.DecryptChunk(_key, data, i, PASS_COUNT); } } if (header.CompressedLength > -1) { using var compressedStream = new MemoryStream(data); using var deflate = new DeflateStream(compressedStream.SetPosition(2), CompressionMode.Decompress); var decompressedData = new byte[header.DecompressedLength]; deflate.Read(decompressedData, 0, decompressedData.Length); data = decompressedData; } _remasteredAssetsData.Add(assetName, data.ToArray()); return(data); }
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); }