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); }
/// <summary> /// Checks if there is an archive containing the specified file name /// </summary> /// <param name="fileName">Full file name including file extension of the file to search for</param> /// <param name="arcFile">The <see cref="Archive"/> the file was found in</param> /// <param name="fileEntry">The <see cref="ArchiveFileEntry"/> for the file</param> public void GetArchiveFileEntryFromFileName(string fileName, out Archive arcFile, out ArchiveFileEntry fileEntry) { // Satisfy the compiler. arcFile = null; fileEntry = null; // Check if we have a patch file entry for this file name. if (this.patchArchiveFileNameReverseDictionary.ContainsKey(fileName) == true) { // Load the file info from the patch archive. DatumIndex patchDatum = this.patchArchiveFileNameReverseDictionary[fileName][0]; GetArchiveFileEntryFromDatum(patchDatum, out arcFile, out fileEntry); return; } // Check if we have an entry for this file name. if (this.archiveFileNameReverseDictionary.ContainsKey(fileName) == false) { // No matching file name found. return; } // Get the datum for this file entry. // TODO: One day, in the maybe not so distant future, we will need to handle loading from // a specific archive. DatumIndex datum = this.archiveFileNameReverseDictionary[fileName][0]; GetArchiveFileEntryFromDatum(datum, out arcFile, out fileEntry); }
public bool OpenAndRead() { bool Result = false; // Open the archive for reading. if (OpenArchive(false) == false) { // Failed to open the archive. return(false); } // Make sure the file is large enough to have the header. if (this.fileStream.Length < ArchiveHeader.kSizeOf) { // File is too small to be a valid archive. goto Cleanup; } // Read the archive header. ArchiveHeader header = new ArchiveHeader(); header.Magic = reader.ReadInt32(); header.Version = reader.ReadInt16(); header.NumberOfFiles = reader.ReadInt16(); // Verify the header magic. if (header.Magic != ArchiveHeader.kHeaderMagic1) { // Check if the magic is in big endian. if (EndianUtilities.ByteFlip32(header.Magic) == ArchiveHeader.kHeaderMagic1) { // Set the endianness for future IO operations. this.Endian = Endianness.Big; this.reader.Endian = Endianness.Big; // Correct the header values we already read. header.Magic = EndianUtilities.ByteFlip32(header.Magic); header.Version = EndianUtilities.ByteFlip16(header.Version); header.NumberOfFiles = EndianUtilities.ByteFlip16(header.NumberOfFiles); } else { // archive header has invalid magic. goto Cleanup; } } // Verify the file version. if (header.Version != ArchiveHeader.kVersion) { // archive is invalid version. goto Cleanup; } // Loop for the number of files and read each file entry. for (int i = 0; i < header.NumberOfFiles; i++) { ArchiveFileEntry fileEntry = new ArchiveFileEntry(); // Save the current position and read the file name. long offset = this.reader.BaseStream.Position; fileEntry.FileName = this.reader.ReadNullTerminatedString(); // Advance to the end of the file name and read the rest of the file entry structure. this.reader.BaseStream.Position = offset + 64; int fileType = this.reader.ReadInt32(); fileEntry.CompressedSize = this.reader.ReadInt32(); fileEntry.DecompressedSize = this.reader.ReadInt32(); fileEntry.DataOffset = this.reader.ReadInt32(); // Create a unique file id for the file. fileEntry.FileId = this.nextFileId++; // Check if the type of file is known. if (GameResource.KnownResourceTypes.ContainsKey(fileType) == true) { // Set the file type and add it as a file extension to the file name. fileEntry.FileType = GameResource.KnownResourceTypes[fileType]; fileEntry.FileName += "." + fileEntry.FileType.ToString(); } else { fileEntry.FileType = ResourceType.Unknown; } // Calculate the unique id for the file and add it to the file loopup dictionary. this.fileEntryLookupDictionary.Add(fileEntry.FileId, this.fileEntries.Count); // Add the file entry to the list of files. this.fileEntries.Add(fileEntry); } // Successfully parsed the archive. Result = true; Cleanup: // Close the archive. CloseArchive(); // Return the result. return(Result); }
/// <summary> /// Finds the archive and file entry for the specified file datum /// </summary> /// <param name="datum">File datum to find in the archive collection</param> /// <param name="arcFile">Archive instance the file is located in</param> /// <param name="fileEntry">File entry for the specified file</param> public void GetArchiveFileEntryFromDatum(DatumIndex datum, out Archive arcFile, out ArchiveFileEntry fileEntry) { // Make sure there is an entry in the reverse lookup table for this archive id. if (this.archiveLookupDictionary.ContainsKey(datum.ArchiveId) == false) { // No entry for this archive id found. arcFile = null; fileEntry = null; return; } // Get the file and archive indices from the datum. int archiveIndex = this.archiveLookupDictionary[datum.ArchiveId]; int fileIndex = this.archives[archiveIndex].FindFileFromDatum(datum); if (fileIndex == -1) { // No file for this datum was found. arcFile = null; fileEntry = null; return; } // Return the archive and file entry. arcFile = this.archives[archiveIndex]; fileEntry = arcFile.FileEntries[fileIndex]; }