/// <summary> /// Gets the next block. /// </summary> /// <returns></returns> /// <exception cref="PK2NotLoadedException"></exception> public PK2Block GetNextBlock() { if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } return(HasBlocks ? new PK2Block(FileAdapter.GetInstance().ReadData((long)Entries[19].NextChain, 2560), Entries[19].NextChain) : this); }
/// <summary> /// Saves the block back to the PK2 archive. /// </summary> /// <exception cref="PK2NotLoadedException"></exception> public void Save() { if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } FileAdapter.GetInstance().WriteData(ToByteArray(), (long)Offset); }
/// <summary> /// Gets the data. /// </summary> /// <returns></returns> /// <exception cref="PK2NotLoadedException"></exception> /// <exception cref="System.InvalidOperationException">It's impossible to read data from a directory or from a deleted file.</exception> public byte[] GetData() { if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } if (Type != PK2EntryType.File) { throw new InvalidOperationException("It's impossible to read data from a directory or from a deleted file."); //TODO: find something better } return(FileAdapter.GetInstance().ReadData((long)Position, (int)Size)); }
/// <summary> /// Extracts the file to the specified destination /// </summary> /// <param name="destination">The destination.</param> /// <exception cref="PK2NotLoadedException"></exception> /// <exception cref="System.InvalidOperationException">Directories can not be extracted.</exception> public void Extract(string destination) { if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } if (Type != PK2EntryType.File) { throw new InvalidOperationException("Directories can not be extracted."); //TODO: find something better } File.WriteAllBytes(destination, FileAdapter.GetInstance().ReadData((long)Position, (int)Size)); }
/// <summary> /// Reads the block. /// </summary> /// <param name="position">The position.</param> /// <param name="currentDirectory">The current directory.</param> private void ReadBlockAt(ulong position, PK2Directory currentDirectory = null) { var currentBlockBuffer = FileAdapter.GetInstance().ReadData((long)position, 2560); var currentBlock = new PK2Block(currentBlockBuffer, position); //Check if the chain continues at another position if (currentBlock.Entries[19].NextChain > 0) { ReadBlockAt(currentBlock.Entries[19].NextChain); } //Read the subfolder chain //The folder "." and ".." are required if you which to use "free-browsing" mode, without reading the whole index at once foreach (var pk2Entry in currentBlock.Entries.Where(entry => !entry.Name.StartsWith(".") && entry.Position > 0)) { switch (pk2Entry.Type) { case PK2EntryType.Directory: if (Config.Mode == PK2Mode.Index) { var subDirectory = new PK2Directory(pk2Entry, currentDirectory?.Path); _directories.Add(subDirectory); ReadBlockAt(pk2Entry.Position, subDirectory); } else { ReadBlockAt(pk2Entry.Position); } break; case PK2EntryType.File: if (Config.Mode == PK2Mode.Index) { _files.Add(new PK2File(pk2Entry, currentDirectory?.Path)); } break; } } if (Config.Mode == PK2Mode.IndexBlocks) { Blocks.Add(currentBlock); } }
/// <summary> /// Creates a new block within the PK2. /// </summary> /// <param name="entries">The entries.</param> /// <returns></returns> /// <exception cref="PK2NotLoadedException"></exception> public static PK2Block Create(PK2Entry[] entries = null) { if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } var buffer = new byte[2560]; using (var stream = new StreamWorker(buffer, StreamOperation.Write)) { for (var i = 0; i < 20; i++) { stream.WriteByteArray(entries?[i] != null ? entries[i].ToByteArray() : new PK2Entry().ToByteArray()); } } return(new PK2Block(buffer, (ulong)FileAdapter.GetInstance().AppendData(buffer))); }
/// <summary> /// Reads the block at. /// This function does not recursively ready any "sub-chunks" /// </summary> /// <param name="position">The position.</param> /// <returns></returns> private void ReadBlockAt(ulong position) { Blocks = new PK2BlockCollection(); Directories = new List <PK2Directory>(); Files = new List <PK2File>(); while (true) { try { var currentBlockBuffer = FileAdapter.GetInstance().ReadData((long)position, 2560); var currentBlock = new PK2Block(currentBlockBuffer, position); Blocks.Blocks.Add(currentBlock); foreach (var entry in currentBlock.Entries) { switch (entry.Type) { case PK2EntryType.Directory: Directories.Add(new PK2Directory(entry, "")); break; case PK2EntryType.File: Files.Add(new PK2File(entry, "")); break; } } //read the next block (only if the first block was not enough for the folder) if (currentBlock.Entries[19].NextChain > 0) { position = currentBlock.Entries[19].NextChain; continue; } break; } catch { throw new InvalidBlockException((long)position); } //should only be thrown, if the user fakes any position data in any entry! } }
/// <summary> /// Creates the file. /// </summary> /// <param name="type">The type.</param> /// <param name="parent">The parent.</param> /// <param name="name">The name.</param> /// <param name="data">The data. Obsolete if the type is a directory.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException">Can only create new items in a directory!</exception> /// <exception cref="PK2NotLoadedException"></exception> /// <exception cref="System.NullReferenceException">Can not write to an empty block</exception> public static PK2Entry Create(PK2EntryType type, PK2Entry parent, string name, byte[] data = null) { if (parent.Type != PK2EntryType.Directory) { throw new InvalidOperationException("Can only create new items in a directory!"); } if (FileAdapter.GetInstance() == null) { throw new PK2NotLoadedException(); } if (parent.GetChildBlock() == null) { throw new NullReferenceException("Can not write to an empty block"); } var entryToOverwrite = parent.GetChildBlock().GetFirstEmptyEntry(); //If all blocks are ocupied we have to create a new one if (entryToOverwrite == null) { var newBlock = PK2Block.Create(); //Tell the last entry where to find our new block.. var lastEntry = parent.GetChildBlock().GetLastBlock().Entries[19]; lastEntry.NextChain = newBlock.Offset; lastEntry.Save(); entryToOverwrite = newBlock.GetFirstEmptyEntry(); } if (type == PK2EntryType.Directory && name == "." || name == "..") { entryToOverwrite.Position = name == "." ? entryToOverwrite.Block.Offset : parent.Block.Offset; } //create new block for the sub directories if (type == PK2EntryType.Directory && name != "." && name != "..") { var newBlock = PK2Block.Create(); entryToOverwrite.Position = newBlock.Offset; } //Assign properties to the entry entryToOverwrite.Type = type; entryToOverwrite.AccessTime = DateTime.Now; entryToOverwrite.CreateTime = DateTime.Now; entryToOverwrite.ModifyTime = DateTime.Now; entryToOverwrite.Name = name; if (type == PK2EntryType.File && data != null) { entryToOverwrite.Size = (uint)data.Length; } //Write file to PK2 and assign the position if (type == PK2EntryType.File) { entryToOverwrite.Position = (ulong)FileAdapter.GetInstance().AppendData(data); } //Write the entry to the PK2 entryToOverwrite.Save(); if (type != PK2EntryType.Directory || name == "." || name == "..") { return(entryToOverwrite); } //Create those browser things... Create(PK2EntryType.Directory, entryToOverwrite, "."); Create(PK2EntryType.Directory, entryToOverwrite, ".."); return(entryToOverwrite); }
/// <summary> /// Gets the child block if the entry is a directory. /// </summary> /// <returns></returns> public PK2Block GetChildBlock() { return(new PK2Block(FileAdapter.GetInstance().ReadData((long)Position, 2560), Position)); }
/// <summary> /// Initializes a new instance of the <see cref="PK2Archive"/> class. /// The key is set to the isro standard encryption key. /// Do not provide any baseKey, if you wish to use the default base key for the archive. /// </summary> /// <exception cref="System.IO.FileNotFoundException"></exception> /// <exception cref="SlimPK2.Security.BlowfishSecurityException"></exception> /// <exception cref="SlimPK2.Types.InvalidHeaderException"></exception> public PK2Archive(string path, PK2Config config = null) { if (!File.Exists(path)) { throw new FileNotFoundException(path); } //Initialize default config if (config == null) { config = PK2Config.GetDefault(); } //assign the config to this instance Config = config; //Create file reader... FileAdapter.SetInstance(new FileAdapter(path)); Path = path; //Read header... Header = new PK2Header(FileAdapter.GetInstance().ReadData(0, 256)); if (Header.Encrypted) { var blowfishKey = BlowfishUtilities.GenerateFinalBlowfishKey(Config.Key, Config.BaseKey); var blowfish = new Blowfish(); blowfish.Initialize(blowfishKey); BlowfishUtilities.SetBlowfish(blowfish); var tempChecksum = BlowfishUtilities.GetBlowfish().Encode(Encoding.ASCII.GetBytes("Joymax Pak File")); //Check if the security checksum equals the generated checksum if (tempChecksum[0] != Header.SecurityChecksum[0] || tempChecksum[1] != Header.SecurityChecksum[1] || tempChecksum[2] != Header.SecurityChecksum[2]) { throw new BlowfishSecurityException(Config.Key); } } if (Config.Mode != PK2Mode.FreeBrowse) { switch (Config.Mode) { case PK2Mode.IndexBlocks: Blocks = new List <PK2Block>(); ReadBlockAt(256); break; case PK2Mode.Index: _files = new List <PK2File>(); _directories = new List <PK2Directory>(); ReadBlockAt(256, new PK2Directory()); break; } } else { _navigator = new PK2Navigator(); } Loaded = true; }