Пример #1
1
        private static void AddNodes(String depth, CFStorage cfs)
        {

            VisitedEntryAction va = delegate(ICFItem target)
            {

                String temp = target.Name + (target is CFStorage ? "" : " (" + target.Size + " bytes )");

                //Stream

                Console.WriteLine(depth + temp);

                if (target is CFStorage)
                {  //Storage

                    String newDepth = depth + "    ";

                    //Recursion into the storage
                    AddNodes(newDepth, (CFStorage)target);

                }
            };

            //Visit NON-recursively (first level only)
            cfs.VisitEntries(va, false);
        }
Пример #2
1
        /// <summary>
        /// Recursive addition of tree nodes foreach child of current item in the storage
        /// </summary>
        /// <param name="node">Current TreeNode</param>
        /// <param name="cfs">Current storage associated with node</param>
        private void AddNodes(TreeNode node, CFStorage cfs)
        {
            VisitedEntryAction va = delegate(CFItem target)
            {
                TreeNode temp = node.Nodes.Add(
                    target.Name,
                    target.Name + (target.IsStream ? " (" + target.Size + " bytes )" : "")
                    );

                temp.Tag = target;

                if (target.IsStream)
                {
                    //Stream
                    temp.ImageIndex = 1;
                    temp.SelectedImageIndex = 1;

                }
                else
                {
                    //Storage
                    temp.ImageIndex = 0;
                    temp.SelectedImageIndex = 0;

                    //Recursion into the storage
                    AddNodes(temp, (CFStorage)target);
                }
            };

            //Visit NON-recursively (first level only)
            cfs.VisitEntries(va, false);
        }
Пример #3
0
        /// <summary>
        /// Get a named storage contained in the current one if existing.
        /// </summary>
        /// <param name="storageName">Name of the storage to look for</param>
        /// <param name="cfStorage">A storage reference if found else null</param>
        /// <returns><see cref="T:System.Boolean"> true if storage found, else false</returns>
        /// <example>
        /// <code>
        ///
        /// String FILENAME = "MultipleStorage2.cfs";
        /// CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false);
        ///
        /// bool b = cf.RootStorage.TryGetStorage("MyStorage",out CFStorage st);
        ///
        /// Assert.IsNotNull(st);
        /// Assert.IsTrue(b);
        ///
        /// cf.Close();
        /// </code>
        /// </example>
        public bool TryGetStorage(String storageName, out CFStorage cfStorage)
        {
            bool result = false;

            cfStorage = null;

            try
            {
                CheckDisposed();
                if (Children.TryLookup(DirectoryEntry.Mock(storageName, StgType.StgInvalid), out IRBNode outDe) && ((IDirectoryEntry)outDe).StgType == StgType.StgStorage)
                {
                    cfStorage = new CFStorage(this.CompoundFile, outDe as IDirectoryEntry);
                    result    = true;
                }
            }
            catch (CFDisposedException)
            {
                result = false;
            }

            return(result);
        }
Пример #4
0
        /// <summary>
        /// Remove an entry from the current storage and compound file.
        /// </summary>
        /// <param name="entryName">The name of the entry in the current storage to delete</param>
        /// <example>
        /// <code>
        /// cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false);
        /// cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist.
        /// cf.Commit(true);
        /// cf.Close();
        /// </code>
        /// </example>
        /// <exception cref="T:OpenMcdf.CFDisposedException">Raised if trying to delete item from a closed compound file</exception>
        /// <exception cref="T:OpenMcdf.CFItemNotFound">Raised if item to delete is not found</exception>
        /// <exception cref="T:OpenMcdf.CFException">Raised if trying to delete root storage</exception>
        public void Delete(String entryName)
        {
            CheckDisposed();

            // Find entry to delete
            IDirectoryEntry tmp = DirectoryEntry.Mock(entryName, StgType.StgInvalid);

            IRBNode foundObj = null;

            this.Children.TryLookup(tmp, out foundObj);

            if (foundObj == null)
            {
                throw new CFItemNotFound("Entry named [" + entryName + "] was not found");
            }

            //if (foundObj.GetType() != typeCheck)
            //    throw new CFException("Entry named [" + entryName + "] has not the correct type");

            if (((IDirectoryEntry)foundObj).StgType == StgType.StgRoot)
            {
                throw new CFException("Root storage cannot be removed");
            }


            IRBNode altDel = null;

            switch (((IDirectoryEntry)foundObj).StgType)
            {
            case StgType.StgStorage:

                CFStorage temp = new CFStorage(this.CompoundFile, ((IDirectoryEntry)foundObj));

                // This is a storage. we have to remove children items first
                foreach (IRBNode de in temp.Children)
                {
                    IDirectoryEntry ded = de as IDirectoryEntry;
                    temp.Delete(ded.Name);
                }



                // ...then we need to rethread the root of siblings tree...
                if (this.Children.Root != null)
                {
                    this.DirEntry.Child = (this.Children.Root as IDirectoryEntry).SID;
                }
                else
                {
                    this.DirEntry.Child = DirectoryEntry.NOSTREAM;
                }

                // ...and finally Remove storage item from children tree...
                this.Children.Delete(foundObj, out altDel);

                // ...and remove directory (storage) entry

                if (altDel != null)
                {
                    foundObj = altDel;
                }

                this.CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID);

                break;

            case StgType.StgStream:

                // Free directory associated data stream.
                CompoundFile.FreeAssociatedData((foundObj as IDirectoryEntry).SID);

                // Remove item from children tree
                this.Children.Delete(foundObj, out altDel);

                // Rethread the root of siblings tree...
                if (this.Children.Root != null)
                {
                    this.DirEntry.Child = (this.Children.Root as IDirectoryEntry).SID;
                }
                else
                {
                    this.DirEntry.Child = DirectoryEntry.NOSTREAM;
                }

                // Delete operation could possibly have cloned a directory, changing its SID.
                // Invalidate the ACTUALLY deleted directory.
                if (altDel != null)
                {
                    foundObj = altDel;
                }

                this.CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID);



                break;
            }

            //// Refresh recursively all SIDs (invariant for tree sorting)
            //VisitedEntryAction action = delegate(CFSItem target)
            //{
            //    if( ((IDirectoryEntry)target).SID>foundObj.SID )
            //    {
            //        ((IDirectoryEntry)target).SID--;
            //    }


            //    ((IDirectoryEntry)target).LeftSibling--;
            //};
        }
Пример #5
0
        /// <summary>
        /// Create new child storage directory inside the current storage.
        /// </summary>
        /// <param name="storageName">The new storage name</param>
        /// <returns>Reference to the new <see cref="T:OpenMcdf.CFStorage">storage</see></returns>
        /// <exception cref="T:OpenMcdf.CFDuplicatedItemException">Raised when adding an item with the same name of an existing one</exception>
        /// <exception cref="T:OpenMcdf.CFDisposedException">Raised when adding a storage to a closed compound file</exception>
        /// <exception cref="T:OpenMcdf.CFException">Raised when adding a storage with null or empty name</exception>
        /// <example>
        /// <code>
        /// 
        ///  String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs";
        ///
        ///  CompoundFile cf = new CompoundFile();
        ///
        ///  CFStorage st = cf.RootStorage.AddStorage("MyStorage");
        ///  CFStream sm = st.AddStream("MyStream");
        ///  byte[] b = Helpers.GetBuffer(220, 0x0A);
        ///  sm.SetData(b);
        ///
        ///  cf.Save(filename);
        ///  
        /// </code>
        /// </example>
        public ICFStorage AddStorage(String storageName)
        {
            CheckDisposed();

            if (String.IsNullOrEmpty(storageName))
                throw new CFException("Stream name cannot be null or empty");

            // Add new Storage directory entry
            CFStorage cfo = null;

            cfo = new CFStorage(this.CompoundFile);
            cfo.DirEntry.SetEntryName(storageName);

            try
            {
                // Add object to Siblings tree
                Children.Add(cfo);
            }
            catch (BSTDuplicatedException)
            {

                CompoundFile.ResetDirectoryEntry(cfo.DirEntry.SID);
                cfo = null;
                throw new CFDuplicatedItemException("An entry with name '" + storageName + "' is already present in storage '" + this.Name + "' ");
            }


            CompoundFile.RefreshIterative(Children.Root);
            this.DirEntry.Child = Children.Root.Value.DirEntry.SID;
            return cfo;
        }
Пример #6
0
        //public void DeleteStream(String name)
        //{
        //    Delete(name, typeof(CFStream));
        //}

        //public void DeleteStorage(String name)
        //{
        //    Delete(name, typeof(CFStorage));
        //}



        /// <summary>
        /// Remove an entry from the current storage and compound file.
        /// </summary>
        /// <param name="entryName">The name of the entry in the current storage to delete</param>
        /// <example>
        /// <code>
        /// cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false);
        /// cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist.
        /// cf.Commit(true);
        /// cf.Close();
        /// </code>
        /// </example>
        /// <exception cref="T:OpenMcdf.CFDisposedException">Raised if trying to delete item from a closed compound file</exception>
        /// <exception cref="T:OpenMcdf.CFItemNotFound">Raised if item to delete is not found</exception>
        /// <exception cref="T:OpenMcdf.CFException">Raised if trying to delete root storage</exception>
        public void Delete(String entryName)
        {
            CheckDisposed();

            // Find entry to delete
            CFMock tmp = new CFMock(entryName, StgType.StgInvalid);

            CFItem foundObj = null;

            this.Children.TryFind(tmp, out foundObj);

            if (foundObj == null)
            {
                throw new CFItemNotFound("Entry named [" + entryName + "] was not found");
            }

            //if (foundObj.GetType() != typeCheck)
            //    throw new CFException("Entry named [" + entryName + "] has not the correct type");

            if (foundObj.DirEntry.StgType == StgType.StgRoot)
            {
                throw new CFException("Root storage cannot be removed");
            }

            switch (foundObj.DirEntry.StgType)
            {
            case StgType.StgStorage:

                CFStorage temp = (CFStorage)foundObj;

                foreach (CFItem de in temp.Children)
                {
                    temp.Delete(de.Name);
                }

                // Remove item from children tree
                this.Children.Remove(foundObj);

                // Synchronize tree with directory entries
                this.CompoundFile.RefreshIterative(this.Children.Root);

                // Rethread the root of siblings tree...
                if (this.Children.Root != null)
                {
                    this.DirEntry.Child = this.Children.Root.Value.DirEntry.SID;
                }
                else
                {
                    this.DirEntry.Child = DirectoryEntry.NOSTREAM;
                }

                // ...and now remove directory (storage) entry
                this.CompoundFile.RemoveDirectoryEntry(foundObj.DirEntry.SID);

                break;

            case StgType.StgStream:

                // Remove item from children tree
                this.Children.Remove(foundObj);

                // Synchronize tree with directory entries
                this.CompoundFile.RefreshIterative(this.Children.Root);

                // Rethread the root of siblings tree...
                if (this.Children.Root != null)
                {
                    this.DirEntry.Child = this.Children.Root.Value.DirEntry.SID;
                }
                else
                {
                    this.DirEntry.Child = DirectoryEntry.NOSTREAM;
                }

                // Remove directory entry
                this.CompoundFile.RemoveDirectoryEntry(foundObj.DirEntry.SID);

                break;
            }

            //// Refresh recursively all SIDs (invariant for tree sorting)
            //VisitedEntryAction action = delegate(CFSItem target)
            //{
            //    if( ((IDirectoryEntry)target).SID>foundObj.SID )
            //    {
            //        ((IDirectoryEntry)target).SID--;
            //    }


            //    ((IDirectoryEntry)target).LeftSibling--;
            //};
        }
Пример #7
0
 /// <summary>
 /// Adds a collection of streams to the hash data.
 /// 
 /// GameItems and Collections are also hashed, but they are separate streams
 /// numbered from 1 to n. This assumes they are sequentially numbered and
 /// adds them all to the current data to hash.
 /// </summary>
 /// <param name="hashBuf">Current data to hash</param>
 /// <param name="streamName">Prefix of the streams to hash</param>
 /// <param name="stg">Storage of the streams</param>
 /// <param name="count">Number of streams to hash</param>
 /// <param name="offset">Offset where to start reading the BIFF data</param>
 /// <param name="stats">Stats collector</param>
 private void AddStreams(ICollection<byte[]> hashBuf, string streamName, CFStorage stg, int count, int offset, TableStats stats)
 {
     _logger.Info("Adding {0} {1}s...", count, streamName);
     for (var n = 0; n < count; n++) {
         AddBiffData(hashBuf, streamName + n, stg, offset, stats);
     }
 }
Пример #8
0
        /// <summary>
        /// Adds a complete stream to the hash data.
        /// </summary>
        /// <param name="hashBuf">Current data to hash</param>
        /// <param name="streamName">Stream to hash</param>
        /// <param name="stg">Storage of the stream</param>
        private void AddStream(ICollection<byte[]> hashBuf, string streamName, CFStorage stg)
        {
            try {
                var stream = stg.GetStream(streamName);
                if (stream != null) {
                    var data = stream.GetData();
                    hashBuf.Add(data);
                }
            } catch (CFItemNotFound) {
                _logger.Warn("Skipping non-existent Stream {0}.", streamName);

            } catch (Exception e) {
                _logger.Error(e, "Error reading data!");
                _crashManager.Report(e, "vpt");
            }
        }
Пример #9
0
        /// <summary>
        /// VPT files offer a way to add custom info through the table info 
        /// dialog. These are key/value pairs that are probably useful 
        /// somewhere.
        /// 
        /// The *keys* are stored in the `CustomInfoTags` stream of `GameStg`. The
        /// *values* are separate streams in the `TableInfo` storage.
        /// 
        /// Since those are also hashed, we need to obtain them and loop through 
        /// them.
        /// </summary>
        /// <param name="hashBuf">Current data to hash</param>
        /// <param name="streamName">Stream name where the keys are stored</param>
        /// <param name="stg">Storage where the keys are stored</param>
        /// <param name="info">Storage where the values are stored</param>
        /// <param name="stats">Stats collector</param>
        private void AddCustomStreams(ICollection<byte[]> hashBuf, string streamName, CFStorage stg, CFStorage info, TableStats stats)
        {
            // retrieve keys
            var keyBlocks = AddBiffData(hashBuf, streamName, stg, 0, stats);
            var keys = keyBlocks.Select(block => Encoding.Default.GetString(block.Data.Take(4).ToArray())).ToList();

            // read stream for every key
            _logger.Info("Reading all blocks in {0}: {1}", streamName, string.Join(", ", keys));
            keys.ForEach(key => { AddStream(hashBuf, key, info); });
        }
Пример #10
0
        /// <summary>
        /// Adds the data of all BIFF blocks to the hash data.
        /// </summary>
        /// <param name="hashBuf">Current data to hash</param>
        /// <param name="streamName">Name of the stream where BIFF data is stored</param>
        /// <param name="stg">Storage of the stream</param>
        /// <param name="offset">Byte offset to start reading BIFF data</param>
        /// <param name="stats">Stats collector</param>
        /// <returns>List of parsed BIFF blocks</returns>
        private List<BiffBlock> AddBiffData(ICollection<byte[]> hashBuf, string streamName, CFStorage stg, int offset, TableStats stats)
        {
            // init result
            var blocks = new List<BiffBlock>();

            // get stream from compound document
            var stream = stg.GetStream(streamName);
            if (stream == null) {
                _logger.Warn("No stream {0} in provided storage!", streamName);
                return blocks;
            }

            // get data from stream
            byte[] buf;
            try {
                buf = stream.GetData();
            } catch (CFItemNotFound) {
                _logger.Warn("No data in stream {0}.", streamName);
                return blocks;
            }

            // loop through BIFF blocks
            var i = offset;
            do {
                // Usually, we have:
                //
                //   [4 bytes] size of block              | `blockSize` - not hashed
                //   [blockSize bytes] data, which is:    | `block`     - hashed
                //       [4 bytes] tag name               | `tag`
                //       [blockSize - 4 bytes] real data  | `data`
                //
                // In case of a string, real data is again prefixed with 4 bytes
                // of string size, but we don't care because those are hashed too.
                //
                // What's NOT hashed is the original block size or the stream block
                // size, see below.
                //
                var blockSize = BitConverter.ToInt32(buf, i);
                var block = buf.Skip(i + 4).Take(blockSize).ToArray(); // contains tag and data
                var tag = Encoding.Default.GetString(block.Take(4).ToArray());

                // treat exceptions where we hash differently than usual
                if (tag == "FONT") {

                    // Not hashed, but need to find out how many bytes to skip. Best guess: tag
                    // is followed by 8 bytes of whatever, then 2 bytes size BE, followed by
                    // data.
                    blockSize = BitConverter.ToInt16(buf
                        .Skip(i + 17)
                        .Take(2)
                        .Reverse() // convert to big endian
                        .ToArray(), 0
                        );
                    // fonts are ignored, so just update the pointer and continue
                    i += 15;

                } else if (tag == "CODE") {

                    // In here, the data starts with 4 size bytes again. This is a special case,
                    // what's hashed now is only the tag and the data *after* the 4 size bytes.
                    // concretely, we have:
                    //
                    //   [4 bytes] size of block | `blockSize` above
                    //   [4 bytes] tag name      | `tag`
                    //   [4 bytes] size of code  | `blockSize` below
                    //   [n bytes] code          | `block` below
                    //
                    i += 8;
                    blockSize = BitConverter.ToInt32(buf, i);
                    _logger.Info("Code is {0} bytes long.", blockSize);

                    block = buf.Skip(i + 4).Take(blockSize).ToArray();
                    block = Encoding.Default.GetBytes(tag).Concat(block).ToArray();
                }

                // parse data block
                if (blockSize > 4) {
                    var data = block.Skip(4).ToArray();
                    blocks.Add(new BiffBlock(tag, data));
                    CollectStats(tag, data, stats);
                }
                i += blockSize + 4;

                // finally, add block to hash data
                hashBuf.Add(block);

            } while (i < buf.Length - 4);

            return blocks;
        }
Пример #11
0
 /// <summary>
 /// Reads the stored checksum from the table's `GameStg` storage.
 /// </summary>
 /// <param name="storage">`GameStg` of the table</param>
 /// <returns>Checksum</returns>
 private static byte[] ReadChecksum(CFStorage storage)
 {
     return storage.GetStream("MAC").GetData();
 }
Пример #12
0
        /// <summary>
        /// Remove an entry from the current storage and compound file.
        /// </summary>
        /// <param name="entryName">The name of the entry in the current storage to delete</param>
        /// <example>
        /// <code>
        /// cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false);
        /// cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist.
        /// cf.Commit(true);
        /// cf.Close();
        /// </code>
        /// </example>
        /// <exception cref="T:OpenMcdf.CFDisposedException">Raised if trying to delete item from a closed compound file</exception>
        /// <exception cref="T:OpenMcdf.CFItemNotFound">Raised if item to delete is not found</exception>
        /// <exception cref="T:OpenMcdf.CFException">Raised if trying to delete root storage</exception>
        public void Delete(string entryName)
        {
            CheckDisposed();

            // Find entry to delete
            var tmp = DirectoryEntry.Mock(entryName, StgType.StgInvalid);

            Children.TryLookup(tmp, out var foundObj);

            if (foundObj == null)
            {
                throw new CFItemNotFound("Entry named [" + entryName + "] was not found");
            }

            if (((IDirectoryEntry)foundObj).StgType == StgType.StgRoot)
            {
                throw new CFException("Root storage cannot be removed");
            }

            IRbNode altDel;

            // ReSharper disable once SwitchStatementMissingSomeCases
            switch (((IDirectoryEntry)foundObj).StgType)
            {
            case StgType.StgStorage:

                var temp = new CFStorage(CompoundFile, ((IDirectoryEntry)foundObj));

                // This is a storage. we have to remove children items first
                foreach (var de in temp.Children)
                {
                    if (de is IDirectoryEntry ded)
                    {
                        temp.Delete(ded.Name);
                    }
                }


                // ...then we need to re-thread the root of siblings tree...
                DirEntry.Child = (Children.Root as IDirectoryEntry)?.SID ?? DirectoryEntry.nostream;

                // ...and finally Remove storage item from children tree...
                Children.Delete(foundObj, out altDel);

                // ...and remove directory (storage) entry

                if (altDel != null)
                {
                    foundObj = altDel;
                }

                CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID);

                break;

            case StgType.StgStream:

                // Free directory associated data stream.
                CompoundFile.FreeAssociatedData((foundObj as IDirectoryEntry).SID);

                // Remove item from children tree
                Children.Delete(foundObj, out altDel);

                // Re-thread the root of siblings tree...
                DirEntry.Child = (Children.Root as IDirectoryEntry)?.SID ?? DirectoryEntry.nostream;

                // Delete operation could possibly have cloned a directory, changing its SID.
                // Invalidate the ACTUALLY deleted directory.
                if (altDel != null)
                {
                    foundObj = altDel;
                }

                CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID);

                break;
            }
        }
        private CFStorage GetStorage(CFStorage cfstorage, string storagename)
        {
            CFStorage storage = null;

            try
            {
                storage = cfstorage.GetStorage(storagename);
            }
            catch (Exception)
            {
            }

            return storage;
        }
        private CFStream GetStream(CFStorage cfstorage, string streamname)
        {
            CFStream stream = null;

            try
            {
                stream = cfstorage.GetStream(streamname);
            }
            catch (Exception)
            {
            }

            return stream;
        }