public void Parse(PatchCreatorOptions op) { var ark = ArkFile.FromFile(op.InputPath); var inplaceEdit = string.IsNullOrWhiteSpace(op.OutputPath); var platformExt = GuessPlatform(op.InputPath); if (!inplaceEdit) { if ((int)ark.Version <= 3) { // If ark version doesn't support multiple parts then copy entire ark to new directory ark = ark.CopyToDirectory(Path.Combine(op.OutputPath, "gen")); inplaceEdit = true; } else { // Add additional ark park var patchPartName = $"{Path.GetFileNameWithoutExtension(op.InputPath)}_{ark.PartCount()}.ark"; var fullPartPath = ((int)ark.Version < 9) ? Path.Combine(op.OutputPath, "gen", patchPartName) : Path.Combine(op.OutputPath, patchPartName); ark.AddAdditionalPart(fullPartPath); } } var files = Directory.GetFiles(op.ArkFilesPath, "*", SearchOption.AllDirectories); // Open hashes var entryInfo = (string.IsNullOrWhiteSpace(op.HashesPath) ? new List <ArkEntryInfo>() : ArkEntryInfo.ReadFromCSV(op.HashesPath)) .ToDictionary(x => x.Path, y => y); var updatedHashes = new List <ArkEntryInfo>(); var dtaRegex = new Regex("(?i).dta$"); var genPathedFile = new Regex(@"(?i)gen[\/][^\/]+$"); var dotRegex = new Regex(@"\([.]+\)/"); var forgeScriptRegex = new Regex("(?i).((dta)|(fusion)|(moggsong)|(script))$"); // Create temp path var tempDir = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, true); } foreach (var file in files) { var internalPath = FileHelper.GetRelativePath(file, op.ArkFilesPath) .Replace("\\", "/"); string inputFilePath = file; if ((int)ark.Version < 7 && dtaRegex.IsMatch(internalPath)) { // Updates path internalPath = $"{internalPath.Substring(0, internalPath.Length - 1)}b"; if (!genPathedFile.IsMatch(internalPath)) { internalPath = internalPath.Insert(internalPath.LastIndexOf('/'), "/gen"); } // Creates temp dtb file inputFilePath = ScriptHelper.ConvertDtaToDtb(file, tempDir, ark.Encrypted, (int)ark.Version); } else if ((int)ark.Version >= 7 && forgeScriptRegex.IsMatch(internalPath)) { // Updates path internalPath = $"{internalPath}_dta_{platformExt}"; // Creates temp dtb file inputFilePath = ScriptHelper.ConvertDtaToDtb(file, tempDir, ark.Encrypted, (int)ark.Version); } if (dotRegex.IsMatch(internalPath)) { internalPath = dotRegex.Replace(internalPath, x => $"{x.Value.Substring(1, x.Length - 3)}/"); } var fileName = Path.GetFileName(internalPath); var dirPath = Path.GetDirectoryName(internalPath).Replace("\\", "/"); var pendingEntry = new PendingArkEntry(fileName, dirPath) { LocalFilePath = inputFilePath }; ark.AddPendingEntry(pendingEntry); Console.WriteLine($"Added {pendingEntry.FullPath}"); if (!entryInfo.TryGetValue(internalPath, out var hashInfo)) { continue; } // Update hash using var fs = File.OpenRead(inputFilePath); hashInfo.Hash = Crypt.SHA1Hash(fs); updatedHashes.Add(hashInfo); } ark.CommitChanges(inplaceEdit); // Clean up temp files if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, true); } if (!inplaceEdit) { // Writes header var hdrPath = ((int)ark.Version < 9) ? Path.Combine(op.OutputPath, "gen", Path.GetFileName(op.InputPath)) : Path.Combine(op.OutputPath, Path.GetFileName(op.InputPath)); ark.WriteHeader(hdrPath); } else { // TODO: Also look at possibly still patching exe return; } // Copy exe var exePath = Path.Combine(op.OutputPath, Path.GetFileName(op.ExePath)); File.Copy(op.ExePath, exePath, true); if (updatedHashes.Count <= 0) { return; } // Patch exe using var exeStream = File.OpenWrite(exePath); foreach (var hashInfo in updatedHashes) { var hashBytes = FileHelper.GetBytes(hashInfo.Hash); exeStream.Seek(hashInfo.Offset, SeekOrigin.Begin); exeStream.Write(hashBytes, 0, hashBytes.Length); Console.WriteLine($"Updated hash for {hashInfo.Path}"); } }
public void Parse(Dir2ArkOptions op) { var dtaRegex = new Regex("(?i).dta$"); var genPathedFile = new Regex(@"(?i)gen[\/][^\/]+$"); var dotRegex = new Regex(@"\([.]+\)/"); var forgeScriptRegex = new Regex("(?i).((dta)|(fusion)|(moggsong)|(script))$"); var arkPartSizeLimit = (op.PartSizeLimit > 0) ? op.PartSizeLimit : uint.MaxValue; var arkDir = Path.GetFullPath(op.OutputPath); // Set encrypted data if (op.Encrypt && !op.EncryptKey.HasValue) { op.EncryptKey = 0x5A_4C_4F_4C; } else if (op.EncryptKey.HasValue) { // Don't encrypt unless explicitly stated op.EncryptKey = default; } // Create directory if it doesn't exist if (!Directory.Exists(arkDir)) { Directory.CreateDirectory(arkDir); } // Create ark var hdrPath = Path.Combine(arkDir, $"{op.ArkName}.hdr"); var ark = ArkFile.Create(hdrPath, (ArkVersion)op.ArkVersion, (int?)op.EncryptKey); var files = Directory.GetFiles(op.InputPath, "*", SearchOption.AllDirectories); // Create temp path and guess platform var tempDir = CreateTemporaryDirectory(); var platformExt = GuessPlatform(op.InputPath); var currentPartSize = 0u; foreach (var file in files) { var internalPath = FileHelper.GetRelativePath(file, op.InputPath) .Replace("\\", "/"); string inputFilePath = file; if ((int)ark.Version < 7 && dtaRegex.IsMatch(internalPath)) { // Updates path internalPath = $"{internalPath.Substring(0, internalPath.Length - 1)}b"; if (!genPathedFile.IsMatch(internalPath)) { internalPath = internalPath.Insert(internalPath.LastIndexOf('/'), "/gen"); } // Creates temp dtb file inputFilePath = ScriptHelper.ConvertDtaToDtb(file, tempDir, ark.Encrypted, (int)ark.Version); } else if ((int)ark.Version >= 7 && forgeScriptRegex.IsMatch(internalPath)) { // Updates path internalPath = $"{internalPath}_dta_{platformExt}"; // Creates temp dtb file inputFilePath = ScriptHelper.ConvertDtaToDtb(file, tempDir, ark.Encrypted, (int)ark.Version); } if (dotRegex.IsMatch(internalPath)) { internalPath = dotRegex.Replace(internalPath, x => $"{x.Value.Substring(1, x.Length - 3)}/"); } // Check part limit var fileSizeLong = new FileInfo(inputFilePath).Length; var fileSize = (uint)fileSizeLong; var potentialPartSize = currentPartSize + fileSize; if (fileSizeLong > (long)uint.MaxValue) { throw new NotSupportedException($"File size above 4GB is unsupported for \"{file}\""); } else if (potentialPartSize >= arkPartSizeLimit) { // Kind of hacky but multiple part writing isn't implemented in commit changes yet ark.CommitChanges(true); ark.AddAdditionalPart(); currentPartSize = 0; } var fileName = Path.GetFileName(internalPath); var dirPath = Path.GetDirectoryName(internalPath).Replace("\\", "/"); var pendingEntry = new PendingArkEntry(fileName, dirPath) { LocalFilePath = inputFilePath }; ark.AddPendingEntry(pendingEntry); Console.WriteLine($"Added {pendingEntry.FullPath}"); currentPartSize += fileSize; } ark.CommitChanges(true); Console.WriteLine($"Wrote hdr to \"{hdrPath}\""); }