Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        /// <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];
        }