using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace PFCRip { public class PFCFile { #region Declarations private byte[] _data; private PFCHeader _header; #endregion #region Constructors / Teardown public PFCFile(byte[] data, bool ignoreErrors) { _data = data; _Parse(); } #endregion #region Public Properties public PFCHeader Header { get { return _header; } } #endregion #region Public Methods public void ExtractFavorites(string directory, bool collapseToOneDirectory) { _DisplayEntry(directory, !collapseToOneDirectory, _header.Entries, _header.Entries[1], directory, true); } #endregion #region Private Methods private void _Parse() { if (_data == null || _data.Length == 0) throw new InvalidOperationException("Data cannot be empty"); string header = LittleEndianConverter.GetString(_data, 0, 8); if (header != "AOLVM100") throw new InvalidOperationException("Invalid header: " + header); //Get the index-table pointer uint ptr = LittleEndianConverter.ToUInt32(_data, 0x10); _header = new PFCHeader(_data, ptr); } private static void _DisplayEntry(string rootPath, bool recurseDirectories, PFCEntry[] allEntries, PFCEntry entry, string subFolder, bool atRoot) { string title = LittleEndianConverter.GetSafeString(entry.EntryData); if (entry.IsFolder) { //Dig down into the folder if (entry.ChildPtr > 0) { if (atRoot) { title = System.IO.Path.GetFileName(title); //For the "root", just get the file name of the PFC subFolder += "\\" + Utilities.SanitizePath(title, '_'); rootPath = subFolder; } else { if (recurseDirectories) subFolder += "\\" + Utilities.SanitizePath(title, '_'); else subFolder = rootPath + "\\" + Utilities.SanitizePath(title, '_'); } if (!System.IO.Directory.Exists(subFolder)) System.IO.Directory.CreateDirectory(subFolder); } if (entry.ChildPtr > 0) _DisplayEntry(rootPath, recurseDirectories, allEntries, allEntries[entry.ChildPtr], subFolder, false); } else { //This is a legitimate entry, see what type it is if (entry.DataPtr > 0) { switch (entry.Type) { case 0x0002: //Favorite { var record = new PFCDataRecord(allEntries[entry.DataPtr].Data); var url = record.SubItems[0x05]; //actual URL data //Write a .lnk shortcut file for this URL _WriteURLFile(subFolder, title, LittleEndianConverter.GetSafeString(url.Data)); break; } default: break; } } } //Then, display the next entry if (entry.NextPtr > 0) _DisplayEntry(rootPath, recurseDirectories, allEntries, allEntries[entry.NextPtr], subFolder, false); //We're done displaying entries in this folder, so move on up and recurse back to where we came from subFolder = System.IO.Directory.GetParent(subFolder).FullName; } private static void _WriteURLFile(string path, string title, string URL) { string filePath = String.Format("{0}\\{1}.url", path, Utilities.SanitizePath(title, '_')); var file = new System.IO.StreamWriter(filePath); file.WriteLine("[InternetShortcut]"); file.WriteLine("URL=" + URL); file.Close(); } #endregion } }