/// <summary> /// Replaces the contents of <paramref name="fileIndex"/> with the specified buffer /// </summary> /// <param name="fileId">File id of the file to replace</param> /// <param name="data">New file contents to write</param> /// <returns>True if the data was successfully compressed and written to the archive, false otherwise</returns> public bool InjectFile(uint fileId, byte[] data) { // Make sure we have a file id entry in the dictionary. if (this.fileEntryLookupDictionary.ContainsKey(fileId) == false) { // No dictionary entry for this file id. return(false); } // Get the index of the file from the file id. int fileIndex = this.fileEntryLookupDictionary[fileId]; // Open the archive for writing. if (OpenArchive(true) == false) { // Failed to open the archive. return(false); } // Compress the data. byte[] compressedData = ZlibUtilities.CompressData(data); // Seek to the end of the this file's data. this.reader.BaseStream.Position = this.fileEntries[fileIndex].DataOffset + this.fileEntries[fileIndex].CompressedSize; // Read all the remaining data in the file. byte[] remainingData = this.reader.ReadBytes((int)(this.reader.BaseStream.Length - this.reader.BaseStream.Position)); // Seek to the start of the old file's data. this.writer.BaseStream.Position = this.fileEntries[fileIndex].DataOffset; // Calculate the offset shift amount and write the new compressed data buffer. int offsetShift = compressedData.Length - this.fileEntries[fileIndex].CompressedSize; this.writer.Write(compressedData); // Write the remaining file data. this.writer.Write(remainingData); // Loop and update any file entries that need to accommodate for the file size change. for (int i = 0; i < this.fileEntries.Count; i++) { // Check if this file's offset needs to be updated or if it is the file we just replaced. if (this.fileEntries[i].DataOffset > this.fileEntries[fileIndex].DataOffset) { // Update this file entries data offset. this.fileEntries[i].DataOffset += offsetShift; // Write the new offset to file. this.writer.BaseStream.Position = ArchiveHeader.kSizeOf + (ArchiveFileEntry.kSizeOf * i) + 76; this.writer.Write(this.fileEntries[i].DataOffset); } else if (i == fileIndex) { // Update the data sizes for this file entry. this.fileEntries[i].CompressedSize = compressedData.Length; this.fileEntries[i].DecompressedSize = data.Length; // Write the new data sizes to file. this.writer.BaseStream.Position = ArchiveHeader.kSizeOf + (ArchiveFileEntry.kSizeOf * i) + 64 + 4; this.writer.Write(this.fileEntries[i].CompressedSize); this.writer.Write(this.fileEntries[i].DecompressedSize); } } // Close the archive and return. CloseArchive(); return(true); }
public bool AddFiles(string[] newFileNames, string[] filePaths, out DatumIndex[] newDatums) { // Satisfy the compiler. newDatums = new DatumIndex[0]; // Open the archive for writing. if (OpenArchive(true) == false) { // Failed to open the archive for writing. return(false); } // Create a new memory stream to hold compressed file data. List <int> fileOffsets = new List <int>(); MemoryStream dataStream = new MemoryStream((int)this.reader.BaseStream.Length); // Loop through all the files in the archive and read each one into the memory stream. for (int i = 0; i < this.fileEntries.Count; i++) { // Seek to the data offset. this.reader.BaseStream.Position = this.fileEntries[i].DataOffset; // Read the compressed file data. byte[] compressedData = this.reader.ReadBytes(this.fileEntries[i].CompressedSize); // Save the data offset and write the compressed data to the memory stream. fileOffsets.Add((int)dataStream.Position); dataStream.Write(compressedData, 0, compressedData.Length); } // Loop through all of the new files and create file entries for each one. newDatums = new DatumIndex[newFileNames.Length]; for (int i = 0; i < newFileNames.Length; i++) { // Read the file contents and compress it. byte[] decompressedData = File.ReadAllBytes(filePaths[i]); byte[] compressedData = ZlibUtilities.CompressData(decompressedData); // Get the resource type from the file extension. ResourceType fileType = (ResourceType)Enum.Parse(typeof(ResourceType), filePaths[i].Substring(filePaths[i].LastIndexOf('.') + 1), true); // Create a new file entry for this file. ArchiveFileEntry newFileEntry = new ArchiveFileEntry(); newFileEntry.FileName = newFileNames[i] + "." + fileType.ToString(); newFileEntry.CompressedSize = compressedData.Length; newFileEntry.DecompressedSize = decompressedData.Length; newFileEntry.FileId = this.nextFileId++; newFileEntry.FileType = fileType; // Copy the new datum to the output list. newDatums[i] = new DatumIndex(this.ArchiveId, newFileEntry.FileId); // Add the file entry to the list and create a reverse lookup entry. this.fileEntryLookupDictionary.Add(newFileEntry.FileId, this.fileEntries.Count); this.fileEntries.Add(newFileEntry); // Add the data offset to the list and write the compressed data to the data stream. fileOffsets.Add((int)dataStream.Position); dataStream.Write(compressedData, 0, compressedData.Length); } // Update the header for the new file count. this.writer.BaseStream.Position = 0; this.writer.Write(ArchiveHeader.kHeaderMagic1); this.writer.Write((short)ArchiveHeader.kVersion); this.writer.Write((short)this.fileEntries.Count); // Calculate the data start offset based on the new number of files. int dataStart = ArchiveHeader.kSizeOf + (this.fileEntries.Count * ArchiveFileEntry.kSizeOf); if (dataStart % 0x8000 != 0) { dataStart += 0x8000 - (dataStart % 0x8000); } // Loop and write all the file entries. for (int i = 0; i < this.fileEntries.Count; i++) { // Write the file entry. string fileName = this.fileEntries[i].GetFileNameNoExtension(); this.writer.Write(fileName.ToCharArray()); this.writer.Write(new byte[ArchiveFileEntry.kMaxFileNameLength - fileName.Length]); // If we know the file type for this file get the file type id. if (GameResource.KnownResourceTypesReverse.ContainsKey(this.fileEntries[i].FileType) == true) { this.writer.Write(GameResource.KnownResourceTypesReverse[this.fileEntries[i].FileType]); } else { this.writer.Write((int)this.fileEntries[i].FileType); } this.writer.Write(this.fileEntries[i].CompressedSize); this.writer.Write(this.fileEntries[i].DecompressedSize); this.writer.Write(dataStart + fileOffsets[i]); // Update the file offset. this.fileEntries[i].DataOffset = dataStart + fileOffsets[i]; } // Fill the rest with padding. this.writer.Write(new byte[dataStart - (int)this.writer.BaseStream.Position]); // Write the data stream and calculate the new file size. this.writer.Write(dataStream.ToArray(), 0, (int)dataStream.Length); this.fileStream.SetLength(this.writer.BaseStream.Position); // Close the archive. CloseArchive(); return(true); }