/// <summary> /// Writes the files to the archive /// </summary> /// <param name="generator">The generator</param> /// <param name="archive">The loaded archive data</param> /// <param name="outputFileStream">The file output stream for the archive</param> /// <param name="files">The files to include</param> public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files) { Logger.Info("A CNT archive is being repacked..."); // Get the archive data var data = (CNT)archive; // Create the file generator using ArchiveFileGenerator <CNT_File> fileGenerator = new(); // Get files and entries var archiveFiles = files.Select(x => new { Entry = (CNT_File)x.ArchiveEntry, FileItem = x }).ToArray(); // Set files and directories data.Directories = files.Select(x => x.Directory).Distinct().Where(x => !x.IsNullOrWhiteSpace()).ToArray(); data.Files = archiveFiles.Select(x => x.Entry).ToArray(); // Set the directory indexes foreach (var file in archiveFiles) { // Set the directory index file.Entry.DirectoryIndex = file.FileItem.Directory == String.Empty ? -1 : data.Directories.FindItemIndex(x => x == file.FileItem.Directory); } BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true); try { Context.AddFile(binaryFile); // Initialize the data data.Init(binaryFile.StartPointer); // Set the current pointer position to the header size data.RecalculateSize(); uint pointer = (uint)data.Size; // Disable checksum data.IsChecksumUsed = false; // NOTE: We can't disable the XOR key entirely as that would disable it for the file bytes too, which would require them all to be decrypted // Reset XOR keys data.StringsXORKey = 0; // Load each file foreach (var file in archiveFiles) { // Get the file entry CNT_File entry = file.Entry; // Reset checksum and XOR key entry.FileChecksum = 0; entry.Pre_FileNameXORKey = 0; // Add to the generator fileGenerator.Add(entry, () => { // Get the file stream to write to the archive Stream fileStream = file.FileItem.GetFileData(generator).Stream; // Set the pointer entry.FileOffset = pointer; // Update the pointer by the file size pointer += entry.FileSize; // Invoke event OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem)); return(fileStream); }); } // Make sure we have a generator for each file if (fileGenerator.Count != data.Files.Length) { throw new Exception("The .cnt file can't be serialized without a file generator for each file"); } // Write the file contents foreach (CNT_File file in data.Files) { // Get the file stream using Stream fileStream = fileGenerator.GetFileStream(file); // Set the position to the pointer outputFileStream.Stream.Position = file.FileOffset; // Write the contents from the generator fileStream.CopyTo(outputFileStream.Stream); } outputFileStream.Stream.Position = 0; // Serialize the data FileFactory.Write(Context, binaryFile.FilePath, data); Logger.Info("The CNT archive has been repacked"); } finally { Context.RemoveFile(binaryFile); } }
/// <summary> /// Writes the files to the archive /// </summary> /// <param name="generator">The generator</param> /// <param name="archive">The loaded archive data</param> /// <param name="outputFileStream">The file output stream for the archive</param> /// <param name="files">The files to include</param> public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files) { Logger.Info("An R1 PC archive is being repacked..."); // Get the archive data var data = (PC_FileArchive)archive; // Create the file generator using ArchiveFileGenerator <PC_FileArchiveEntry> fileGenerator = new(); // Get files and entries var archiveFiles = files.Select(x => new { Entry = (PC_FileArchiveEntry)x.ArchiveEntry, FileItem = x }).ToArray(); // Set files and directories data.Entries = archiveFiles.Select(x => x.Entry).ToArray(); BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true); try { Context.AddFile(binaryFile); // Initialize the data data.Init(binaryFile.StartPointer); // Set the current pointer position to the header size data.RecalculateSize(); uint pointer = (uint)data.Size; // Load each file foreach (var file in archiveFiles) { // Process the file name file.Entry.FileName = ProcessFileName(file.Entry.FileName); // Get the file entry PC_FileArchiveEntry entry = file.Entry; // Add to the generator fileGenerator.Add(entry, () => { // Get the file stream to write to the archive Stream fileStream = file.FileItem.GetFileData(generator).Stream; // Set the pointer entry.FileOffset = pointer; // Update the pointer by the file size pointer += entry.FileSize; // Invoke event OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem)); return(fileStream); }); } // Make sure we have a generator for each file if (fileGenerator.Count != data.Entries.Length) { throw new Exception("The .dat file can't be serialized without a file generator for each file"); } // Write the file contents foreach (PC_FileArchiveEntry file in data.Entries) { // Get the file stream using Stream fileStream = fileGenerator.GetFileStream(file); // Set the position to the pointer outputFileStream.Stream.Position = file.FileOffset; // Write the contents from the generator fileStream.CopyTo(outputFileStream.Stream); } outputFileStream.Stream.Position = 0; // Serialize the data FileFactory.Write(Context, binaryFile.FilePath, data); Logger.Info("The R1 PC archive has been repacked"); } finally { Context.RemoveFile(binaryFile); } }
/// <summary> /// Writes the files to the archive /// </summary> /// <param name="generator">The generator</param> /// <param name="archive">The loaded archive data</param> /// <param name="outputFileStream">The file output stream for the archive</param> /// <param name="files">The files to include</param> public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files) { Logger.Info("An IPK archive is being repacked..."); // Get the archive data var data = (BundleFile)archive; // Create the file generator using ArchiveFileGenerator <BundleFile_FileEntry> fileGenerator = new(); // Get files and entries var archiveFiles = files.Select(x => new { Entry = (BundleFile_FileEntry)x.ArchiveEntry, FileItem = x }).ToArray(); // Set the files data.FilePack.Files = archiveFiles.Select(x => x.Entry).ToArray(); data.BootHeader.FilesCount = (uint)data.FilePack.Files.Length; // Save the old base offset uint oldBaseOffset = data.BootHeader.BaseOffset; // Keep track of the current pointer position ulong currentOffset = 0; // Handle each file foreach (var file in archiveFiles) { // Get the file BundleFile_FileEntry entry = file.Entry; // Reset the offset array to always contain 1 item entry.Offsets = new ulong[] { entry.Offsets?.FirstOrDefault() ?? 0 }; // Set the count entry.OffsetsCount = (uint)entry.Offsets.Length; // Add to the generator fileGenerator.Add(entry, () => { // When reading the original file we need to use the old base offset uint newBaseOffset = data.BootHeader.BaseOffset; data.BootHeader.BaseOffset = oldBaseOffset; // Get the file bytes to write to the archive Stream fileStream = file.FileItem.GetFileData(generator).Stream; data.BootHeader.BaseOffset = newBaseOffset; // Set the offset entry.Offsets[0] = currentOffset; // Increase by the file size currentOffset += entry.ArchiveSize; // Invoke event OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem)); return(fileStream); }); } BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true); try { Context.AddFile(binaryFile); // Initialize the data data.Init(binaryFile.StartPointer); // Set the base offset data.RecalculateSize(); data.BootHeader.BaseOffset = (uint)data.Size; // Write the files WriteArchiveContent(data, outputFileStream.Stream, fileGenerator, Config.ShouldCompress(data.BootHeader)); outputFileStream.Stream.Position = 0; // Serialize the data FileFactory.Write(Context, binaryFile.FilePath, data); Logger.Info("The IPK archive has been repacked"); } finally { Context.RemoveFile(binaryFile); } }