public static EQArchive Load(string Filename, byte[] Contents) { if (Util.IsBlank(Filename) || (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; } }
private void AddToMRUs(EQArchive OldArchive) { if ((Settings.RememberMRUs > 0) && (OldArchive != null) && (OldArchive.Filename != "(Untitled)") && (OldArchive.FilePath != "")) { string _fullPath = OldArchive.FilePath + @"\" + OldArchive.Filename; int _maxMRU = (Settings.RememberMRUs - 1); int _newMRU = _maxMRU; for (int _mru = 0; _mru < _maxMRU; _mru++) { if (Settings.MRUs[_mru].Equals(_fullPath, StringComparison.CurrentCultureIgnoreCase)) { _newMRU = _mru; break; } } for (int _i = _newMRU; _i > 0; _i--) { Settings.MRUs[_i] = Settings.MRUs[_i - 1]; } Settings.MRUs[0] = _fullPath; } }
public void Status_Changed(bool ForceListReload) { if (ArchiveLoading && (NewFile != null)) { ListViewItem _item = new ListViewItem(); _item.Tag = NewFile; UpdateItem(_item, false); listView1.Items.Add(_item); Application.DoEvents(); } else { saveToolStripMenuItem.Enabled = ((CurrentArchive != null) && (CurrentArchive.IsDirty) && (!CurrentArchive.Filename.Equals("(Untitled)"))); saveToolStripButton.Enabled = saveToolStripMenuItem.Enabled; saveAsToolStripMenuItem.Enabled = (CurrentArchive != null); saveAsToolStripButton.Enabled = saveAsToolStripMenuItem.Enabled; importFileToolStripMenuItem.Enabled = (CurrentArchive != null); importFileToolStripButton.Enabled = importFileToolStripMenuItem.Enabled; if (ForceListReload || (LastArchive != CurrentArchive) || ArchiveChanged) { ViewMode_Changed(); CancelThumbnailThread(); listView1.Items.Clear(); while (itemThumbsLarge.Images.Count > 3) { // Clear all but our default built-in icons itemThumbsLarge.Images.RemoveAt(3); itemThumbsSmall.Images.RemoveAt(3); } if (CurrentArchive != null) { foreach (EQArchiveFile _file in CurrentArchive.Files.Values) { ListViewItem _item = new ListViewItem(); _item.Tag = _file; UpdateItem(_item, false); listView1.Items.Add(_item); } } listView1.Enabled = (CurrentArchive != null); UpdateMRUs(); LastArchive = CurrentArchive; ArchiveChanged = false; listView1.LabelEdit = false; if (CurrentArchive != null) { toolStripProgressBar1.Value = 0; toolStripProgressBar1.Maximum = CurrentArchive.Files.Count; ThumbnailsLoaded = false; toolStripProgressBar1.Visible = true; } Application.DoEvents(); ViewMode_Restore(); threadListView.RunWorkerAsync(); } } if (CurrentArchive == null) { this.Text = Application.ProductName + " " + VersionNumber; toolStripStatusLabelFileCount.Text = ""; // "Files: 0"; toolStripStatusLabelArchiveSize.Text = ""; // "Size on Disk: 0"; } else { this.Text = CurrentArchive.Filename + (CurrentArchive.IsDirty ? "* - " : " - ") + Application.ProductName + " " + VersionNumber; toolStripStatusLabelFileCount.Text = "Files: " + CurrentArchive.Files.Count; toolStripStatusLabelArchiveSize.Text = "Size on Disk: " + CurrentArchive.SizeOnDisk.ToString("###,###,###,##0"); } Selection_Changed(); }
public void LoadArchive(string FilePath) { if (!ConfirmDiscard()) { // User canceled the change in archive return; } CancelThumbnailThread(); EQArchive _newArchive; if (FilePath == null) { _newArchive = null; } else if (FilePath == "") { // Requesting a new empty archive _newArchive = new EQArchive(); } else { _newArchive = EQArchive.Load(FilePath); switch (_newArchive.Status) { case Result.OK: // Hey, look at that. Nothing bad happened. Awesome. Let's use it. break; case Result.WrongFileType: MessageBox.Show(this, "The specified file was not recognized as a supported EQ Archive type:\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; case Result.MalformedFile: MessageBox.Show(this, "The specified file was recognized as an EQ archive, but contained invalid data:\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; case Result.FileTooLarge: MessageBox.Show(this, "The specified file is larger than the 4GB limit supported (WTF?!):\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; case Result.FileReadError: MessageBox.Show(this, "The specified file could not be read, or may be corrupted:\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; case Result.FileNotFound: MessageBox.Show(this, "The specified file does not exist:\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; case Result.DirectoryNotFound: MessageBox.Show(this, "The specified file location not exist:\r\n\r\n" + FilePath, "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; default: MessageBox.Show(this, "The specified file could not be opened as an archive:\n\n" + FilePath + "\n\nThe error received was: " + _newArchive.Status.ToString(), "Problem Opening Archive", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } AddToMRUs(CurrentArchive); CurrentArchive = _newArchive; ArchiveChanged = true; Status_Changed(); UpdateMRUs(); }
private bool ImportFile(EQArchiveFile File, bool Override) { if (CurrentArchive == null) { CurrentArchive = new EQArchive(); } EQArchiveFile _existing; if (Settings.ImportFormat != "") { _existing = CurrentArchive.FindFileOrSimilarImage(File.Filename); } else { _existing = CurrentArchive.FindFile(File.Filename); } string _newName = null; if (_existing != null) { _newName = _existing.Filename; if (!Override || Settings.ConfirmImportOverwrite) { switch (MessageBox.Show(this, "The file " + _existing.Filename + " exists in the archive.\n\nDo you wish to overwrite this file with your new one?", "Importing File Exists", MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { case DialogResult.No: _newName = File.Filename; while (CurrentArchive.FindFileOrSimilarImage(_newName) != null) { _newName = System.IO.Path.GetFileNameWithoutExtension(_newName) + " (New)" + System.IO.Path.GetExtension(_newName); } break; } } File.Filename = _newName; if (_newName == _existing.Filename) // We're replacing it. { DeleteItem(listView1.Items[_existing.Filename.ToLower()]); } } if ((Settings.ImportFormat != "") && (File.GetImage() != null) && (File.ImageFormat != Settings.ImportFormat)) { File = File.AsFormat(Settings.ImportFormat, (_existing == null)); } bool _ok = (CurrentArchive.Add(File) == Result.OK); NewFile = File; Status_Changed(true); return _ok; }
public static EQArchive Load(string Filename, byte[] Contents) { if (Util.IsBlank(Filename) || (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); } }