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);
        }
Beispiel #2
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #6
0
        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();
        }
Beispiel #7
0
        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);
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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);
        }