Esempio n. 1
0
    public Result Add(EQArchiveFile File, bool ReplaceSimilarImage)
    {
        if (File == null)
        {
            return(Result.InvalidArgument);
        }

        EQArchiveFile _archiveFile;

        if (ReplaceSimilarImage)
        {
            _archiveFile = this.FindFileOrSimilarImage(Filename);
        }
        else
        {
            _archiveFile = this.FindFile(Filename);
        }

        if (_archiveFile != null)
        {
            // We already have a file that has a similar name. Import the new file AS that file, replacing it.

            this.Files.RemoveAt(this.Files.IndexOfValue(_archiveFile));

            File.Filename = _archiveFile.Filename;
        }

        this.Files[File.Filename.ToLower()] = File;

        this.IsDirty = true;

        return(File.Status);
    }
Esempio n. 2
0
    public Result Remove(EQArchiveFile File)
    {
        if (File == null)
        {
            return(Result.FileNotFound);
        }

        this.Files.RemoveAt(this.Files.IndexOfValue(File));

        this.IsDirty = true;

        return(Result.OK);
    }
Esempio n. 3
0
    public EQArchiveFile AsFormat(string NewFormat, bool ChangeExtension)
    {
        if ((NewFormat == null) || (NewFormat == "") || (this.GetImage() == null))
        {
            return(this);
        }

        NewFormat = NewFormat.ToLower();

        if (NewFormat == "auto")
        {
            switch (GetAlphaBits())
            {
            case 0:
            case 1:
                NewFormat = "16-bit";
                break;

            case 8:
                NewFormat = "32-bit";
                break;

            default:     // ?
                NewFormat = "32-bit";
                break;
            }
        }

        EQArchiveFile _newFile = null;

        if (NewFormat != this.ImageFormat)
        {
            _newFile          = new EQArchiveFile();
            _newFile.Filename = this.Filename;
            _newFile.SetImage(this.GetImage(), NewFormat);
        }

        if (ChangeExtension && !System.IO.Path.GetExtension(this.Filename).Equals((NewFormat[0] == '.' ? NewFormat : ".dds"), StringComparison.CurrentCultureIgnoreCase))
        {
            // Gotta change the extension

            if (_newFile == null)
            {
                _newFile = new EQArchiveFile();
                _newFile.SetContents(this.GetContents());
            }

            _newFile.Filename = System.IO.Path.GetFileNameWithoutExtension(this.Filename) + (NewFormat[0] == '.' ? NewFormat : ".dds");
        }
        else
        {
            if (_newFile == null)
            {
                _newFile = this; // Unchanged from our current contents
            }
            else
            {
                _newFile.Filename = this.Filename;
            }
        }

        return(_newFile);
    }
Esempio n. 4
0
    public Result Save()
    {
        if ((this.FilePath == "") || (this.Filename == "(Untitled)") || (this.Filename == ""))
        {
            return(Result.InvalidArgument);
        }

        Result _result = Result.OK;

        try
        {
            using (BinaryWriter _file = new BinaryWriter(File.Create(this.FilePath + @"\" + this.Filename)))
            {
                //
                //    Step 1 - Get an order of files by filename CRC, per standard PFS archive practice.
                //

                SortedList <UInt32, EQArchiveFile> _filesByCRC = new SortedList <UInt32, EQArchiveFile>();

                foreach (EQArchiveFile _entry in this.Files.Values)
                {
                    _filesByCRC.Add(GetFilenameCRC(_entry.Filename), _entry);
                }

                //
                //    Step 2 - Build the directory of filenames and compress it for adding at the end of the archive
                //

                EQArchiveFile _directory = new EQArchiveFile();

                byte[] _directoryBytes = new byte[64 * 1024]; // global_chr.s3d = ~29,000 bytes of filenames!

                using (BinaryWriter _stream = new BinaryWriter(new MemoryStream(_directoryBytes)))
                {
                    UInt32 _directorySize = 0;

                    _stream.Write((UInt32)this.Files.Count);

                    foreach (EQArchiveFile _entry in _filesByCRC.Values)
                    {
                        _stream.Write((UInt32)_entry.Filename.Length + 1);
                        foreach (char _c in _entry.Filename)
                        {
                            _stream.Write(_c);
                        }
                        _stream.Write('\0');
                    }

                    _directorySize = (UInt32)_stream.BaseStream.Position;

                    Array.Resize <byte>(ref _directoryBytes, (int)_directorySize);

                    _directory.SetContents(_directoryBytes);
                }

                //
                //    Step 3 - Build the file header
                //

                Header _header = new Header();
                _header.MagicNumber   = _MagicNumber;
                _header.VersionNumber = 0x00020000;

                // a. Index Pointer must be determined. Start with the size after the header itself
                _header.IndexPointer = 4 + 4 + 4;

                // b. Add in the size of all of the compressed chunks and their two size values
                foreach (EQArchiveFile _entry in this.Files.Values)
                {
                    _header.IndexPointer += (4 + 4) * (_entry.CompressedChunks == null ? 1 : (UInt32)_entry.CompressedChunks.Count);
                    _header.IndexPointer += _entry.Size.Compressed;
                }

                // c. Add in the size of the compressed filename directory and its size values
                _header.IndexPointer += (4 + 4) * (UInt32)_directory.CompressedChunks.Count + _directory.Size.Compressed;

                //
                //    Step 4 - Write the file Header
                //

                _file.Write(_header.IndexPointer);
                _file.Write(_header.MagicNumber);
                _file.Write(_header.VersionNumber);

                //
                //    Step 5 - Compressed File Chunks
                //
                foreach (EQArchiveFile _entry in _filesByCRC.Values)
                {
                    _entry.FilePointer = (UInt32)_file.BaseStream.Position;

                    foreach (EQArchiveFile.Chunk _chunk in _entry.CompressedChunks)
                    {
                        _file.Write(_chunk.Size.Compressed);
                        _file.Write(_chunk.Size.Uncompressed);
                        _file.Write(_chunk.CompressedData, 0, (int)_chunk.Size.Compressed);
                    }
                }

                //
                //    Step 6 - Filename Directory compressed chunks at the end
                //
                _directory.FilePointer = (UInt32)_file.BaseStream.Position;

                foreach (EQArchiveFile.Chunk _chunk in _directory.CompressedChunks)
                {
                    _file.Write(_chunk.Size.Compressed);
                    _file.Write(_chunk.Size.Uncompressed);
                    _file.Write(_chunk.CompressedData, 0, (int)_chunk.Size.Compressed);
                }

                //
                //    Step 7 - Index of File Entries
                //
                _file.Write((UInt32)(this.Files.Count + 1));

                foreach (KeyValuePair <UInt32, EQArchiveFile> _kvp in _filesByCRC)
                {
                    _file.Write(_kvp.Key);
                    _file.Write(_kvp.Value.FilePointer);
                    _file.Write(_kvp.Value.Size.Uncompressed);
                }

                //
                //    Step 8 - Add filename directory to end of index
                //

                _file.Write(0xFFFFFFFFU);
                _file.Write(_directory.FilePointer);
                _file.Write(_directory.Size.Uncompressed);

                //
                //    Step 9 - PFS Footer
                //

                foreach (char _letter in _FooterToken)
                {
                    _file.Write(_letter);
                }

                _file.Write(_header.DateStamp);

                _file.Close();
            }
        }
        catch
        {
            return(Result.FileWriteError);
        }

        if (_result == Result.OK)
        {
            this.IsDirty = false;
        }

        return(_result);
    }
Esempio n. 5
0
    public static EQArchive Load(string Filename, byte[] Contents)
    {
        if (Filename.Length == 0 || Contents == null)
        {
            // We got a bad filename, or the file is zero length, and thus not a PFS archive.

            return(null);
        }

        Header _header = new Header();

        using (BinaryReader _input = new BinaryReader(new MemoryStream(Contents)))
        {
            try
            {
                //
                //   1. Read the file header
                //

                _header.IndexPointer  = _input.ReadUInt32();
                _header.MagicNumber   = _input.ReadUInt32();
                _header.VersionNumber = _input.ReadUInt32();
            }
            catch
            {
                // Too small to be a PFS archive

                return(null);
            }

            if (_header.MagicNumber != _MagicNumber)
            {
                // Not a PFS archive

                return(null);
            }

            EQArchive _archive = new EQArchive();
            _archive.Filename   = Filename;
            _archive.SizeOnDisk = (UInt32)Contents.Length;

            try
            {
                //
                //   2. Read Index of File Pointers and Sizes in Archive
                //

                _input.BaseStream.Seek(_header.IndexPointer, SeekOrigin.Begin);

                _header.EntryCount = _input.ReadUInt32();

                if (_header.EntryCount == 0)
                {
                    // Empty archive...?
                    _archive.Files = new SortedList <string, EQArchiveFile>();
                }
                else
                {
                    _archive.Files = new SortedList <string, EQArchiveFile>((int)_header.EntryCount);
                }

                // Filename directory is the "file" at the end of the archive (with the highest FilePointer)
                EQArchiveFile _directory = null;

                // For verification later, which is optional, but will catch a malformed/corrupted archive.
                Dictionary <UInt32, UInt32> _filenameCRCs = new Dictionary <UInt32, UInt32>();

                // Files in a PFS archive tend to be stored by ascending order of FilenameCRC.
                // However, the filename directory is sorted by FilePointer
                SortedList <UInt32, EQArchiveFile> _files = new SortedList <UInt32, EQArchiveFile>();

                for (UInt32 _index = 0; _index < _header.EntryCount; _index++)
                {
                    EQArchiveFile _file        = new EQArchiveFile();
                    UInt32        _filenameCRC = _input.ReadUInt32();
                    _file.FilePointer       = _input.ReadUInt32();
                    _file.Size.Uncompressed = _input.ReadUInt32();
                    _filenameCRCs.Add(_file.FilePointer, _filenameCRC);

                    if ((_directory == null) || (_file.FilePointer > _directory.FilePointer))
                    {
                        _directory = _file;
                    }

                    _files.Add(_file.FilePointer, _file);
                }

                if ((_input.BaseStream.Length - _input.BaseStream.Position) >= 9)
                {
                    // PFS Footer

                    char[] _token = _input.ReadChars(5);

                    if (new String(_token).Equals("STEVE"))
                    {
                        // Valid Footer Token

                        _header.DateStamp = _input.ReadUInt32();
                    }
                }

                //
                //   3. Read the compressed file entries (each split into compressed chunks)
                //

                foreach (EQArchiveFile _file in _files.Values)
                {
                    // Seek to entry position in stream
                    _input.BaseStream.Seek(_file.FilePointer, SeekOrigin.Begin);

                    UInt32 _totalUncompressedBytes = _file.Size.Uncompressed;
                    _file.Size.Uncompressed = 0;

                    while ((_file.Size.Uncompressed < _totalUncompressedBytes) && (_input.BaseStream.Position < _input.BaseStream.Length))
                    {
                        UInt32 _blockSizeCmp = _input.ReadUInt32();
                        UInt32 _blockSizeUnc = _input.ReadUInt32();

                        // Sanity Check 1: Uncompressed data larger than what we were told?
                        if ((_blockSizeUnc + _file.Size.Uncompressed) > _totalUncompressedBytes)
                        {
                            throw new Exception();
                        }

                        // Sanity Check 2: Compressed data goes past the end of the file?
                        if ((_input.BaseStream.Position + _blockSizeCmp) > _input.BaseStream.Length)
                        {
                            throw new Exception();
                        }

                        _file.AddChunk(new EQArchiveFile.Chunk(Contents, (UInt32)_input.BaseStream.Position, _blockSizeCmp, _blockSizeUnc, true));

                        _input.BaseStream.Position += _blockSizeCmp;
                    }
                }

                //
                //    4. Unpack and parse the directory of filenames from the "file" at the end of the archive (highest FilePointer)
                //

                // Remove directory from file entries in archive. We'll have to rebuild it when saving the archive anyway.
                _files.Remove(_directory.FilePointer);
                _header.EntryCount--;

                // Load filenames from directory
                BinaryReader _dirStream = new BinaryReader(new MemoryStream(_directory.GetContents()));

                UInt32 _filenameCount = _dirStream.ReadUInt32();

                if (_filenameCount > _header.EntryCount)
                {
                    // If we somehow have more filenames than entries in the archive, ignore the glitched extras

                    _filenameCount = _header.EntryCount;
                }

                _archive.Files = new SortedList <string, EQArchiveFile>();

                foreach (EQArchiveFile _file in _files.Values)
                {
                    Int32  _len       = _dirStream.ReadInt32();
                    char[] _inputname = _dirStream.ReadChars(_len);
                    UInt32 _crc       = GetFilenameCRC(_inputname);

                    if (_crc != _filenameCRCs[_file.FilePointer])
                    {
                        // Filename doesn't match with what we were given in Step 2

                        // This happens in gequip.s3d. We are ignoring it.

                        //throw new Exception();
                    }

                    _file.Filename = new string(_inputname, 0, _len - 1);

                    _archive.Files.Add(_file.Filename.ToLower(), _file);
                }

                // All entries loaded and filenames read from directory.

                _archive.Status = Result.OK;
            }
            catch
            {
                _archive.Status = Result.MalformedFile;
            }

            return(_archive);
        }
    }
Esempio n. 6
0
 public Result Add(EQArchiveFile File)
 {
     return(this.Add(File, false));
 }