Beispiel #1
0
                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));
                        }
                    }
                }
Beispiel #2
0
                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;
                    }
                }
Beispiel #3
0
                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);
                }
Beispiel #4
0
                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);
                }